salvager: Do not set fileName on header fixup
[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 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
105 #if defined(AFS_VFSINCL_ENV)
106 #include <sys/vnode.h>
107 #ifdef  AFS_SUN5_ENV
108 #include <sys/fs/ufs_inode.h>
109 #else
110 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
111 #include <ufs/ufs/dinode.h>
112 #include <ufs/ffs/fs.h>
113 #else
114 #include <ufs/inode.h>
115 #endif
116 #endif
117 #else /* AFS_VFSINCL_ENV */
118 #ifdef  AFS_OSF_ENV
119 #include <ufs/inode.h>
120 #else /* AFS_OSF_ENV */
121 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
122 #include <sys/inode.h>
123 #endif
124 #endif
125 #endif /* AFS_VFSINCL_ENV */
126 #endif /* AFS_SGI_ENV */
127 #ifdef  AFS_AIX_ENV
128 #include <sys/vfs.h>
129 #include <sys/lockf.h>
130 #else
131 #ifdef  AFS_HPUX_ENV
132 #include <checklist.h>
133 #else
134 #if defined(AFS_SGI_ENV)
135 #include <mntent.h>
136 #else
137 #if     defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
138 #ifdef    AFS_SUN5_ENV
139 #include <sys/mnttab.h>
140 #include <sys/mntent.h>
141 #else
142 #include <mntent.h>
143 #endif
144 #else
145 #endif /* AFS_SGI_ENV */
146 #endif /* AFS_HPUX_ENV */
147 #endif
148 #endif
149 #ifndef AFS_NT40_ENV
150 #include <afs/osi_inode.h>
151 #endif
152 #include <afs/cmd.h>
153 #include <afs/dir.h>
154 #include <afs/afsutil.h>
155 #include <afs/fileutil.h>
156
157 #include "nfs.h"
158 #include "lwp.h"
159 #include "lock.h"
160 #include <afs/afssyscalls.h>
161 #include "ihandle.h"
162 #include "vnode.h"
163 #include "volume.h"
164 #include "partition.h"
165 #include "daemon_com.h"
166 #include "daemon_com_inline.h"
167 #include "fssync.h"
168 #include "fssync_inline.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[9];     /**< 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 #ifdef AFS_NT40_ENV
893         nt_SalvageVolumeGroup(salvinfo, &salvinfo->inodeSummary[i], j - i);
894 #else
895         DoSalvageVolumeGroup(salvinfo, &salvinfo->inodeSummary[i], j - i);
896 #endif /* AFS_NT40_ENV */
897
898     }
899
900     /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
901     for (; vsp < esp; vsp++) {
902         if (vsp->fileName)
903             DeleteExtraVolumeHeaderFile(salvinfo, vsp);
904     }
905
906     if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
907         RemoveTheForce(salvinfo->fileSysPath);
908
909     if (!Testing && singleVolumeNumber) {
910         int foundSVN = 0;
911 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
912         /* unlock vol headers so the fs can attach them when we AskOnline */
913         VLockFileReinit(&salvinfo->fileSysPartition->volLockFile);
914 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
915
916         /* Step through the volumeSummary list and set all volumes on-line.
917          * Most volumes were taken off-line in GetVolumeSummary.
918          * If a volume was deleted, don't tell the fileserver anything, since
919          * we already told the fileserver the volume was deleted back when we
920          * we destroyed the volume header.
921          * Also, make sure we bring the singleVolumeNumber back online first.
922          */
923
924         for (j = 0; j < salvinfo->nVolumes; j++) {
925             if (salvinfo->volumeSummaryp[j].header.id == singleVolumeNumber) {
926                 foundSVN = 1;
927                 if (!salvinfo->volumeSummaryp[j].deleted) {
928                     AskOnline(salvinfo, singleVolumeNumber);
929                 }
930             }
931         }
932
933         if (!foundSVN) {
934             /* If singleVolumeNumber is not in our volumeSummary, it means that
935              * at least one other volume in the VG is on the partition, but the
936              * RW volume is not. We've already AskOffline'd it by now, though,
937              * so make sure we don't still have the volume checked out. */
938             AskDelete(salvinfo, singleVolumeNumber);
939         }
940
941         for (j = 0; j < salvinfo->nVolumes; j++) {
942             if (salvinfo->volumeSummaryp[j].header.id != singleVolumeNumber) {
943                 if (!salvinfo->volumeSummaryp[j].deleted) {
944                     AskOnline(salvinfo, salvinfo->volumeSummaryp[j].header.id);
945                 }
946             }
947         }
948     } else {
949         if (!Showmode)
950             Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
951                 salvinfo->fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
952     }
953
954     OS_CLOSE(inodeFile);                /* SalvageVolumeGroup was the last which needed it. */
955 }
956
957 void
958 DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
959 {
960     char path[64];
961     sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, vsp->fileName);
962
963     if (!Showmode)
964         Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
965     if (!Testing) {
966         afs_int32 code;
967         code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, vsp->header.id, vsp->header.parent);
968         if (code) {
969             Log("Error %ld destroying volume disk header for volume %lu\n",
970                 afs_printable_int32_ld(code),
971                 afs_printable_uint32_lu(vsp->header.id));
972         }
973
974         /* make sure we actually delete the fileName file; ENOENT
975          * is fine, since VDestroyVolumeDiskHeader probably already
976          * unlinked it */
977         if (unlink(path) && errno != ENOENT) {
978             Log("Unable to unlink %s (errno = %d)\n", path, errno);
979         }
980         if (salvinfo->useFSYNC) {
981             AskDelete(salvinfo, vsp->header.id);
982         }
983         vsp->deleted = 1;
984     }
985     vsp->fileName = 0;
986 }
987
988 int
989 CompareInodes(const void *_p1, const void *_p2)
990 {
991     const struct ViceInodeInfo *p1 = _p1;
992     const struct ViceInodeInfo *p2 = _p2;
993     if (p1->u.vnode.vnodeNumber == INODESPECIAL
994         || p2->u.vnode.vnodeNumber == INODESPECIAL) {
995         VolumeId p1rwid, p2rwid;
996         p1rwid =
997             (p1->u.vnode.vnodeNumber ==
998              INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
999         p2rwid =
1000             (p2->u.vnode.vnodeNumber ==
1001              INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
1002         if (p1rwid < p2rwid)
1003             return -1;
1004         if (p1rwid > p2rwid)
1005             return 1;
1006         if (p1->u.vnode.vnodeNumber == INODESPECIAL
1007             && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1008             if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1009                 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
1010             if (p1->u.vnode.volumeId == p1rwid)
1011                 return -1;
1012             if (p2->u.vnode.volumeId == p2rwid)
1013                 return 1;
1014             return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
1015         }
1016         if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1017             return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
1018         return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
1019     }
1020     if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1021         return -1;
1022     if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1023         return 1;
1024     if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1025         return -1;
1026     if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1027         return 1;
1028     /* The following tests are reversed, so that the most desirable
1029      * of several similar inodes comes first */
1030     if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1031 #ifdef  AFS_3DISPARES
1032         if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
1033             p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1034             return 1;
1035 #endif
1036 #ifdef  AFS_SGI_EXMAG
1037         if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
1038             p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1039             return 1;
1040 #endif
1041         return -1;
1042     }
1043     if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1044 #ifdef  AFS_3DISPARES
1045         if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
1046             p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1047             return -1;
1048 #endif
1049 #ifdef  AFS_SGI_EXMAG
1050         if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
1051             p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1052             return 1;
1053 #endif
1054         return 1;
1055     }
1056     if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1057 #ifdef  AFS_3DISPARES
1058         if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
1059             p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1060             return 1;
1061 #endif
1062 #ifdef  AFS_SGI_EXMAG
1063         if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
1064             p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1065             return 1;
1066 #endif
1067         return -1;
1068     }
1069     if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1070 #ifdef  AFS_3DISPARES
1071         if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
1072             p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1073             return -1;
1074 #endif
1075 #ifdef  AFS_SGI_EXMAG
1076         if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
1077             p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1078             return 1;
1079 #endif
1080         return 1;
1081     }
1082     return 0;
1083 }
1084
1085 void
1086 CountVolumeInodes(struct ViceInodeInfo *ip, int maxInodes,
1087                   struct InodeSummary *summary)
1088 {
1089     VolumeId volume = ip->u.vnode.volumeId;
1090     VolumeId rwvolume = volume;
1091     int n, nSpecial;
1092     Unique maxunique;
1093     n = nSpecial = 0;
1094     maxunique = 0;
1095     while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1096         n++;
1097         if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1098             nSpecial++;
1099             rwvolume = ip->u.special.parentId;
1100             /* This isn't quite right, as there could (in error) be different
1101              * parent inodes in different special vnodes */
1102         } else {
1103             if (maxunique < ip->u.vnode.vnodeUniquifier)
1104                 maxunique = ip->u.vnode.vnodeUniquifier;
1105         }
1106         ip++;
1107     }
1108     summary->volumeId = volume;
1109     summary->RWvolumeId = rwvolume;
1110     summary->nInodes = n;
1111     summary->nSpecialInodes = nSpecial;
1112     summary->maxUniquifier = maxunique;
1113 }
1114
1115 int
1116 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1117 {
1118     if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1119         return (inodeinfo->u.special.parentId == singleVolumeNumber);
1120     return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1121 }
1122
1123 /* GetInodeSummary
1124  *
1125  * Collect list of inodes in file named by path. If a truly fatal error,
1126  * unlink the file and abort. For lessor errors, return -1. The file will
1127  * be unlinked by the caller.
1128  */
1129 int
1130 GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolumeNumber)
1131 {
1132     int forceSal, err;
1133     int code;
1134     struct ViceInodeInfo *ip, *ip_save;
1135     struct InodeSummary summary;
1136     char summaryFileName[50];
1137     FD_t summaryFile = INVALID_FD;
1138 #ifdef AFS_NT40_ENV
1139     char *dev = salvinfo->fileSysPath;
1140     char *wpath = salvinfo->fileSysPath;
1141 #else
1142     char *dev = salvinfo->fileSysDeviceName;
1143     char *wpath = salvinfo->filesysfulldev;
1144 #endif
1145     char *part = salvinfo->fileSysPath;
1146     char *tdir;
1147     int i;
1148     int retcode = 0;
1149     int deleted = 0;
1150     afs_sfsize_t st_size;
1151
1152     /* This file used to come from vfsck; cobble it up ourselves now... */
1153     if ((err =
1154          ListViceInodes(dev, salvinfo->fileSysPath, inodeFile,
1155                         singleVolumeNumber ? OnlyOneVolume : 0,
1156                         singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1157         if (err == -2) {
1158             Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1159             retcode = -1;
1160             goto error;
1161         }
1162         Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1163     }
1164     if (forceSal && !ForceSalvage) {
1165         Log("***Forced salvage of all volumes on this partition***\n");
1166         ForceSalvage = 1;
1167     }
1168     OS_SEEK(inodeFile, 0L, SEEK_SET);
1169     salvinfo->inodeFd = inodeFile;
1170     if (salvinfo->inodeFd == INVALID_FD ||
1171         (st_size = OS_SIZE(salvinfo->inodeFd)) == -1) {
1172         Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1173     }
1174     tdir = (tmpdir ? tmpdir : part);
1175 #ifdef AFS_NT40_ENV
1176     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
1177     (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp."));
1178 #else
1179     snprintf(summaryFileName, sizeof summaryFileName,
1180              "%s" OS_DIRSEP "salvage.temp.%d", tdir, getpid());
1181 #endif
1182     summaryFile = OS_OPEN(summaryFileName, O_RDWR|O_APPEND|O_CREAT, 0666);
1183     if (summaryFile == INVALID_FD) {
1184         Abort("Unable to create inode summary file\n");
1185     }
1186
1187 #ifdef AFS_NT40_ENV
1188     /* Using nt_unlink here since we're really using the delete on close
1189      * semantics of unlink. In most places in the salvager, we really do
1190      * mean to unlink the file at that point. Those places have been
1191      * modified to actually do that so that the NT crt can be used there.
1192      *
1193      * jaltman - As commented elsewhere, this cannot work because fopen()
1194      * does not open files with DELETE and FILE_SHARE_DELETE.
1195      */
1196     code = nt_unlink(summaryFileName);
1197 #else
1198     code = unlink(summaryFileName);
1199 #endif
1200     if (code < 0) {
1201         Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1202     }
1203
1204     if (!canfork || debug || Fork() == 0) {
1205         int nInodes = st_size / sizeof(struct ViceInodeInfo);
1206         if (nInodes == 0) {
1207             OS_CLOSE(summaryFile);
1208             if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
1209                 RemoveTheForce(salvinfo->fileSysPath);
1210             else {
1211                 struct VolumeSummary *vsp;
1212                 int i;
1213                 int foundSVN = 0;
1214
1215                 GetVolumeSummary(salvinfo, singleVolumeNumber);
1216
1217                 for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; i++) {
1218                     if (vsp->fileName) {
1219                         if (vsp->header.id == singleVolumeNumber) {
1220                             foundSVN = 1;
1221                         }
1222                         DeleteExtraVolumeHeaderFile(salvinfo, vsp);
1223                     }
1224                 }
1225
1226                 if (!foundSVN) {
1227                     if (Testing) {
1228                         MaybeAskOnline(salvinfo, singleVolumeNumber);
1229                     } else {
1230                         /* make sure we get rid of stray .vol headers, even if
1231                          * they're not in our volume summary (might happen if
1232                          * e.g. something else created them and they're not in the
1233                          * fileserver VGC) */
1234                         VDestroyVolumeDiskHeader(salvinfo->fileSysPartition,
1235                                                  singleVolumeNumber, 0 /*parent*/);
1236                         AskDelete(salvinfo, singleVolumeNumber);
1237                     }
1238                 }
1239             }
1240             Log("%s vice inodes on %s; not salvaged\n",
1241                 singleVolumeNumber ? "No applicable" : "No", dev);
1242             retcode = -1;
1243             deleted = 1;
1244             goto error;
1245         }
1246         ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1247         if (ip == NULL) {
1248             OS_CLOSE(summaryFile);
1249             Abort
1250                 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1251                  dev);
1252         }
1253         if (OS_READ(salvinfo->inodeFd, ip, st_size) != st_size) {
1254             OS_CLOSE(summaryFile);
1255             Abort("Unable to read inode table; %s not salvaged\n", dev);
1256         }
1257         qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1258         if (OS_SEEK(salvinfo->inodeFd, 0, SEEK_SET) == -1
1259             || OS_WRITE(salvinfo->inodeFd, ip, st_size) != st_size) {
1260             OS_CLOSE(summaryFile);
1261             Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1262         }
1263         summary.index = 0;
1264         ip_save = ip;
1265         while (nInodes) {
1266             CountVolumeInodes(ip, nInodes, &summary);
1267             if (OS_WRITE(summaryFile, &summary, sizeof(summary)) != sizeof(summary)) {
1268                 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1269                 OS_CLOSE(summaryFile);
1270                 retcode = -1;
1271                 goto error;
1272             }
1273             summary.index += (summary.nInodes);
1274             nInodes -= summary.nInodes;
1275             ip += summary.nInodes;
1276         }
1277         free(ip_save);
1278         ip = ip_save = NULL;
1279         /* Following fflush is not fclose, because if it was debug mode would not work */
1280         if (OS_SYNC(summaryFile) == -1) {
1281             Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1282             OS_CLOSE(summaryFile);
1283             retcode = -1;
1284             goto error;
1285         }
1286         if (canfork && !debug) {
1287             ShowLog = 0;
1288             Exit(0);
1289         }
1290     } else {
1291         if (Wait("Inode summary") == -1) {
1292             OS_CLOSE(summaryFile);
1293             Exit(1);            /* salvage of this partition aborted */
1294         }
1295     }
1296
1297     st_size = OS_SIZE(summaryFile);
1298     osi_Assert(st_size >= 0);
1299     if (st_size != 0) {
1300         int ret;
1301         salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_size);
1302         osi_Assert(salvinfo->inodeSummary != NULL);
1303         /* For GNU we need to do lseek to get the file pointer moved. */
1304         osi_Assert(OS_SEEK(summaryFile, 0, SEEK_SET) == 0);
1305         ret = OS_READ(summaryFile, salvinfo->inodeSummary, st_size);
1306         osi_Assert(ret == st_size);
1307     }
1308     salvinfo->nVolumesInInodeFile = st_size / sizeof(struct InodeSummary);
1309     for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
1310         salvinfo->inodeSummary[i].volSummary = NULL;
1311     }
1312     Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)st_size);
1313     OS_CLOSE(summaryFile);
1314
1315  error:
1316     if (retcode && singleVolumeNumber && !deleted) {
1317         AskError(salvinfo, singleVolumeNumber);
1318     }
1319
1320     return retcode;
1321 }
1322
1323 /* Comparison routine for volume sort.
1324    This is setup so that a read-write volume comes immediately before
1325    any read-only clones of that volume */
1326 int
1327 CompareVolumes(const void *_p1, const void *_p2)
1328 {
1329     const struct VolumeSummary *p1 = _p1;
1330     const struct VolumeSummary *p2 = _p2;
1331     if (p1->header.parent != p2->header.parent)
1332         return p1->header.parent < p2->header.parent ? -1 : 1;
1333     if (p1->header.id == p1->header.parent)     /* p1 is rw volume */
1334         return -1;
1335     if (p2->header.id == p2->header.parent)     /* p2 is rw volume */
1336         return 1;
1337     return p1->header.id < p2->header.id ? -1 : 1;      /* Both read-only */
1338 }
1339
1340 /**
1341  * Gleans volumeSummary information by asking the fileserver
1342  *
1343  * @param[in] singleVolumeNumber  the volume we're salvaging. 0 if we're
1344  *                                salvaging a whole partition
1345  *
1346  * @return whether we obtained the volume summary information or not
1347  *  @retval 0  success; we obtained the volume summary information
1348  *  @retval -1 we raced with a fileserver restart; volume locks and checkout
1349  *             must be retried
1350  *  @retval 1  we did not get the volume summary information; either the
1351  *             fileserver responded with an error, or we are not supposed to
1352  *             ask the fileserver for the information (e.g. we are salvaging
1353  *             the entire partition or we are not the salvageserver)
1354  *
1355  * @note for non-DAFS, always returns 1
1356  */
1357 static int
1358 AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1359 {
1360     afs_int32 code = 1;
1361 #if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
1362     if (programType == salvageServer) {
1363         if (singleVolumeNumber) {
1364             FSSYNC_VGQry_response_t q_res;
1365             SYNC_response res;
1366             struct VolumeSummary *vsp;
1367             int i;
1368             struct VolumeDiskHeader diskHdr;
1369
1370             memset(&res, 0, sizeof(res));
1371
1372             code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1373
1374             /*
1375              * We must wait for the partition to finish scanning before
1376              * can continue, since we will not know if we got the entire
1377              * VG membership unless the partition is fully scanned.
1378              * We could, in theory, just scan the partition ourselves if
1379              * the VG cache is not ready, but we would be doing the exact
1380              * same scan the fileserver is doing; it will almost always
1381              * be faster to wait for the fileserver. The only exceptions
1382              * are if the partition does not take very long to scan, and
1383              * in that case it's fast either way, so who cares?
1384              */
1385             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1386                 Log("waiting for fileserver to finish scanning partition %s...\n",
1387                     salvinfo->fileSysPartition->name);
1388
1389                 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1390                     /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1391                      * just so small partitions don't need to wait over 10
1392                      * seconds every time, and large partitions are generally
1393                      * polled only once every ten seconds. */
1394                     sleep((i > 10) ? (i = 10) : i);
1395
1396                     code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1397                 }
1398             }
1399
1400             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1401                 /* This can happen if there's no header for the volume
1402                  * we're salvaging, or no headers exist for the VG (if
1403                  * we're salvaging an RW). Act as if we got a response
1404                  * with no VG members. The headers may be created during
1405                  * salvaging, if there are inodes in this VG. */
1406                 code = 0;
1407                 memset(&q_res, 0, sizeof(q_res));
1408                 q_res.rw = singleVolumeNumber;
1409             }
1410
1411             if (code) {
1412                 Log("fileserver refused VGCQuery request for volume %lu on "
1413                     "partition %s, code %ld reason %ld\n",
1414                     afs_printable_uint32_lu(singleVolumeNumber),
1415                     salvinfo->fileSysPartition->name,
1416                     afs_printable_int32_ld(code),
1417                     afs_printable_int32_ld(res.hdr.reason));
1418                 goto done;
1419             }
1420
1421             if (q_res.rw != singleVolumeNumber) {
1422                 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1423                     afs_printable_uint32_lu(singleVolumeNumber),
1424                     afs_printable_uint32_lu(q_res.rw));
1425 #ifdef SALVSYNC_BUILD_CLIENT
1426                 if (SALVSYNC_LinkVolume(q_res.rw,
1427                                        singleVolumeNumber,
1428                                        salvinfo->fileSysPartition->name,
1429                                        NULL) != SYNC_OK) {
1430                     Log("schedule request failed\n");
1431                 }
1432 #endif /* SALVSYNC_BUILD_CLIENT */
1433                 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1434             }
1435
1436             salvinfo->volumeSummaryp = calloc(VOL_VG_MAX_VOLS, sizeof(struct VolumeSummary));
1437             osi_Assert(salvinfo->volumeSummaryp != NULL);
1438
1439             salvinfo->nVolumes = 0;
1440             vsp = salvinfo->volumeSummaryp;
1441
1442             for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1443                 char name[VMAXPATHLEN];
1444
1445                 if (!q_res.children[i]) {
1446                     continue;
1447                 }
1448
1449                 /* AskOffline for singleVolumeNumber was called much earlier */
1450                 if (q_res.children[i] != singleVolumeNumber) {
1451                     AskOffline(salvinfo, q_res.children[i]);
1452                     if (LockVolume(salvinfo, q_res.children[i])) {
1453                         /* need to retry */
1454                         return -1;
1455                     }
1456                 }
1457
1458                 code = VReadVolumeDiskHeader(q_res.children[i], salvinfo->fileSysPartition, &diskHdr);
1459                 if (code) {
1460                     Log("Cannot read header for %lu; trying to salvage group anyway\n",
1461                         afs_printable_uint32_lu(q_res.children[i]));
1462                     code = 0;
1463                     continue;
1464                 }
1465
1466                 DiskToVolumeHeader(&vsp->header, &diskHdr);
1467                 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1468                 vsp->fileName = ToString(name);
1469                 salvinfo->nVolumes++;
1470                 vsp++;
1471             }
1472
1473             qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1474                   CompareVolumes);
1475         }
1476       done:
1477         if (code) {
1478             Log("Cannot get volume summary from fileserver; falling back to scanning "
1479                 "entire partition\n");
1480         }
1481     }
1482 #endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
1483     return code;
1484 }
1485
1486 /**
1487  * count how many volume headers are found by VWalkVolumeHeaders.
1488  *
1489  * @param[in] dp   the disk partition (unused)
1490  * @param[in] name full path to the .vol header (unused)
1491  * @param[in] hdr  the header data (unused)
1492  * @param[in] last whether this is the last try or not (unused)
1493  * @param[in] rock actually an afs_int32*; the running count of how many
1494  *                 volumes we have found
1495  *
1496  * @retval 0 always
1497  */
1498 static int
1499 CountHeader(struct DiskPartition64 *dp, const char *name,
1500             struct VolumeDiskHeader *hdr, int last, void *rock)
1501 {
1502     afs_int32 *nvols = (afs_int32 *)rock;
1503     (*nvols)++;
1504     return 0;
1505 }
1506
1507 /**
1508  * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1509  * data.
1510  */
1511 struct SalvageScanParams {
1512     VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1513                                   * vol id of the VG we're salvaging */
1514     struct VolumeSummary *vsp;   /**< ptr to the current volume summary object
1515                                   * we're filling in */
1516     afs_int32 nVolumes;          /**< # of vols we've encountered */
1517     afs_int32 totalVolumes;      /**< max # of vols we should encounter (the
1518                                   * # of vols we've alloc'd memory for) */
1519     int retry;  /**< do we need to retry vol lock/checkout? */
1520     struct SalvInfo *salvinfo; /**< salvage job info */
1521 };
1522
1523 /**
1524  * records volume summary info found from VWalkVolumeHeaders.
1525  *
1526  * Found volumes are also taken offline if they are in the specific volume
1527  * group we are looking for.
1528  *
1529  * @param[in] dp   the disk partition
1530  * @param[in] name full path to the .vol header
1531  * @param[in] hdr  the header data
1532  * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1533  * @param[in] rock actually a struct SalvageScanParams*, containing the
1534  *                 information needed to record the volume summary data
1535  *
1536  * @return operation status
1537  *  @retval 0  success
1538  *  @retval -1 volume locking raced with fileserver restart; checking out
1539  *             and locking volumes needs to be retried
1540  *  @retval 1  volume header is mis-named and should be deleted
1541  */
1542 static int
1543 RecordHeader(struct DiskPartition64 *dp, const char *name,
1544              struct VolumeDiskHeader *hdr, int last, void *rock)
1545 {
1546     char nameShouldBe[64];
1547     struct SalvageScanParams *params;
1548     struct VolumeSummary summary;
1549     VolumeId singleVolumeNumber;
1550     struct SalvInfo *salvinfo;
1551
1552     params = (struct SalvageScanParams *)rock;
1553
1554     memset(&summary, 0, sizeof(summary));
1555
1556     singleVolumeNumber = params->singleVolumeNumber;
1557     salvinfo = params->salvinfo;
1558
1559     DiskToVolumeHeader(&summary.header, hdr);
1560
1561     if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1562         && summary.header.parent != singleVolumeNumber) {
1563
1564         if (programType == salvageServer) {
1565 #ifdef SALVSYNC_BUILD_CLIENT
1566             Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1567                 summary.header.id, summary.header.parent);
1568             if (SALVSYNC_LinkVolume(summary.header.parent,
1569                                     summary.header.id,
1570                                     dp->name,
1571                                     NULL) != SYNC_OK) {
1572                 Log("schedule request failed\n");
1573             }
1574 #endif
1575             Exit(SALSRV_EXIT_VOLGROUP_LINK);
1576
1577         } else {
1578             Log("%u is a read-only volume; not salvaged\n",
1579                 singleVolumeNumber);
1580             Exit(1);
1581         }
1582     }
1583
1584     if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1585         || summary.header.parent == singleVolumeNumber) {
1586
1587         /* check if the header file is incorrectly named */
1588         int badname = 0;
1589         const char *base = strrchr(name, OS_DIRSEPC);
1590         if (base) {
1591             base++;
1592         } else {
1593             base = name;
1594         }
1595
1596         snprintf(nameShouldBe, sizeof nameShouldBe,
1597                  VFORMAT, afs_printable_uint32_lu(summary.header.id));
1598
1599
1600         if (strcmp(nameShouldBe, base)) {
1601             /* .vol file has wrong name; retry/delete */
1602             badname = 1;
1603         }
1604
1605         if (!badname || last) {
1606             /* only offline the volume if the header is good, or if this is
1607              * the last try looking at it; avoid AskOffline'ing the same vol
1608              * multiple times */
1609
1610             if (singleVolumeNumber
1611                 && summary.header.id != singleVolumeNumber) {
1612                 /* don't offline singleVolumeNumber; we already did that
1613                  * earlier */
1614
1615                 AskOffline(salvinfo, summary.header.id);
1616
1617 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
1618                 if (!badname) {
1619                     /* don't lock the volume if the header is bad, since we're
1620                      * about to delete it anyway. */
1621                     if (LockVolume(salvinfo, summary.header.id)) {
1622                         params->retry = 1;
1623                         return -1;
1624                     }
1625                 }
1626 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
1627             }
1628         }
1629         if (badname) {
1630             if (last && !Showmode) {
1631                 Log("Volume header file %s is incorrectly named (should be %s "
1632                     "not %s); %sdeleted (it will be recreated later, if "
1633                     "necessary)\n", name, nameShouldBe, base,
1634                     (Testing ? "it would have been " : ""));
1635             }
1636             return 1;
1637         }
1638
1639         summary.fileName = ToString(base);
1640         params->nVolumes++;
1641
1642         if (params->nVolumes > params->totalVolumes) {
1643             /* We found more volumes than we found on the first partition walk;
1644              * apparently something created a volume while we were
1645              * partition-salvaging, or we found more than 20 vols when salvaging a
1646              * particular volume. Abort if we detect this, since other programs
1647              * supposed to not touch the partition while it is partition-salvaging,
1648              * and we shouldn't find more than 20 vols in a VG.
1649              */
1650             Abort("Found %ld vol headers, but should have found at most %ld! "
1651                   "Make sure the volserver/fileserver are not running at the "
1652                   "same time as a partition salvage\n",
1653                   afs_printable_int32_ld(params->nVolumes),
1654                   afs_printable_int32_ld(params->totalVolumes));
1655         }
1656
1657         memcpy(params->vsp, &summary, sizeof(summary));
1658         params->vsp++;
1659     }
1660
1661     return 0;
1662 }
1663
1664 /**
1665  * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1666  *
1667  * If the header could not be read in at all, the header is always unlinked.
1668  * If instead RecordHeader said the header was bad (that is, the header file
1669  * is mis-named), we only unlink if we are doing a partition salvage, as
1670  * opposed to salvaging a specific volume group.
1671  *
1672  * @param[in] dp   the disk partition
1673  * @param[in] name full path to the .vol header
1674  * @param[in] hdr  header data, or NULL if the header could not be read
1675  * @param[in] rock actually a struct SalvageScanParams*, with some information
1676  *                 about the scan
1677  */
1678 static void
1679 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1680              struct VolumeDiskHeader *hdr, void *rock)
1681 {
1682     struct SalvageScanParams *params;
1683     int dounlink = 0;
1684
1685     params = (struct SalvageScanParams *)rock;
1686
1687     if (!hdr) {
1688         /* no header; header is too bogus to read in at all */
1689         if (!Showmode) {
1690             Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1691         }
1692         if (!Testing) {
1693             dounlink = 1;
1694         }
1695
1696     } else if (!params->singleVolumeNumber) {
1697         /* We were able to read in a header, but RecordHeader said something
1698          * was wrong with it. We only unlink those if we are doing a partition
1699          * salvage. */
1700         if (!Testing) {
1701             dounlink = 1;
1702         }
1703     }
1704
1705     if (dounlink && unlink(name)) {
1706         Log("Error %d while trying to unlink %s\n", errno, name);
1707     }
1708 }
1709
1710 /**
1711  * Populates salvinfo->volumeSummaryp with volume summary information, either by asking
1712  * the fileserver for VG information, or by scanning the /vicepX partition.
1713  *
1714  * @param[in] singleVolumeNumber  the volume ID of the single volume group we
1715  *                                are salvaging, or 0 if this is a partition
1716  *                                salvage
1717  *
1718  * @return operation status
1719  *  @retval 0  success
1720  *  @retval -1 we raced with a fileserver restart; checking out and locking
1721  *             volumes must be retried
1722  */
1723 int
1724 GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1725 {
1726     afs_int32 nvols = 0;
1727     struct SalvageScanParams params;
1728     int code;
1729
1730     code = AskVolumeSummary(salvinfo, singleVolumeNumber);
1731     if (code == 0) {
1732         /* we successfully got the vol information from the fileserver; no
1733          * need to scan the partition */
1734         return 0;
1735     }
1736     if (code < 0) {
1737         /* we need to retry volume checkout */
1738         return code;
1739     }
1740
1741     if (!singleVolumeNumber) {
1742         /* Count how many volumes we have in /vicepX */
1743         code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, CountHeader,
1744                                   NULL, &nvols);
1745         if (code < 0) {
1746             Abort("Can't read directory %s; not salvaged\n", salvinfo->fileSysPath);
1747         }
1748         if (!nvols)
1749             nvols = 1;
1750     } else {
1751         nvols = VOL_VG_MAX_VOLS;
1752     }
1753
1754     salvinfo->volumeSummaryp = calloc(nvols, sizeof(struct VolumeSummary));
1755     osi_Assert(salvinfo->volumeSummaryp != NULL);
1756
1757     params.singleVolumeNumber = singleVolumeNumber;
1758     params.vsp = salvinfo->volumeSummaryp;
1759     params.nVolumes = 0;
1760     params.totalVolumes = nvols;
1761     params.retry = 0;
1762     params.salvinfo = salvinfo;
1763
1764     /* walk the partition directory of volume headers and record the info
1765      * about them; unlinking invalid headers */
1766     code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, RecordHeader,
1767                               UnlinkHeader, &params);
1768     if (params.retry) {
1769         /* we apparently need to retry checking-out/locking volumes */
1770         return -1;
1771     }
1772     if (code < 0) {
1773         Abort("Failed to get volume header summary\n");
1774     }
1775     salvinfo->nVolumes = params.nVolumes;
1776
1777     qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1778           CompareVolumes);
1779
1780     return 0;
1781 }
1782
1783 /* Find the link table. This should be associated with the RW volume or, if
1784  * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1785  */
1786 Inode
1787 FindLinkHandle(struct InodeSummary *isp, int nVols,
1788                struct ViceInodeInfo *allInodes)
1789 {
1790     int i, j;
1791     struct ViceInodeInfo *ip;
1792
1793     for (i = 0; i < nVols; i++) {
1794         ip = allInodes + isp[i].index;
1795         for (j = 0; j < isp[i].nSpecialInodes; j++) {
1796             if (ip[j].u.special.type == VI_LINKTABLE)
1797                 return ip[j].inodeNumber;
1798         }
1799     }
1800     return (Inode) - 1;
1801 }
1802
1803 int
1804 CreateLinkTable(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
1805 {
1806     struct versionStamp version;
1807     FdHandle_t *fdP;
1808
1809     if (!VALID_INO(ino))
1810         ino =
1811             IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->RWvolumeId,
1812                       INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1813     if (!VALID_INO(ino))
1814         Abort
1815             ("Unable to allocate link table inode for volume %u (error = %d)\n",
1816              isp->RWvolumeId, errno);
1817     IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
1818     fdP = IH_OPEN(salvinfo->VGLinkH);
1819     if (fdP == NULL)
1820         Abort("Can't open link table for volume %u (error = %d)\n",
1821               isp->RWvolumeId, errno);
1822
1823     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1824         Abort("Can't truncate link table for volume %u (error = %d)\n",
1825               isp->RWvolumeId, errno);
1826
1827     version.magic = LINKTABLEMAGIC;
1828     version.version = LINKTABLEVERSION;
1829
1830     if (FDH_PWRITE(fdP, (char *)&version, sizeof(version), 0)
1831         != sizeof(version))
1832         Abort("Can't truncate link table for volume %u (error = %d)\n",
1833               isp->RWvolumeId, errno);
1834
1835     FDH_REALLYCLOSE(fdP);
1836
1837     /* If the volume summary exits (i.e.,  the V*.vol header file exists),
1838      * then set this inode there as well.
1839      */
1840     if (isp->volSummary)
1841         isp->volSummary->header.linkTable = ino;
1842
1843     return 0;
1844 }
1845
1846 #ifdef AFS_NT40_ENV
1847 void *
1848 nt_SVG(void *arg)
1849 {
1850     SVGParms_t *parms = (SVGParms_t *) arg;
1851     DoSalvageVolumeGroup(parms->svgp_salvinfo, parms->svgp_inodeSummaryp, parms->svgp_count);
1852     return NULL;
1853 }
1854
1855 void
1856 nt_SalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
1857 {
1858     pthread_t tid;
1859     pthread_attr_t tattr;
1860     int code;
1861     SVGParms_t parms;
1862
1863     /* Initialize per volume global variables, even if later code does so */
1864     salvinfo->VolumeChanged = 0;
1865     salvinfo->VGLinkH = NULL;
1866     salvinfo->VGLinkH_cnt = 0;
1867     memset(&salvinfo->VolInfo, 0, sizeof(salvinfo->VolInfo));
1868
1869     parms.svgp_inodeSummaryp = isp;
1870     parms.svgp_count = nVols;
1871     parms.svgp_salvinfo = salvinfo;
1872     code = pthread_attr_init(&tattr);
1873     if (code) {
1874         Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1875             isp->RWvolumeId);
1876         return;
1877     }
1878     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1879     if (code) {
1880         Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1881         return;
1882     }
1883     code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1884     if (code) {
1885         Log("Failed to create thread to salvage volume group %u\n",
1886             isp->RWvolumeId);
1887         return;
1888     }
1889     (void)pthread_join(tid, NULL);
1890 }
1891 #endif /* AFS_NT40_ENV */
1892
1893 void
1894 DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
1895 {
1896     struct ViceInodeInfo *inodes, *allInodes, *ip;
1897     int i, totalInodes, size, salvageTo;
1898     int haveRWvolume;
1899     int check;
1900     Inode ino;
1901     int dec_VGLinkH = 0;
1902     int VGLinkH_p1 =0;
1903     FdHandle_t *fdP = NULL;
1904
1905     salvinfo->VGLinkH_cnt = 0;
1906     haveRWvolume = (isp->volumeId == isp->RWvolumeId
1907                     && isp->nSpecialInodes > 0);
1908     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1909         if (!ForceSalvage && QuickCheck(salvinfo, isp, nVols))
1910             return;
1911     }
1912     if (ShowMounts && !haveRWvolume)
1913         return;
1914     if (canfork && !debug && Fork() != 0) {
1915         (void)Wait("Salvage volume group");
1916         return;
1917     }
1918     for (i = 0, totalInodes = 0; i < nVols; i++)
1919         totalInodes += isp[i].nInodes;
1920     size = totalInodes * sizeof(struct ViceInodeInfo);
1921     inodes = (struct ViceInodeInfo *)malloc(size);
1922     allInodes = inodes - isp->index;    /* this would the base of all the inodes
1923                                          * for the partition, if all the inodes
1924                                          * had been read into memory */
1925     osi_Assert(OS_SEEK
1926            (salvinfo->inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1927             SEEK_SET) != -1);
1928     osi_Assert(OS_READ(salvinfo->inodeFd, inodes, size) == size);
1929
1930     /* Don't try to salvage a read write volume if there isn't one on this
1931      * partition */
1932     salvageTo = haveRWvolume ? 0 : 1;
1933
1934 #ifdef AFS_NAMEI_ENV
1935     ino = FindLinkHandle(isp, nVols, allInodes);
1936     if (VALID_INO(ino)) {
1937         IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
1938         fdP = IH_OPEN(salvinfo->VGLinkH);
1939     }
1940     if (VALID_INO(ino) && fdP != NULL) {
1941         struct versionStamp header;
1942         afs_sfsize_t nBytes;
1943
1944         nBytes = FDH_PREAD(fdP, (char *)&header, sizeof(struct versionStamp), 0);
1945         if (nBytes != sizeof(struct versionStamp)
1946             || header.magic != LINKTABLEMAGIC) {
1947             Log("Bad linktable header for volume %u.\n", isp->RWvolumeId);
1948             FDH_REALLYCLOSE(fdP);
1949             fdP = NULL;
1950         }
1951     }
1952     if (!VALID_INO(ino) || fdP == NULL) {
1953         Log("%s link table for volume %u.\n",
1954             Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1955         if (Testing) {
1956             IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
1957         } else {
1958             int i, j;
1959             struct ViceInodeInfo *ip;
1960             CreateLinkTable(salvinfo, isp, ino);
1961             fdP = IH_OPEN(salvinfo->VGLinkH);
1962             /* Sync fake 1 link counts to the link table, now that it exists */
1963             if (fdP) {
1964                 for (i = 0; i < nVols; i++) {
1965                         ip = allInodes + isp[i].index;
1966                          for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1967                                  namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1968                     }
1969                 }
1970             }
1971         }
1972     }
1973     if (fdP)
1974         FDH_REALLYCLOSE(fdP);
1975 #else
1976     IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
1977 #endif
1978
1979     /* Salvage in reverse order--read/write volume last; this way any
1980      * Inodes not referenced by the time we salvage the read/write volume
1981      * can be picked up by the read/write volume */
1982     /* ACTUALLY, that's not done right now--the inodes just vanish */
1983     for (i = nVols - 1; i >= salvageTo; i--) {
1984         int rw = (i == 0);
1985         struct InodeSummary *lisp = &isp[i];
1986 #ifdef AFS_NAMEI_ENV
1987         /* If only the RO is present on this partition, the link table
1988          * shows up as a RW volume special file. Need to make sure the
1989          * salvager doesn't try to salvage the non-existent RW.
1990          */
1991         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1992             /* If this only special inode is the link table, continue */
1993             if (inodes->u.special.type == VI_LINKTABLE) {
1994                 haveRWvolume = 0;
1995                 continue;
1996             }
1997         }
1998 #endif
1999         if (!Showmode)
2000             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
2001                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
2002         /* Check inodes twice.  The second time do things seriously.  This
2003          * way the whole RO volume can be deleted, below, if anything goes wrong */
2004         for (check = 1; check >= 0; check--) {
2005             int deleteMe;
2006             if (SalvageVolumeHeaderFile(salvinfo, lisp, allInodes, rw, check, &deleteMe)
2007                 == -1) {
2008                 MaybeZapVolume(salvinfo, lisp, "Volume header", deleteMe, check);
2009                 if (rw && deleteMe) {
2010                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
2011                                          * volume won't be called */
2012                     break;
2013                 }
2014                 if (!rw)
2015                     break;
2016             }
2017             if (rw && check == 1)
2018                 continue;
2019             if (SalvageVnodes(salvinfo, isp, lisp, allInodes, check) == -1) {
2020                 MaybeZapVolume(salvinfo, lisp, "Vnode index", 0, check);
2021                 break;
2022             }
2023         }
2024     }
2025
2026     /* Fix actual inode counts */
2027     if (!Showmode) {
2028         afs_ino_str_t stmp;
2029         Log("totalInodes %d\n",totalInodes);
2030         for (ip = inodes; totalInodes; ip++, totalInodes--) {
2031             static int TraceBadLinkCounts = 0;
2032 #ifdef AFS_NAMEI_ENV
2033             if (salvinfo->VGLinkH->ih_ino == ip->inodeNumber) {
2034                 dec_VGLinkH = ip->linkCount - salvinfo->VGLinkH_cnt;
2035                 VGLinkH_p1 = ip->u.param[0];
2036                 continue;       /* Deal with this last. */
2037             }
2038 #endif
2039             if (ip->linkCount != 0 && TraceBadLinkCounts) {
2040                 TraceBadLinkCounts--;   /* Limit reports, per volume */
2041                 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]);
2042             }
2043             while (ip->linkCount > 0) {
2044                 /* below used to assert, not break */
2045                 if (!Testing) {
2046                     if (IH_DEC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2047                         Log("idec failed. inode %s errno %d\n",
2048                             PrintInode(stmp, ip->inodeNumber), errno);
2049                         break;
2050                     }
2051                 }
2052                 ip->linkCount--;
2053             }
2054             while (ip->linkCount < 0) {
2055                 /* these used to be asserts */
2056                 if (!Testing) {
2057                     if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2058                         Log("iinc failed. inode %s errno %d\n",
2059                             PrintInode(stmp, ip->inodeNumber), errno);
2060                         break;
2061                     }
2062                 }
2063                 ip->linkCount++;
2064             }
2065         }
2066 #ifdef AFS_NAMEI_ENV
2067         while (dec_VGLinkH > 0) {
2068             if (IH_DEC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2069                 Log("idec failed on link table, errno = %d\n", errno);
2070             }
2071             dec_VGLinkH--;
2072         }
2073         while (dec_VGLinkH < 0) {
2074             if (IH_INC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2075                 Log("iinc failed on link table, errno = %d\n", errno);
2076             }
2077             dec_VGLinkH++;
2078         }
2079 #endif
2080     }
2081     free(inodes);
2082     /* Directory consistency checks on the rw volume */
2083     if (haveRWvolume)
2084         SalvageVolume(salvinfo, isp, salvinfo->VGLinkH);
2085     IH_RELEASE(salvinfo->VGLinkH);
2086
2087     if (canfork && !debug) {
2088         ShowLog = 0;
2089         Exit(0);
2090     }
2091 }
2092
2093 int
2094 QuickCheck(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
2095 {
2096     /* Check headers BEFORE forking */
2097     int i;
2098     IHandle_t *h;
2099
2100     for (i = 0; i < nVols; i++) {
2101         struct VolumeSummary *vs = isp[i].volSummary;
2102         VolumeDiskData volHeader;
2103         if (!vs) {
2104             /* Don't salvage just because phantom rw volume is there... */
2105             /* (If a read-only volume exists, read/write inodes must also exist) */
2106             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2107                 continue;
2108             return 0;
2109         }
2110         IH_INIT(h, salvinfo->fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2111         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2112             == sizeof(volHeader)
2113             && volHeader.stamp.magic == VOLUMEINFOMAGIC
2114             && volHeader.dontSalvage == DONT_SALVAGE
2115             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2116             if (volHeader.inUse != 0) {
2117                 volHeader.inUse = 0;
2118                 volHeader.inService = 1;
2119                 if (!Testing) {
2120                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2121                         != sizeof(volHeader)) {
2122                         IH_RELEASE(h);
2123                         return 0;
2124                     }
2125                 }
2126             }
2127             IH_RELEASE(h);
2128         } else {
2129             IH_RELEASE(h);
2130             return 0;
2131         }
2132     }
2133     return 1;
2134 }
2135
2136
2137 /* SalvageVolumeHeaderFile
2138  *
2139  * Salvage the top level V*.vol header file. Make sure the special files
2140  * exist and that there are no duplicates.
2141  *
2142  * Calls SalvageHeader for each possible type of volume special file.
2143  */
2144
2145 int
2146 SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
2147                         struct ViceInodeInfo *inodes, int RW,
2148                         int check, int *deleteMe)
2149 {
2150     int i;
2151     struct ViceInodeInfo *ip;
2152     int allinodesobsolete = 1;
2153     struct VolumeDiskHeader diskHeader;
2154     afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
2155     int *skip;
2156     struct VolumeHeader tempHeader;
2157     struct afs_inode_info stuff[MAXINODETYPE];
2158
2159     /* keeps track of special inodes that are probably 'good'; they are
2160      * referenced in the vol header, and are included in the given inodes
2161      * array */
2162     struct {
2163         int valid;
2164         Inode inode;
2165     } goodspecial[MAXINODETYPE];
2166
2167     if (deleteMe)
2168         *deleteMe = 0;
2169
2170     memset(goodspecial, 0, sizeof(goodspecial));
2171
2172     skip = malloc(isp->nSpecialInodes * sizeof(*skip));
2173     if (skip) {
2174         memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
2175     } else {
2176         Log("cannot allocate memory for inode skip array when salvaging "
2177             "volume %lu; not performing duplicate special inode recovery\n",
2178             afs_printable_uint32_lu(isp->volumeId));
2179         /* still try to perform the salvage; the skip array only does anything
2180          * if we detect duplicate special inodes */
2181     }
2182
2183     init_inode_info(&tempHeader, stuff);
2184
2185     /*
2186      * First, look at the special inodes and see if any are referenced by
2187      * the existing volume header. If we find duplicate special inodes, we
2188      * can use this information to use the referenced inode (it's more
2189      * likely to be the 'good' one), and throw away the duplicates.
2190      */
2191     if (isp->volSummary && skip) {
2192         /* use tempHeader, so we can use the stuff[] array to easily index
2193          * into the isp->volSummary special inodes */
2194         memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2195
2196         for (i = 0; i < isp->nSpecialInodes; i++) {
2197             ip = &inodes[isp->index + i];
2198             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2199                 /* will get taken care of in a later loop */
2200                 continue;
2201             }
2202             if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2203                 goodspecial[ip->u.special.type-1].valid = 1;
2204                 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2205             }
2206         }
2207     }
2208
2209     memset(&tempHeader, 0, sizeof(tempHeader));
2210     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2211     tempHeader.stamp.version = VOLUMEHEADERVERSION;
2212     tempHeader.id = isp->volumeId;
2213     tempHeader.parent = isp->RWvolumeId;
2214
2215     /* Check for duplicates (inodes are sorted by type field) */
2216     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2217         ip = &inodes[isp->index + i];
2218         if (ip->u.special.type == (ip + 1)->u.special.type) {
2219             afs_ino_str_t stmp1, stmp2;
2220
2221             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2222                 /* Will be caught in the loop below */
2223                 continue;
2224             }
2225             if (!Showmode) {
2226                 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2227                     ip->u.special.type, isp->volumeId,
2228                     PrintInode(stmp1, ip->inodeNumber),
2229                     PrintInode(stmp2, (ip+1)->inodeNumber));
2230             }
2231             if (skip && goodspecial[ip->u.special.type-1].valid) {
2232                 Inode gi = goodspecial[ip->u.special.type-1].inode;
2233
2234                 if (!Showmode) {
2235                     Log("using special inode referenced by vol header (%s)\n",
2236                         PrintInode(stmp1, gi));
2237                 }
2238
2239                 /* the volume header references some special inode of
2240                  * this type in the inodes array; are we it? */
2241                 if (ip->inodeNumber != gi) {
2242                     skip[i] = 1;
2243                 } else if ((ip+1)->inodeNumber != gi) {
2244                     /* in case this is the last iteration; we need to
2245                      * make sure we check ip+1, too */
2246                     skip[i+1] = 1;
2247                 }
2248             } else {
2249                 if (!Showmode)
2250                     Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2251                 if (skip) {
2252                     free(skip);
2253                 }
2254                 return -1;
2255             }
2256         }
2257     }
2258     for (i = 0; i < isp->nSpecialInodes; i++) {
2259         afs_ino_str_t stmp;
2260         ip = &inodes[isp->index + i];
2261         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2262             if (check) {
2263                 Log("Rubbish header inode %s of type %d\n",
2264                     PrintInode(stmp, ip->inodeNumber),
2265                     ip->u.special.type);
2266                 if (skip) {
2267                     free(skip);
2268                 }
2269                 return -1;
2270             }
2271             Log("Rubbish header inode %s of type %d; deleted\n",
2272                 PrintInode(stmp, ip->inodeNumber),
2273                 ip->u.special.type);
2274         } else if (!stuff[ip->u.special.type - 1].obsolete) {
2275             if (skip && skip[i]) {
2276                 if (orphans == ORPH_REMOVE) {
2277                     Log("Removing orphan special inode %s of type %d\n",
2278                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2279                     continue;
2280                 } else {
2281                     Log("Ignoring orphan special inode %s of type %d\n",
2282                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2283                     /* fall through to the ip->linkCount--; line below */
2284                 }
2285             } else {
2286                 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2287                 allinodesobsolete = 0;
2288             }
2289             if (!check && ip->u.special.type != VI_LINKTABLE)
2290                 ip->linkCount--;        /* Keep the inode around */
2291         }
2292     }
2293     if (skip) {
2294         free(skip);
2295     }
2296     skip = NULL;
2297
2298     if (allinodesobsolete) {
2299         if (deleteMe)
2300             *deleteMe = 1;
2301         return -1;
2302     }
2303
2304     if (!check)
2305         salvinfo->VGLinkH_cnt++;                /* one for every header. */
2306
2307     if (!RW && !check && isp->volSummary) {
2308         ClearROInUseBit(isp->volSummary);
2309         return 0;
2310     }
2311
2312     for (i = 0; i < MAXINODETYPE; i++) {
2313         if (stuff[i].inodeType == VI_LINKTABLE) {
2314             /* Gross hack: SalvageHeader does a bcmp on the volume header.
2315              * And we may have recreated the link table earlier, so set the
2316              * RW header as well. The header magic was already checked.
2317              */
2318             if (VALID_INO(salvinfo->VGLinkH->ih_ino)) {
2319                 *stuff[i].inode = salvinfo->VGLinkH->ih_ino;
2320             }
2321             continue;
2322         }
2323         if (SalvageHeader(salvinfo, &stuff[i], isp, check, deleteMe) == -1 && check)
2324             return -1;
2325     }
2326
2327     if (isp->volSummary == NULL) {
2328         char path[64];
2329         char headerName[64];
2330         snprintf(headerName, sizeof headerName, VFORMAT,
2331                  afs_printable_uint32_lu(isp->volumeId));
2332         snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
2333                  salvinfo->fileSysPath, headerName);
2334         if (check) {
2335             Log("No header file for volume %u\n", isp->volumeId);
2336             return -1;
2337         }
2338         if (!Showmode)
2339             Log("No header file for volume %u; %screating %s\n",
2340                 isp->volumeId, (Testing ? "it would have been " : ""),
2341                 path);
2342         isp->volSummary = calloc(1, sizeof(struct VolumeSummary));
2343
2344         writefunc = VCreateVolumeDiskHeader;
2345     } else {
2346         char path[64];
2347         char headerName[64];
2348         /* hack: these two fields are obsolete... */
2349         isp->volSummary->header.volumeAcl = 0;
2350         isp->volSummary->header.volumeMountTable = 0;
2351
2352         if (memcmp
2353             (&isp->volSummary->header, &tempHeader,
2354              sizeof(struct VolumeHeader))) {
2355             /* We often remove the name before calling us, so we make a fake one up */
2356             if (isp->volSummary->fileName) {
2357                 strcpy(headerName, isp->volSummary->fileName);
2358             } else {
2359                 snprintf(headerName, sizeof headerName, VFORMAT,
2360                          afs_printable_uint32_lu(isp->volumeId));
2361             }
2362             snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
2363                      salvinfo->fileSysPath, headerName);
2364
2365             Log("Header file %s is damaged or no longer valid%s\n", path,
2366                 (check ? "" : "; repairing"));
2367             if (check)
2368                 return -1;
2369
2370             writefunc = VWriteVolumeDiskHeader;
2371         }
2372     }
2373     if (writefunc) {
2374         memcpy(&isp->volSummary->header, &tempHeader,
2375                sizeof(struct VolumeHeader));
2376         if (Testing) {
2377             if (!Showmode)
2378                 Log("It would have written a new header file for volume %u\n",
2379                     isp->volumeId);
2380         } else {
2381             afs_int32 code;
2382             VolumeHeaderToDisk(&diskHeader, &tempHeader);
2383             code = (*writefunc)(&diskHeader, salvinfo->fileSysPartition);
2384             if (code) {
2385                 Log("Error %ld writing volume header file for volume %lu\n",
2386                     afs_printable_int32_ld(code),
2387                     afs_printable_uint32_lu(diskHeader.id));
2388                 return -1;
2389             }
2390         }
2391     }
2392     IH_INIT(isp->volSummary->volumeInfoHandle, salvinfo->fileSysDevice, isp->RWvolumeId,
2393             isp->volSummary->header.volumeInfo);
2394     return 0;
2395 }
2396
2397 int
2398 SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
2399               struct InodeSummary *isp, int check, int *deleteMe)
2400 {
2401     union {
2402         VolumeDiskData volumeInfo;
2403         struct versionStamp fileHeader;
2404     } header;
2405     IHandle_t *specH;
2406     int recreate = 0;
2407     ssize_t nBytes;
2408     FdHandle_t *fdP;
2409
2410     if (sp->obsolete)
2411         return 0;
2412 #ifndef AFS_NAMEI_ENV
2413     if (sp->inodeType == VI_LINKTABLE)
2414         return 0; /* header magic was already checked */
2415 #endif
2416     if (*(sp->inode) == 0) {
2417         if (check) {
2418             Log("Missing inode in volume header (%s)\n", sp->description);
2419             return -1;
2420         }
2421         if (!Showmode)
2422             Log("Missing inode in volume header (%s); %s\n", sp->description,
2423                 (Testing ? "it would have recreated it" : "recreating"));
2424         if (!Testing) {
2425             *(sp->inode) =
2426                 IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
2427                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2428             if (!VALID_INO(*(sp->inode)))
2429                 Abort
2430                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2431                      sp->description, errno);
2432         }
2433         recreate = 1;
2434     }
2435
2436     IH_INIT(specH, salvinfo->fileSysDevice, isp->RWvolumeId, *(sp->inode));
2437     fdP = IH_OPEN(specH);
2438     if (OKToZap && (fdP == NULL) && BadError(errno)) {
2439         /* bail out early and destroy the volume */
2440         if (!Showmode)
2441             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2442         if (deleteMe)
2443             *deleteMe = 1;
2444         IH_RELEASE(specH);
2445         return -1;
2446     }
2447     if (fdP == NULL)
2448         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2449               sp->description, errno);
2450
2451     if (!recreate
2452         && (FDH_PREAD(fdP, (char *)&header, sp->size, 0) != sp->size
2453             || header.fileHeader.magic != sp->stamp.magic)) {
2454         if (check) {
2455             Log("Part of the header (%s) is corrupted\n", sp->description);
2456             FDH_REALLYCLOSE(fdP);
2457             IH_RELEASE(specH);
2458             return -1;
2459         }
2460         Log("Part of the header (%s) is corrupted; recreating\n",
2461             sp->description);
2462         recreate = 1;
2463         /* header can be garbage; make sure we don't read garbage data from
2464          * it below */
2465         memset(&header, 0, sizeof(header));
2466     }
2467     if (sp->inodeType == VI_VOLINFO
2468         && header.volumeInfo.destroyMe == DESTROY_ME) {
2469         if (deleteMe)
2470             *deleteMe = 1;
2471         FDH_REALLYCLOSE(fdP);
2472         IH_RELEASE(specH);
2473         return -1;
2474     }
2475     if (recreate && !Testing) {
2476         if (check)
2477             Abort
2478                 ("Internal error: recreating volume header (%s) in check mode\n",
2479                  sp->description);
2480         nBytes = FDH_TRUNC(fdP, 0);
2481         if (nBytes == -1)
2482             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2483                   sp->description, errno);
2484
2485         /* The following code should be moved into vutil.c */
2486         if (sp->inodeType == VI_VOLINFO) {
2487             struct timeval tp;
2488             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2489             header.volumeInfo.stamp = sp->stamp;
2490             header.volumeInfo.id = isp->volumeId;
2491             header.volumeInfo.parentId = isp->RWvolumeId;
2492             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2493             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2494                 isp->volumeId, isp->volumeId);
2495             header.volumeInfo.inService = 0;
2496             header.volumeInfo.blessed = 0;
2497             /* The + 1000 is a hack in case there are any files out in venus caches */
2498             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2499             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
2500             header.volumeInfo.needsCallback = 0;
2501             gettimeofday(&tp, NULL);
2502             header.volumeInfo.creationDate = tp.tv_sec;
2503             nBytes =
2504                 FDH_PWRITE(fdP, (char *)&header.volumeInfo,
2505                            sizeof(header.volumeInfo), 0);
2506             if (nBytes != sizeof(header.volumeInfo)) {
2507                 if (nBytes < 0)
2508                     Abort
2509                         ("Unable to write volume header file (%s) (errno = %d)\n",
2510                          sp->description, errno);
2511                 Abort("Unable to write entire volume header file (%s)\n",
2512                       sp->description);
2513             }
2514         } else {
2515             nBytes = FDH_PWRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp), 0);
2516             if (nBytes != sizeof(sp->stamp)) {
2517                 if (nBytes < 0)
2518                     Abort
2519                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2520                          sp->description, errno);
2521                 Abort
2522                     ("Unable to write entire version stamp in volume header file (%s)\n",
2523                      sp->description);
2524             }
2525         }
2526     }
2527     FDH_REALLYCLOSE(fdP);
2528     IH_RELEASE(specH);
2529     if (sp->inodeType == VI_VOLINFO) {
2530         salvinfo->VolInfo = header.volumeInfo;
2531         if (check) {
2532             char update[25];
2533
2534             if (salvinfo->VolInfo.updateDate) {
2535                 strcpy(update, TimeStamp(salvinfo->VolInfo.updateDate, 0));
2536                 if (!Showmode)
2537                     Log("%s (%u) %supdated %s\n", salvinfo->VolInfo.name,
2538                         salvinfo->VolInfo.id,
2539                         (Testing ? "it would have been " : ""), update);
2540             } else {
2541                 strcpy(update, TimeStamp(salvinfo->VolInfo.creationDate, 0));
2542                 if (!Showmode)
2543                     Log("%s (%u) not updated (created %s)\n",
2544                         salvinfo->VolInfo.name, salvinfo->VolInfo.id, update);
2545             }
2546
2547         }
2548     }
2549
2550     return 0;
2551 }
2552
2553 int
2554 SalvageVnodes(struct SalvInfo *salvinfo,
2555               struct InodeSummary *rwIsp,
2556               struct InodeSummary *thisIsp,
2557               struct ViceInodeInfo *inodes, int check)
2558 {
2559     int ilarge, ismall, ioffset, RW, nInodes;
2560     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
2561     if (Showmode)
2562         return 0;
2563     RW = (rwIsp == thisIsp);
2564     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2565     ismall =
2566         SalvageIndex(salvinfo, thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2567                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2568     if (check && ismall == -1)
2569         return -1;
2570     ilarge =
2571         SalvageIndex(salvinfo, thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2572                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2573     return (ilarge == 0 && ismall == 0 ? 0 : -1);
2574 }
2575
2576 int
2577 SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
2578              struct ViceInodeInfo *ip, int nInodes,
2579              struct VolumeSummary *volSummary, int check)
2580 {
2581     char buf[SIZEOF_LARGEDISKVNODE];
2582     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2583     int err = 0;
2584     StreamHandle_t *file;
2585     struct VnodeClassInfo *vcp;
2586     afs_sfsize_t size;
2587     afs_sfsize_t nVnodes;
2588     afs_fsize_t vnodeLength;
2589     int vnodeIndex;
2590     afs_ino_str_t stmp1, stmp2;
2591     IHandle_t *handle;
2592     FdHandle_t *fdP;
2593
2594     IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
2595     fdP = IH_OPEN(handle);
2596     osi_Assert(fdP != NULL);
2597     file = FDH_FDOPEN(fdP, "r+");
2598     osi_Assert(file != NULL);
2599     vcp = &VnodeClassInfo[class];
2600     size = OS_SIZE(fdP->fd_fd);
2601     osi_Assert(size != -1);
2602     nVnodes = (size / vcp->diskSize) - 1;
2603     if (nVnodes > 0) {
2604         osi_Assert((nVnodes + 1) * vcp->diskSize == size);
2605         osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
2606     } else {
2607         nVnodes = 0;
2608     }
2609     for (vnodeIndex = 0;
2610          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2611          nVnodes--, vnodeIndex++) {
2612         if (vnode->type != vNull) {
2613             int vnodeChanged = 0;
2614             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2615             if (VNDISK_GET_INO(vnode) == 0) {
2616                 if (RW) {
2617                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2618                     memset(vnode, 0, vcp->diskSize);
2619                     vnodeChanged = 1;
2620                 }
2621             } else {
2622                 if (vcp->magic != vnode->vnodeMagic) {
2623                     /* bad magic #, probably partially created vnode */
2624                     if (check) {
2625                        Log("Partially allocated vnode %d: bad magic (is %lx should be %lx)\n",
2626                            vnodeNumber, afs_printable_uint32_lu(vnode->vnodeMagic),
2627                            afs_printable_uint32_lu(vcp->magic));
2628                        memset(vnode, 0, vcp->diskSize);
2629                        err = -1;
2630                        goto zooks;
2631                     }
2632                     Log("Partially allocated vnode %d deleted.\n",
2633                         vnodeNumber);
2634                     memset(vnode, 0, vcp->diskSize);
2635                     vnodeChanged = 1;
2636                     goto vnodeDone;
2637                 }
2638                 /* ****** Should do a bit more salvage here:  e.g. make sure
2639                  * vnode type matches what it should be given the index */
2640                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2641 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2642  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2643  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2644  *                  }
2645  */
2646                     ip++;
2647                     nInodes--;
2648                 }
2649                 if (!RW) {
2650                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2651                         /* The following doesn't work, because the version number
2652                          * is not maintained correctly by the file server */
2653                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2654                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2655                          * break; */
2656                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2657                             break;
2658                         ip++;
2659                         nInodes--;
2660                     }
2661                 } else {
2662                     /* For RW volume, look for vnode with matching inode number;
2663                      * if no such match, take the first determined by our sort
2664                      * order */
2665                     struct ViceInodeInfo *lip = ip;
2666                     int lnInodes = nInodes;
2667                     while (lnInodes
2668                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2669                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2670                             ip = lip;
2671                             nInodes = lnInodes;
2672                             break;
2673                         }
2674                         lip++;
2675                         lnInodes--;
2676                     }
2677                 }
2678                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2679                     /* "Matching" inode */
2680                     if (RW) {
2681                         Unique vu, iu;
2682                         FileVersion vd, id;
2683                         vu = vnode->uniquifier;
2684                         iu = ip->u.vnode.vnodeUniquifier;
2685                         vd = vnode->dataVersion;
2686                         id = ip->u.vnode.inodeDataVersion;
2687                         /*
2688                          * Because of the possibility of the uniquifier overflows (> 4M)
2689                          * we compare them modulo the low 22-bits; we shouldn't worry
2690                          * about mismatching since they shouldn't to many old
2691                          * uniquifiers of the same vnode...
2692                          */
2693                         if (IUnique(vu) != IUnique(iu)) {
2694                             if (!Showmode) {
2695                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2696                             }
2697
2698                             vnode->uniquifier = iu;
2699 #ifdef  AFS_3DISPARES
2700                             vnode->dataVersion = (id >= vd ?
2701                                                   /* 90% of 2.1M */
2702                                                   ((id - vd) >
2703                                                    1887437 ? vd : id) :
2704                                                   /* 90% of 2.1M */
2705                                                   ((vd - id) >
2706                                                    1887437 ? id : vd));
2707 #else
2708 #if defined(AFS_SGI_EXMAG)
2709                             vnode->dataVersion = (id >= vd ?
2710                                                   /* 90% of 16M */
2711                                                   ((id - vd) >
2712                                                    15099494 ? vd : id) :
2713                                                   /* 90% of 16M */
2714                                                   ((vd - id) >
2715                                                    15099494 ? id : vd));
2716 #else
2717                             vnode->dataVersion = (id > vd ? id : vd);
2718 #endif /* AFS_SGI_EXMAG */
2719 #endif /* AFS_3DISPARES */
2720                             vnodeChanged = 1;
2721                         } else {
2722                             /* don't bother checking for vd > id any more, since
2723                              * partial file transfers always result in this state,
2724                              * and you can't do much else anyway (you've already
2725                              * found the best data you can) */
2726 #ifdef  AFS_3DISPARES
2727                             if (!vnodeIsDirectory(vnodeNumber)
2728                                 && ((vd < id && (id - vd) < 1887437)
2729                                     || ((vd > id && (vd - id) > 1887437)))) {
2730 #else
2731 #if defined(AFS_SGI_EXMAG)
2732                             if (!vnodeIsDirectory(vnodeNumber)
2733                                 && ((vd < id && (id - vd) < 15099494)
2734                                     || ((vd > id && (vd - id) > 15099494)))) {
2735 #else
2736                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2737 #endif /* AFS_SGI_EXMAG */
2738 #endif
2739                                 if (!Showmode)
2740                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2741                                 vnode->dataVersion = id;
2742                                 vnodeChanged = 1;
2743                             }
2744                         }
2745                     }
2746                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2747                         if (check) {
2748                             if (!Showmode) {
2749                                 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);
2750                             }
2751                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2752                             err = -1;
2753                             goto zooks;
2754                         }
2755                         if (!Showmode) {
2756                             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);
2757                         }
2758                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2759                         vnodeChanged = 1;
2760                     }
2761                     VNDISK_GET_LEN(vnodeLength, vnode);
2762                     if (ip->byteCount != vnodeLength) {
2763                         if (check) {
2764                             if (!Showmode)
2765                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2766                             err = -1;
2767                             goto zooks;
2768                         }
2769                         if (!Showmode)
2770                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2771                         VNDISK_SET_LEN(vnode, ip->byteCount);
2772                         vnodeChanged = 1;
2773                     }
2774                     if (!check)
2775                         ip->linkCount--;        /* Keep the inode around */
2776                     ip++;
2777                     nInodes--;
2778                 } else {        /* no matching inode */
2779                     afs_ino_str_t stmp;
2780                     if (VNDISK_GET_INO(vnode) != 0
2781                         || vnode->type == vDirectory) {
2782                         /* No matching inode--get rid of the vnode */
2783                         if (check) {
2784                             if (VNDISK_GET_INO(vnode)) {
2785                                 if (!Showmode) {
2786                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)));
2787                                 }
2788                             } else {
2789                                 if (!Showmode)
2790                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2791                             }
2792                             err = -1;
2793                             goto zooks;
2794                         }
2795                         if (VNDISK_GET_INO(vnode)) {
2796                             if (!Showmode) {
2797                                 time_t serverModifyTime = vnode->serverModifyTime;
2798                                 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));
2799                             }
2800                         } else {
2801                             if (!Showmode) {
2802                                 time_t serverModifyTime = vnode->serverModifyTime;
2803                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2804                             }
2805                         }
2806                         memset(vnode, 0, vcp->diskSize);
2807                         vnodeChanged = 1;
2808                     } else {
2809                         /* Should not reach here becuase we checked for
2810                          * (inodeNumber == 0) above. And where we zero the vnode,
2811                          * we also goto vnodeDone.
2812                          */
2813                     }
2814                 }
2815                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2816                     ip++;
2817                     nInodes--;
2818                 }
2819             }                   /* VNDISK_GET_INO(vnode) != 0 */
2820           vnodeDone:
2821             osi_Assert(!(vnodeChanged && check));
2822             if (vnodeChanged && !Testing) {
2823                 osi_Assert(IH_IWRITE
2824                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2825                         (char *)vnode, vcp->diskSize)
2826                        == vcp->diskSize);
2827                 salvinfo->VolumeChanged = 1;    /* For break call back */
2828             }
2829         }
2830     }
2831   zooks:
2832     STREAM_CLOSE(file);
2833     FDH_CLOSE(fdP);
2834     IH_RELEASE(handle);
2835     return err;
2836 }
2837
2838 struct VnodeEssence *
2839 CheckVnodeNumber(struct SalvInfo *salvinfo, VnodeId vnodeNumber)
2840 {
2841     VnodeClass class;
2842     struct VnodeInfo *vip;
2843     int offset;
2844
2845     class = vnodeIdToClass(vnodeNumber);
2846     vip = &salvinfo->vnodeInfo[class];
2847     offset = vnodeIdToBitNumber(vnodeNumber);
2848     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2849 }
2850
2851 void
2852 CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
2853 {
2854     /* Copy the directory unconditionally if we are going to change it:
2855      * not just if was cloned.
2856      */
2857     struct VnodeDiskObject vnode;
2858     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2859     Inode oldinode, newinode;
2860     afs_sfsize_t code;
2861
2862     if (dir->copied || Testing)
2863         return;
2864     DFlush();                   /* Well justified paranoia... */
2865
2866     code =
2867         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2868                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2869                  sizeof(vnode));
2870     osi_Assert(code == sizeof(vnode));
2871     oldinode = VNDISK_GET_INO(&vnode);
2872     /* Increment the version number by a whole lot to avoid problems with
2873      * clients that were promised new version numbers--but the file server
2874      * crashed before the versions were written to disk.
2875      */
2876     newinode =
2877         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2878                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2879                   200);
2880     osi_Assert(VALID_INO(newinode));
2881     osi_Assert(CopyInode(salvinfo->fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2882     vnode.cloned = 0;
2883     VNDISK_SET_INO(&vnode, newinode);
2884     code =
2885         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
2886                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2887                   sizeof(vnode));
2888     osi_Assert(code == sizeof(vnode));
2889
2890     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2891                         salvinfo->fileSysDevice, newinode,
2892                         &salvinfo->VolumeChanged);
2893     /* Don't delete the original inode right away, because the directory is
2894      * still being scanned.
2895      */
2896     dir->copied = 1;
2897 }
2898
2899 /*
2900  * This function should either successfully create a new dir, or give up
2901  * and leave things the way they were.  In particular, if it fails to write
2902  * the new dir properly, it should return w/o changing the reference to the
2903  * old dir.
2904  */
2905 void
2906 CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
2907 {
2908     struct VnodeDiskObject vnode;
2909     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2910     Inode oldinode, newinode;
2911     DirHandle newdir;
2912     FdHandle_t *fdP;
2913     afs_int32 code;
2914     afs_sfsize_t lcode;
2915     afs_int32 parentUnique = 1;
2916     struct VnodeEssence *vnodeEssence;
2917     afs_fsize_t length;
2918
2919     if (Testing)
2920         return;
2921     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2922     lcode =
2923         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2924                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2925                  sizeof(vnode));
2926     osi_Assert(lcode == sizeof(vnode));
2927     oldinode = VNDISK_GET_INO(&vnode);
2928     /* Increment the version number by a whole lot to avoid problems with
2929      * clients that were promised new version numbers--but the file server
2930      * crashed before the versions were written to disk.
2931      */
2932     newinode =
2933         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2934                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2935                   200);
2936     osi_Assert(VALID_INO(newinode));
2937     SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
2938                         &salvinfo->VolumeChanged);
2939
2940     /* Assign . and .. vnode numbers from dir and vnode.parent.
2941      * The uniquifier for . is in the vnode.
2942      * The uniquifier for .. might be set to a bogus value of 1 and
2943      * the salvager will later clean it up.
2944      */
2945     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(salvinfo, vnode.parent))) {
2946         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2947     }
2948     code =
2949         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2950                    vnode.uniquifier,
2951                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2952                    parentUnique);
2953     if (code == 0)
2954         code = DFlush();
2955     if (code) {
2956         /* didn't really build the new directory properly, let's just give up. */
2957         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2958         Log("Directory salvage returned code %d, continuing.\n", code);
2959         if (code) {
2960             Log("also failed to decrement link count on new inode");
2961         }
2962         osi_Assert(1 == 2);
2963     }
2964     Log("Checking the results of the directory salvage...\n");
2965     if (!DirOK(&newdir)) {
2966         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2967         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2968         osi_Assert(code == 0);
2969         osi_Assert(1 == 2);
2970     }
2971     vnode.cloned = 0;
2972     VNDISK_SET_INO(&vnode, newinode);
2973     length = afs_dir_Length(&newdir);
2974     VNDISK_SET_LEN(&vnode, length);
2975     lcode =
2976         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
2977                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2978                   sizeof(vnode));
2979     osi_Assert(lcode == sizeof(vnode));
2980 #if 0
2981 #ifdef AFS_NT40_ENV
2982     nt_sync(salvinfo->fileSysDevice);
2983 #else
2984     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2985                                  * an open FD on the file itself to fsync.
2986                                  */
2987 #endif
2988 #else
2989     salvinfo->vnodeInfo[vLarge].handle->ih_synced = 1;
2990 #endif
2991     /* make sure old directory file is really closed */
2992     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2993     FDH_REALLYCLOSE(fdP);
2994
2995     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2996     osi_Assert(code == 0);
2997     dir->dirHandle = newdir;
2998 }
2999
3000 /**
3001  * arguments for JudgeEntry.
3002  */
3003 struct judgeEntry_params {
3004     struct DirSummary *dir;    /**< directory we're examining entries in */
3005     struct SalvInfo *salvinfo; /**< SalvInfo for the current salvage job */
3006 };
3007
3008 int
3009 JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
3010            afs_int32 unique)
3011 {
3012     struct judgeEntry_params *params = arock;
3013     struct DirSummary *dir = params->dir;
3014     struct SalvInfo *salvinfo = params->salvinfo;
3015     struct VnodeEssence *vnodeEssence;
3016     afs_int32 dirOrphaned, todelete;
3017
3018     dirOrphaned = IsVnodeOrphaned(salvinfo, dir->vnodeNumber);
3019
3020     vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3021     if (vnodeEssence == NULL) {
3022         if (!Showmode) {
3023             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);
3024         }
3025         if (!Testing) {
3026             CopyOnWrite(salvinfo, dir);
3027             osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
3028         }
3029         return 0;
3030     }
3031 #ifdef AFS_AIX_ENV
3032 #ifndef AFS_NAMEI_ENV
3033     /* On AIX machines, don't allow entries to point to inode 0. That is a special
3034      * mount inode for the partition. If this inode were deleted, it would crash
3035      * the machine.
3036      */
3037     if (vnodeEssence->InodeNumber == 0) {
3038         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"));
3039         if (!Testing) {
3040             CopyOnWrite(salvinfo, dir);
3041             osi_Assert(Delete(&dir->dirHandle, name) == 0);
3042         }
3043         return 0;
3044     }
3045 #endif
3046 #endif
3047
3048     if (!(vnodeNumber & 1) && !Showmode
3049         && !(vnodeEssence->count || vnodeEssence->unique
3050              || vnodeEssence->modeBits)) {
3051         Log("dir vnode %u: invalid entry: %s" OS_DIRSEP "%s (vnode %u, unique %u)%s\n",
3052             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
3053             vnodeNumber, unique,
3054             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
3055              ""));
3056         if (!unique) {
3057             if (!Testing) {
3058                 CopyOnWrite(salvinfo, dir);
3059                 osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
3060             }
3061             return 0;
3062         }
3063     }
3064
3065     /* Check if the Uniquifiers match. If not, change the directory entry
3066      * so its unique matches the vnode unique. Delete if the unique is zero
3067      * or if the directory is orphaned.
3068      */
3069     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
3070         if (!vnodeEssence->unique
3071             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
3072             /* This is an orphaned directory. Don't delete the . or ..
3073              * entry. Otherwise, it will get created in the next
3074              * salvage and deleted again here. So Just skip it.
3075              */
3076             return 0;
3077         }
3078
3079         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
3080
3081         if (!Showmode) {
3082             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")));
3083         }
3084         if (!Testing) {
3085             AFSFid fid;
3086             fid.Vnode = vnodeNumber;
3087             fid.Unique = vnodeEssence->unique;
3088             CopyOnWrite(salvinfo, dir);
3089             osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
3090             if (!todelete)
3091                 osi_Assert(afs_dir_Create(&dir->dirHandle, name, &fid) == 0);
3092         }
3093         if (todelete)
3094             return 0;           /* no need to continue */
3095     }
3096
3097     if (strcmp(name, ".") == 0) {
3098         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
3099             AFSFid fid;
3100             if (!Showmode)
3101                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3102             if (!Testing) {
3103                 CopyOnWrite(salvinfo, dir);
3104                 osi_Assert(afs_dir_Delete(&dir->dirHandle, ".") == 0);
3105                 fid.Vnode = dir->vnodeNumber;
3106                 fid.Unique = dir->unique;
3107                 osi_Assert(afs_dir_Create(&dir->dirHandle, ".", &fid) == 0);
3108             }
3109
3110             vnodeNumber = fid.Vnode;    /* Get the new Essence */
3111             unique = fid.Unique;
3112             vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3113         }
3114         dir->haveDot = 1;
3115     } else if (strcmp(name, "..") == 0) {
3116         AFSFid pa;
3117         if (dir->parent) {
3118             struct VnodeEssence *dotdot;
3119             pa.Vnode = dir->parent;
3120             dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
3121             osi_Assert(dotdot != NULL); /* XXX Should not be assert */
3122             pa.Unique = dotdot->unique;
3123         } else {
3124             pa.Vnode = dir->vnodeNumber;
3125             pa.Unique = dir->unique;
3126         }
3127         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
3128             if (!Showmode)
3129                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3130             if (!Testing) {
3131                 CopyOnWrite(salvinfo, dir);
3132                 osi_Assert(afs_dir_Delete(&dir->dirHandle, "..") == 0);
3133                 osi_Assert(afs_dir_Create(&dir->dirHandle, "..", &pa) == 0);
3134             }
3135
3136             vnodeNumber = pa.Vnode;     /* Get the new Essence */
3137             unique = pa.Unique;
3138             vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3139         }
3140         dir->haveDotDot = 1;
3141     } else if (strncmp(name, ".__afs", 6) == 0) {
3142         if (!Showmode) {
3143             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);
3144         }
3145         if (!Testing) {
3146             CopyOnWrite(salvinfo, dir);
3147             osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
3148         }
3149         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
3150         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
3151         return 0;
3152     } else {
3153         if (ShowSuid && (vnodeEssence->modeBits & 06000))
3154             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);
3155         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
3156             && !(vnodeEssence->modeBits & 0111)) {
3157             afs_sfsize_t nBytes;
3158             afs_sfsize_t size;
3159             char buf[1025];
3160             IHandle_t *ihP;
3161             FdHandle_t *fdP;
3162
3163             IH_INIT(ihP, salvinfo->fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3164                     vnodeEssence->InodeNumber);
3165             fdP = IH_OPEN(ihP);
3166             if (fdP == NULL) {
3167                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
3168                 IH_RELEASE(ihP);
3169                 return 0;
3170             }
3171             size = FDH_SIZE(fdP);
3172             if (size < 0) {
3173                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, (int)size, vnodeNumber);
3174                 FDH_REALLYCLOSE(fdP);
3175                 IH_RELEASE(ihP);
3176                 return 0;
3177             }
3178
3179             if (size > 1024)
3180                 size = 1024;
3181             nBytes = FDH_PREAD(fdP, buf, size, 0);
3182             if (nBytes == size) {
3183                 buf[size] = '\0';
3184                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
3185                     Log("Volume %u (%s) mount point %s" OS_DIRSEP "%s to '%s' invalid, %s to symbolic link\n",
3186                         dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
3187                         Testing ? "would convert" : "converted");
3188                     vnodeEssence->modeBits |= 0111;
3189                     vnodeEssence->changed = 1;
3190                 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s" OS_DIRSEP "%s to '%s'\n",
3191                     dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3192                     dir->name ? dir->name : "??", name, buf);
3193             } else {
3194                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3195                     dir->vname, vnodeNumber, (int)size, (int)nBytes);
3196             }
3197             FDH_REALLYCLOSE(fdP);
3198             IH_RELEASE(ihP);
3199         }
3200         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3201             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);
3202         if (vnodeIdToClass(vnodeNumber) == vLarge
3203             && vnodeEssence->name == NULL) {
3204             char *n;
3205             if ((n = (char *)malloc(strlen(name) + 1)))
3206                 strcpy(n, name);
3207             vnodeEssence->name = n;
3208         }
3209
3210         /* The directory entry points to the vnode. Check to see if the
3211          * vnode points back to the directory. If not, then let the
3212          * directory claim it (else it might end up orphaned). Vnodes
3213          * already claimed by another directory are deleted from this
3214          * directory: hardlinks to the same vnode are not allowed
3215          * from different directories.
3216          */
3217         if (vnodeEssence->parent != dir->vnodeNumber) {
3218             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3219                 /* Vnode does not point back to this directory.
3220                  * Orphaned dirs cannot claim a file (it may belong to
3221                  * another non-orphaned dir).
3222                  */
3223                 if (!Showmode) {
3224                     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);
3225                 }
3226                 vnodeEssence->parent = dir->vnodeNumber;
3227                 vnodeEssence->changed = 1;
3228             } else {
3229                 /* Vnode was claimed by another directory */
3230                 if (!Showmode) {
3231                     if (dirOrphaned) {
3232                         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 " : ""));
3233                     } else if (vnodeNumber == 1) {
3234                         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 " : ""));
3235                     } else {
3236                         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 " : ""));
3237                     }
3238                 }
3239                 if (!Testing) {
3240                     CopyOnWrite(salvinfo, dir);
3241                     osi_Assert(afs_dir_Delete(&dir->dirHandle, name) == 0);
3242                 }
3243                 return 0;
3244             }
3245         }
3246         /* This directory claims the vnode */
3247         vnodeEssence->claimed = 1;
3248     }
3249     vnodeEssence->count--;
3250     return 0;
3251 }
3252
3253 void
3254 DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
3255                    VnodeClass class, Inode ino, Unique * maxu)
3256 {
3257     struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
3258     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3259     char buf[SIZEOF_LARGEDISKVNODE];
3260     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3261     afs_sfsize_t size;
3262     StreamHandle_t *file;
3263     int vnodeIndex;
3264     int nVnodes;
3265     FdHandle_t *fdP;
3266
3267     IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
3268     fdP = IH_OPEN(vip->handle);
3269     osi_Assert(fdP != NULL);
3270     file = FDH_FDOPEN(fdP, "r+");
3271     osi_Assert(file != NULL);
3272     size = OS_SIZE(fdP->fd_fd);
3273     osi_Assert(size != -1);
3274     vip->nVnodes = (size / vcp->diskSize) - 1;
3275     if (vip->nVnodes > 0) {
3276         osi_Assert((vip->nVnodes + 1) * vcp->diskSize == size);
3277         osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
3278         osi_Assert((vip->vnodes = (struct VnodeEssence *)
3279                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3280         if (class == vLarge) {
3281             osi_Assert((vip->inodes = (Inode *)
3282                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3283         } else {
3284             vip->inodes = NULL;
3285         }
3286     } else {
3287         vip->nVnodes = 0;
3288         vip->vnodes = NULL;
3289         vip->inodes = NULL;
3290     }
3291     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3292     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3293          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3294          nVnodes--, vnodeIndex++) {
3295         if (vnode->type != vNull) {
3296             struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3297             afs_fsize_t vnodeLength;
3298             vip->nAllocatedVnodes++;
3299             vep->count = vnode->linkCount;
3300             VNDISK_GET_LEN(vnodeLength, vnode);
3301             vep->blockCount = nBlocks(vnodeLength);
3302             vip->volumeBlockCount += vep->blockCount;
3303             vep->parent = vnode->parent;
3304             vep->unique = vnode->uniquifier;
3305             if (*maxu < vnode->uniquifier)
3306                 *maxu = vnode->uniquifier;
3307             vep->modeBits = vnode->modeBits;
3308             vep->InodeNumber = VNDISK_GET_INO(vnode);
3309             vep->type = vnode->type;
3310             vep->author = vnode->author;
3311             vep->owner = vnode->owner;
3312             vep->group = vnode->group;
3313             if (vnode->type == vDirectory) {
3314                 if (class != vLarge) {
3315                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3316                     vip->nAllocatedVnodes--;
3317                     memset(vnode, 0, sizeof(vnode));
3318                     IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3319                               vnodeIndexOffset(vcp, vnodeNumber),
3320                               (char *)&vnode, sizeof(vnode));
3321                     salvinfo->VolumeChanged = 1;
3322                 } else
3323                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3324             }
3325         }
3326     }
3327     STREAM_CLOSE(file);
3328     FDH_CLOSE(fdP);
3329 }
3330
3331 static char *
3332 GetDirName(struct SalvInfo *salvinfo, VnodeId vnode, struct VnodeEssence *vp,
3333            char *path)
3334 {
3335     struct VnodeEssence *parentvp;
3336
3337     if (vnode == 1) {
3338         strcpy(path, ".");
3339         return path;
3340     }
3341     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(salvinfo, vp->parent))
3342         && GetDirName(salvinfo, vp->parent, parentvp, path)) {
3343         strcat(path, OS_DIRSEP);
3344         strcat(path, vp->name);
3345         return path;
3346     }
3347     return 0;
3348 }
3349
3350 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3351  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3352  */
3353 static int
3354 IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode)
3355 {
3356     struct VnodeEssence *vep;
3357
3358     if (vnode == 0)
3359         return (1);             /* Vnode zero does not exist */
3360     if (vnode == 1)
3361         return (0);             /* The root dir vnode is always claimed */
3362     vep = CheckVnodeNumber(salvinfo, vnode);    /* Get the vnode essence */
3363     if (!vep || !vep->claimed)
3364         return (1);             /* Vnode is not claimed - it is orphaned */
3365
3366     return (IsVnodeOrphaned(salvinfo, vep->parent));
3367 }
3368
3369 void
3370 SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
3371            struct VnodeInfo *dirVnodeInfo, IHandle_t * alinkH, int i,
3372            struct DirSummary *rootdir, int *rootdirfound)
3373 {
3374     static struct DirSummary dir;
3375     static struct DirHandle dirHandle;
3376     struct VnodeEssence *parent;
3377     static char path[MAXPATHLEN];
3378     int dirok, code;
3379
3380     if (dirVnodeInfo->vnodes[i].salvaged)
3381         return;                 /* already salvaged */
3382
3383     dir.rwVid = rwVid;
3384     dirVnodeInfo->vnodes[i].salvaged = 1;
3385
3386     if (dirVnodeInfo->inodes[i] == 0)
3387         return;                 /* Not allocated to a directory */
3388
3389     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3390         if (dirVnodeInfo->vnodes[i].parent) {
3391             Log("Bad parent, vnode 1; %s...\n",
3392                 (Testing ? "skipping" : "salvaging"));
3393             dirVnodeInfo->vnodes[i].parent = 0;
3394             dirVnodeInfo->vnodes[i].changed = 1;
3395         }
3396     } else {
3397         parent = CheckVnodeNumber(salvinfo, dirVnodeInfo->vnodes[i].parent);
3398