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