138bf05e453121f712c75a4445be944b10a5b261
[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)
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 res[RES_LEN];
473     int d1, d2;
474     static int dowarn = 1;
475
476     if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
477         return 1;
478     if (strncmp(res, HDSTR, HDLEN)) {
479         if (dowarn) {
480             dowarn = 0;
481             Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
482                 res, HDSTR, p1->devName);
483         }
484         return 1;
485     }
486     d1 = atoi(&res[HDLEN]);
487
488     if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
489         return 1;
490     if (strncmp(res, HDSTR, HDLEN)) {
491         if (dowarn) {
492             dowarn = 0;
493             Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
494                 res, HDSTR, p2->devName);
495         }
496         return 1;
497     }
498     d2 = atoi(&res[HDLEN]);
499
500     return d1 == d2;
501 }
502 #else
503 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
504 #endif
505
506 /* This assumes that two partitions with the same device number divided by
507  * PartsPerDisk are on the same disk.
508  */
509 void
510 SalvageFileSysParallel(struct DiskPartition64 *partP)
511 {
512     struct job {
513         struct DiskPartition64 *partP;
514         int pid;                /* Pid for this job */
515         int jobnumb;            /* Log file job number */
516         struct job *nextjob;    /* Next partition on disk to salvage */
517     };
518     static struct job *jobs[MAXPARALLEL] = { 0 };       /* Need to zero this */
519     struct job *thisjob = 0;
520     static int numjobs = 0;
521     static int jobcount = 0;
522     char buf[1024];
523     int wstatus;
524     struct job *oldjob;
525     int startjob;
526     FILE *passLog;
527     char logFileName[256];
528     int i, j, pid;
529
530     if (partP) {
531         /* We have a partition to salvage. Copy it into thisjob */
532         thisjob = (struct job *)malloc(sizeof(struct job));
533         if (!thisjob) {
534             Log("Can't salvage '%s'. Not enough memory\n", partP->name);
535             return;
536         }
537         memset(thisjob, 0, sizeof(struct job));
538         thisjob->partP = partP;
539         thisjob->jobnumb = jobcount;
540         jobcount++;
541     } else if (jobcount == 0) {
542         /* We are asking to wait for all jobs (partp == 0), yet we never
543          * started any.
544          */
545         Log("No file system partitions named %s* found; not salvaged\n",
546             VICE_PARTITION_PREFIX);
547         return;
548     }
549
550     if (debug || Parallel == 1) {
551         if (thisjob) {
552             SalvageFileSys(thisjob->partP, 0);
553             free(thisjob);
554         }
555         return;
556     }
557
558     if (thisjob) {
559         /* Check to see if thisjob is for a disk that we are already
560          * salvaging. If it is, link it in as the next job to do. The
561          * jobs array has 1 entry per disk being salvages. numjobs is
562          * the total number of disks currently being salvaged. In
563          * order to keep thejobs array compact, when a disk is
564          * completed, the hightest element in the jobs array is moved
565          * down to now open slot.
566          */
567         for (j = 0; j < numjobs; j++) {
568             if (SameDisk(jobs[j]->partP, thisjob->partP)) {
569                 /* On same disk, add it to this list and return */
570                 thisjob->nextjob = jobs[j]->nextjob;
571                 jobs[j]->nextjob = thisjob;
572                 thisjob = 0;
573                 break;
574             }
575         }
576     }
577
578     /* Loop until we start thisjob or until all existing jobs are finished */
579     while (thisjob || (!partP && (numjobs > 0))) {
580         startjob = -1;          /* No new job to start */
581
582         if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
583             /* Either the max jobs are running or we have to wait for all
584              * the jobs to finish. In either case, we wait for at least one
585              * job to finish. When it's done, clean up after it.
586              */
587             pid = wait(&wstatus);
588             osi_Assert(pid != -1);
589             for (j = 0; j < numjobs; j++) {     /* Find which job it is */
590                 if (pid == jobs[j]->pid)
591                     break;
592             }
593             osi_Assert(j < numjobs);
594             if (WCOREDUMP(wstatus)) {   /* Say if the job core dumped */
595                 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
596             }
597
598             numjobs--;          /* job no longer running */
599             oldjob = jobs[j];   /* remember */
600             jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
601             free(oldjob);       /* free the old job */
602
603             /* If there is another partition on the disk to salvage, then
604              * say we will start it (startjob). If not, then put thisjob there
605              * and say we will start it.
606              */
607             if (jobs[j]) {      /* Another partitions to salvage */
608                 startjob = j;   /* Will start it */
609             } else {            /* There is not another partition to salvage */
610                 if (thisjob) {
611                     jobs[j] = thisjob;  /* Add thisjob */
612                     thisjob = 0;
613                     startjob = j;       /* Will start it */
614                 } else {
615                     jobs[j] = jobs[numjobs];    /* Move last job up to this slot */
616                     startjob = -1;      /* Don't start it - already running */
617                 }
618             }
619         } else {
620             /* We don't have to wait for a job to complete */
621             if (thisjob) {
622                 jobs[numjobs] = thisjob;        /* Add this job */
623                 thisjob = 0;
624                 startjob = numjobs;     /* Will start it */
625             }
626         }
627
628         /* Start up a new salvage job on a partition in job slot "startjob" */
629         if (startjob != -1) {
630             if (!Showmode)
631                 Log("Starting salvage of file system partition %s\n",
632                     jobs[startjob]->partP->name);
633 #ifdef AFS_NT40_ENV
634             /* For NT, we not only fork, but re-exec the salvager. Pass in the
635              * commands and pass the child job number via the data path.
636              */
637             pid =
638                 nt_SalvagePartition(jobs[startjob]->partP->name,
639                                     jobs[startjob]->jobnumb);
640             jobs[startjob]->pid = pid;
641             numjobs++;
642 #else
643             pid = Fork();
644             if (pid) {
645                 jobs[startjob]->pid = pid;
646                 numjobs++;
647             } else {
648                 int fd;
649
650                 ShowLog = 0;
651                 for (fd = 0; fd < 16; fd++)
652                     close(fd);
653                 open("/", 0);
654                 dup2(0, 1);
655                 dup2(0, 2);
656 #ifndef AFS_NT40_ENV
657                 if (useSyslog) {
658                     openlog("salvager", LOG_PID, useSyslogFacility);
659                 } else
660 #endif
661                 {
662                     (void)afs_snprintf(logFileName, sizeof logFileName,
663                                        "%s.%d",
664                                        AFSDIR_SERVER_SLVGLOG_FILEPATH,
665                                        jobs[startjob]->jobnumb);
666                     logFile = afs_fopen(logFileName, "w");
667                 }
668                 if (!logFile)
669                     logFile = stdout;
670
671                 SalvageFileSys1(jobs[startjob]->partP, 0);
672                 Exit(0);
673             }
674 #endif
675         }
676     }                           /* while ( thisjob || (!partP && numjobs > 0) ) */
677
678     /* If waited for all jobs to complete, now collect log files and return */
679 #ifndef AFS_NT40_ENV
680     if (!useSyslog)             /* if syslogging - no need to collect */
681 #endif
682         if (!partP) {
683             for (i = 0; i < jobcount; i++) {
684                 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
685                                    AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
686                 if ((passLog = afs_fopen(logFileName, "r"))) {
687                     while (fgets(buf, sizeof(buf), passLog)) {
688                         fputs(buf, logFile);
689                     }
690                     fclose(passLog);
691                 }
692                 (void)unlink(logFileName);
693             }
694             fflush(logFile);
695         }
696     return;
697 }
698
699
700 void
701 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
702 {
703     if (!canfork || debug || Fork() == 0) {
704         SalvageFileSys1(partP, singleVolumeNumber);
705         if (canfork && !debug) {
706             ShowLog = 0;
707             Exit(0);
708         }
709     } else
710         Wait("SalvageFileSys");
711 }
712
713 char *
714 get_DevName(char *pbuffer, char *wpath)
715 {
716     char pbuf[128], *ptr;
717     strcpy(pbuf, pbuffer);
718     ptr = (char *)strrchr(pbuf, '/');
719     if (ptr) {
720         *ptr = '\0';
721         strcpy(wpath, pbuf);
722     } else
723         return NULL;
724     ptr = (char *)strrchr(pbuffer, '/');
725     if (ptr) {
726         strcpy(pbuffer, ptr + 1);
727         return pbuffer;
728     } else
729         return NULL;
730 }
731
732 void
733 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
734 {
735     char *name, *tdir;
736     char inodeListPath[256];
737     FILE *inodeFile = NULL;
738     static char tmpDevName[100];
739     static char wpath[100];
740     struct VolumeSummary *vsp, *esp;
741     int i, j;
742     int code;
743     int tries = 0;
744     struct SalvInfo l_salvinfo;
745     struct SalvInfo *salvinfo = &l_salvinfo;
746
747  retry:
748     memset(salvinfo, 0, sizeof(*salvinfo));
749
750     tries++;
751     if (inodeFile) {
752         fclose(inodeFile);
753         inodeFile = NULL;
754     }
755     if (tries > VOL_MAX_CHECKOUT_RETRIES) {
756         Abort("Raced too many times with fileserver restarts while trying to "
757               "checkout/lock volumes; Aborted\n");
758     }
759 #ifdef AFS_DEMAND_ATTACH_FS
760     if (tries > 1) {
761         /* unlock all previous volume locks, since we're about to lock them
762          * again */
763         VLockFileReinit(&partP->volLockFile);
764     }
765 #endif /* AFS_DEMAND_ATTACH_FS */
766
767     salvinfo->fileSysPartition = partP;
768     salvinfo->fileSysDevice = salvinfo->fileSysPartition->device;
769     salvinfo->fileSysPathName = VPartitionPath(salvinfo->fileSysPartition);
770
771 #ifdef AFS_NT40_ENV
772     /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
773     (void)sprintf(salvinfo->fileSysPath, "%s\\", salvinfo->fileSysPathName);
774     name = partP->devName;
775 #else
776     strlcpy(salvinfo->fileSysPath, salvinfo->fileSysPathName, sizeof(salvinfo->fileSysPath));
777     strcpy(tmpDevName, partP->devName);
778     name = get_DevName(tmpDevName, wpath);
779     salvinfo->fileSysDeviceName = name;
780     salvinfo->filesysfulldev = wpath;
781 #endif
782
783     if (singleVolumeNumber) {
784 #ifndef AFS_DEMAND_ATTACH_FS
785         /* only non-DAFS locks the partition when salvaging a single volume;
786          * DAFS will lock the individual volumes in the VG */
787         VLockPartition(partP->name);
788 #endif /* !AFS_DEMAND_ATTACH_FS */
789
790         ForceSalvage = 1;
791
792         /* salvageserver already setup fssync conn for us */
793         if ((programType != salvageServer) && !VConnectFS()) {
794             Abort("Couldn't connect to file server\n");
795         }
796
797         salvinfo->useFSYNC = 1;
798         AskOffline(salvinfo, singleVolumeNumber);
799 #ifdef AFS_DEMAND_ATTACH_FS
800         if (LockVolume(salvinfo, singleVolumeNumber)) {
801             goto retry;
802         }
803 #endif /* AFS_DEMAND_ATTACH_FS */
804
805     } else {
806         salvinfo->useFSYNC = 0;
807         VLockPartition(partP->name);
808         if (ForceSalvage) {
809             ForceSalvage = 1;
810         } else {
811             ForceSalvage = UseTheForceLuke(salvinfo->fileSysPath);
812         }
813         if (!Showmode)
814             Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
815                 partP->name, name, (Testing ? "(READONLY mode)" : ""));
816         if (ForceSalvage)
817             Log("***Forced salvage of all volumes on this partition***\n");
818     }
819
820
821     /*
822      * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
823      * files
824      */
825     {
826         DIR *dirp;
827         struct dirent *dp;
828
829         osi_Assert((dirp = opendir(salvinfo->fileSysPath)) != NULL);
830         while ((dp = readdir(dirp))) {
831             if (!strncmp(dp->d_name, "salvage.inodes.", 15)
832                 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
833                 char npath[1024];
834                 Log("Removing old salvager temp files %s\n", dp->d_name);
835                 strcpy(npath, salvinfo->fileSysPath);
836                 strcat(npath, "/");
837                 strcat(npath, dp->d_name);
838                 unlink(npath);
839             }
840         }
841         closedir(dirp);
842     }
843     tdir = (tmpdir ? tmpdir : salvinfo->fileSysPath);
844 #ifdef AFS_NT40_ENV
845     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
846     (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
847 #else
848     snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
849              getpid());
850 #endif
851
852     inodeFile = fopen(inodeListPath, "w+b");
853     if (!inodeFile) {
854         Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
855     }
856 #ifdef AFS_NT40_ENV
857     /* Using nt_unlink here since we're really using the delete on close
858      * semantics of unlink. In most places in the salvager, we really do
859      * mean to unlink the file at that point. Those places have been
860      * modified to actually do that so that the NT crt can be used there.
861      */
862     code = nt_unlink(inodeListPath);
863 #else
864     code = unlink(inodeListPath);
865 #endif
866     if (code < 0) {
867         Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
868     }
869
870     if (GetInodeSummary(salvinfo, inodeFile, singleVolumeNumber) < 0) {
871         fclose(inodeFile);
872         return;
873     }
874     salvinfo->inodeFd = fileno(inodeFile);
875     if (salvinfo->inodeFd == -1)
876         Abort("Temporary file %s is missing...\n", inodeListPath);
877     afs_lseek(salvinfo->inodeFd, 0L, SEEK_SET);
878     if (ListInodeOption) {
879         PrintInodeList(salvinfo);
880         return;
881     }
882     /* enumerate volumes in the partition.
883      * figure out sets of read-only + rw volumes.
884      * salvage each set, read-only volumes first, then read-write.
885      * Fix up inodes on last volume in set (whether it is read-write
886      * or read-only).
887      */
888     if (GetVolumeSummary(salvinfo, singleVolumeNumber)) {
889         goto retry;
890     }
891
892     for (i = j = 0, vsp = salvinfo->volumeSummaryp, esp = vsp + salvinfo->nVolumes;
893          i < salvinfo->nVolumesInInodeFile; i = j) {
894         VolumeId rwvid = salvinfo->inodeSummary[i].RWvolumeId;
895         for (j = i;
896              j < salvinfo->nVolumesInInodeFile && salvinfo->inodeSummary[j].RWvolumeId == rwvid;
897              j++) {
898             VolumeId vid = salvinfo->inodeSummary[j].volumeId;
899             struct VolumeSummary *tsp;
900             /* Scan volume list (from partition root directory) looking for the
901              * current rw volume number in the volume list from the inode scan.
902              * If there is one here that is not in the inode volume list,
903              * delete it now. */
904             for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
905                 if (vsp->fileName)
906                     DeleteExtraVolumeHeaderFile(salvinfo, vsp);
907             }
908             /* Now match up the volume summary info from the root directory with the
909              * entry in the volume list obtained from scanning inodes */
910             salvinfo->inodeSummary[j].volSummary = NULL;
911             for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
912                 if (tsp->header.id == vid) {
913                     salvinfo->inodeSummary[j].volSummary = tsp;
914                     tsp->fileName = 0;
915                     break;
916                 }
917             }
918         }
919         /* Salvage the group of volumes (several read-only + 1 read/write)
920          * starting with the current read-only volume we're looking at.
921          */
922         SalvageVolumeGroup(salvinfo, &salvinfo->inodeSummary[i], j - i);
923     }
924
925     /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
926     for (; vsp < esp; vsp++) {
927         if (vsp->fileName)
928             DeleteExtraVolumeHeaderFile(salvinfo, vsp);
929     }
930
931     if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
932         RemoveTheForce(salvinfo->fileSysPath);
933
934     if (!Testing && singleVolumeNumber) {
935 #ifdef AFS_DEMAND_ATTACH_FS
936         /* unlock vol headers so the fs can attach them when we AskOnline */
937         VLockFileReinit(&salvinfo->fileSysPartition->volLockFile);
938 #endif /* AFS_DEMAND_ATTACH_FS */
939
940         AskOnline(salvinfo, singleVolumeNumber);
941
942         /* Step through the volumeSummary list and set all volumes on-line.
943          * The volumes were taken off-line in GetVolumeSummary.
944          */
945         for (j = 0; j < salvinfo->nVolumes; j++) {
946             AskOnline(salvinfo, salvinfo->volumeSummaryp[j].header.id);
947         }
948     } else {
949         if (!Showmode)
950             Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
951                 salvinfo->fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
952     }
953
954     fclose(inodeFile);          /* SalvageVolumeGroup was the last which needed it. */
955 }
956
957 void
958 DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
959 {
960     char path[64];
961     sprintf(path, "%s/%s", salvinfo->fileSysPath, vsp->fileName);
962
963     if (!Showmode)
964         Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
965     if (!Testing) {
966         afs_int32 code;
967         code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, vsp->header.id, vsp->header.parent);
968         if (code) {
969             Log("Error %ld destroying volume disk header for volume %lu\n",
970                 afs_printable_int32_ld(code),
971                 afs_printable_uint32_lu(vsp->header.id));
972         }
973
974         /* make sure we actually delete the fileName file; ENOENT
975          * is fine, since VDestroyVolumeDiskHeader probably already
976          * unlinked it */
977         if (unlink(path) && errno != ENOENT) {
978             Log("Unable to unlink %s (errno = %d)\n", path, errno);
979         }
980     }
981     vsp->fileName = 0;
982 }
983
984 int
985 CompareInodes(const void *_p1, const void *_p2)
986 {
987     const struct ViceInodeInfo *p1 = _p1;
988     const struct ViceInodeInfo *p2 = _p2;
989     if (p1->u.vnode.vnodeNumber == INODESPECIAL
990         || p2->u.vnode.vnodeNumber == INODESPECIAL) {
991         VolumeId p1rwid, p2rwid;
992         p1rwid =
993             (p1->u.vnode.vnodeNumber ==
994              INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
995         p2rwid =
996             (p2->u.vnode.vnodeNumber ==
997              INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
998         if (p1rwid < p2rwid)
999             return -1;
1000         if (p1rwid > p2rwid)
1001             return 1;
1002         if (p1->u.vnode.vnodeNumber == INODESPECIAL
1003             && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1004             if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1005                 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
1006             if (p1->u.vnode.volumeId == p1rwid)
1007                 return -1;
1008             if (p2->u.vnode.volumeId == p2rwid)
1009                 return 1;
1010             return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
1011         }
1012         if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1013             return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
1014         return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
1015     }
1016     if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1017         return -1;
1018     if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1019         return 1;
1020     if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1021         return -1;
1022     if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1023         return 1;
1024     /* The following tests are reversed, so that the most desirable
1025      * of several similar inodes comes first */
1026     if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1027 #ifdef  AFS_3DISPARES
1028         if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
1029             p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1030             return 1;
1031 #endif
1032 #ifdef  AFS_SGI_EXMAG
1033         if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
1034             p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1035             return 1;
1036 #endif
1037         return -1;
1038     }
1039     if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1040 #ifdef  AFS_3DISPARES
1041         if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
1042             p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1043             return -1;
1044 #endif
1045 #ifdef  AFS_SGI_EXMAG
1046         if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
1047             p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1048             return 1;
1049 #endif
1050         return 1;
1051     }
1052     if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1053 #ifdef  AFS_3DISPARES
1054         if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
1055             p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1056             return 1;
1057 #endif
1058 #ifdef  AFS_SGI_EXMAG
1059         if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
1060             p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1061             return 1;
1062 #endif
1063         return -1;
1064     }
1065     if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1066 #ifdef  AFS_3DISPARES
1067         if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
1068             p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1069             return -1;
1070 #endif
1071 #ifdef  AFS_SGI_EXMAG
1072         if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
1073             p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1074             return 1;
1075 #endif
1076         return 1;
1077     }
1078     return 0;
1079 }
1080
1081 void
1082 CountVolumeInodes(struct ViceInodeInfo *ip, int maxInodes,
1083                   struct InodeSummary *summary)
1084 {
1085     VolumeId volume = ip->u.vnode.volumeId;
1086     VolumeId rwvolume = volume;
1087     int n, nSpecial;
1088     Unique maxunique;
1089     n = nSpecial = 0;
1090     maxunique = 0;
1091     while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1092         n++;
1093         if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1094             nSpecial++;
1095             rwvolume = ip->u.special.parentId;
1096             /* This isn't quite right, as there could (in error) be different
1097              * parent inodes in different special vnodes */
1098         } else {
1099             if (maxunique < ip->u.vnode.vnodeUniquifier)
1100                 maxunique = ip->u.vnode.vnodeUniquifier;
1101         }
1102         ip++;
1103     }
1104     summary->volumeId = volume;
1105     summary->RWvolumeId = rwvolume;
1106     summary->nInodes = n;
1107     summary->nSpecialInodes = nSpecial;
1108     summary->maxUniquifier = maxunique;
1109 }
1110
1111 int
1112 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1113 {
1114     if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1115         return (inodeinfo->u.special.parentId == singleVolumeNumber);
1116     return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1117 }
1118
1119 /* GetInodeSummary
1120  *
1121  * Collect list of inodes in file named by path. If a truly fatal error,
1122  * unlink the file and abort. For lessor errors, return -1. The file will
1123  * be unlinked by the caller.
1124  */
1125 int
1126 GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolumeNumber)
1127 {
1128     struct afs_stat status;
1129     int forceSal, err;
1130     int code;
1131     struct ViceInodeInfo *ip, *ip_save;
1132     struct InodeSummary summary;
1133     char summaryFileName[50];
1134     FILE *summaryFile;
1135 #ifdef AFS_NT40_ENV
1136     char *dev = salvinfo->fileSysPath;
1137     char *wpath = salvinfo->fileSysPath;
1138 #else
1139     char *dev = salvinfo->fileSysDeviceName;
1140     char *wpath = salvinfo->filesysfulldev;
1141 #endif
1142     char *part = salvinfo->fileSysPath;
1143     char *tdir;
1144     int i;
1145
1146     /* This file used to come from vfsck; cobble it up ourselves now... */
1147     if ((err =
1148          ListViceInodes(dev, salvinfo->fileSysPath, inodeFile,
1149                         singleVolumeNumber ? OnlyOneVolume : 0,
1150                         singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1151         if (err == -2) {
1152             Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1153             return -1;
1154         }
1155         Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1156     }
1157     if (forceSal && !ForceSalvage) {
1158         Log("***Forced salvage of all volumes on this partition***\n");
1159         ForceSalvage = 1;
1160     }
1161     fseek(inodeFile, 0L, SEEK_SET);
1162     salvinfo->inodeFd = fileno(inodeFile);
1163     if (salvinfo->inodeFd == -1 || afs_fstat(salvinfo->inodeFd, &status) == -1) {
1164         Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1165     }
1166     tdir = (tmpdir ? tmpdir : part);
1167 #ifdef AFS_NT40_ENV
1168     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
1169     (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1170 #else
1171     (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1172                        "%s/salvage.temp.%d", tdir, getpid());
1173 #endif
1174     summaryFile = afs_fopen(summaryFileName, "a+");
1175     if (summaryFile == NULL) {
1176         Abort("Unable to create inode summary file\n");
1177     }
1178
1179 #ifdef AFS_NT40_ENV
1180     /* Using nt_unlink here since we're really using the delete on close
1181      * semantics of unlink. In most places in the salvager, we really do
1182      * mean to unlink the file at that point. Those places have been
1183      * modified to actually do that so that the NT crt can be used there.
1184      */
1185     code = nt_unlink(summaryFileName);
1186 #else
1187     code = unlink(summaryFileName);
1188 #endif
1189     if (code < 0) {
1190         Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1191     }
1192
1193     if (!canfork || debug || Fork() == 0) {
1194         int nInodes;
1195         unsigned long st_size=(unsigned long) status.st_size;
1196         nInodes = st_size / sizeof(struct ViceInodeInfo);
1197         if (nInodes == 0) {
1198             fclose(summaryFile);
1199             if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
1200                 RemoveTheForce(salvinfo->fileSysPath);
1201             else {
1202                 struct VolumeSummary *vsp;
1203                 int i;
1204
1205                 GetVolumeSummary(salvinfo, singleVolumeNumber);
1206
1207                 for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; i++) {
1208                     if (vsp->fileName)
1209                         DeleteExtraVolumeHeaderFile(salvinfo, vsp);
1210                 }
1211             }
1212             Log("%s vice inodes on %s; not salvaged\n",
1213                 singleVolumeNumber ? "No applicable" : "No", dev);
1214             return -1;
1215         }
1216         ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1217         if (ip == NULL) {
1218             fclose(summaryFile);
1219             Abort
1220                 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1221                  dev);
1222         }
1223         if (read(salvinfo->inodeFd, ip, st_size) != st_size) {
1224             fclose(summaryFile);
1225             Abort("Unable to read inode table; %s not salvaged\n", dev);
1226         }
1227         qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1228         if (afs_lseek(salvinfo->inodeFd, 0, SEEK_SET) == -1
1229             || write(salvinfo->inodeFd, ip, st_size) != st_size) {
1230             fclose(summaryFile);
1231             Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1232         }
1233         summary.index = 0;
1234         ip_save = ip;
1235         while (nInodes) {
1236             CountVolumeInodes(ip, nInodes, &summary);
1237             if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1238                 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1239                 fclose(summaryFile);
1240                 return -1;
1241             }
1242             summary.index += (summary.nInodes);
1243             nInodes -= summary.nInodes;
1244             ip += summary.nInodes;
1245         }
1246         free(ip_save);
1247         ip = ip_save = NULL;
1248         /* Following fflush is not fclose, because if it was debug mode would not work */
1249         if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1250             Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1251             fclose(summaryFile);
1252             return -1;
1253         }
1254         if (canfork && !debug) {
1255             ShowLog = 0;
1256             Exit(0);
1257         }
1258     } else {
1259         if (Wait("Inode summary") == -1) {
1260             fclose(summaryFile);
1261             Exit(1);            /* salvage of this partition aborted */
1262         }
1263     }
1264     osi_Assert(afs_fstat(fileno(summaryFile), &status) != -1);
1265     if (status.st_size != 0) {
1266         int ret;
1267         unsigned long st_status=(unsigned long)status.st_size;
1268         salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_status);
1269         osi_Assert(salvinfo->inodeSummary != NULL);
1270         /* For GNU we need to do lseek to get the file pointer moved. */
1271         osi_Assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1272         ret = read(fileno(summaryFile), salvinfo->inodeSummary, st_status);
1273         osi_Assert(ret == st_status);
1274     }
1275     salvinfo->nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1276     for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
1277         salvinfo->inodeSummary[i].volSummary = NULL;
1278     }
1279     Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)(status.st_size));
1280     fclose(summaryFile);
1281     return 0;
1282 }
1283
1284 /* Comparison routine for volume sort.
1285    This is setup so that a read-write volume comes immediately before
1286    any read-only clones of that volume */
1287 int
1288 CompareVolumes(const void *_p1, const void *_p2)
1289 {
1290     const struct VolumeSummary *p1 = _p1;
1291     const struct VolumeSummary *p2 = _p2;
1292     if (p1->header.parent != p2->header.parent)
1293         return p1->header.parent < p2->header.parent ? -1 : 1;
1294     if (p1->header.id == p1->header.parent)     /* p1 is rw volume */
1295         return -1;
1296     if (p2->header.id == p2->header.parent)     /* p2 is rw volume */
1297         return 1;
1298     return p1->header.id < p2->header.id ? -1 : 1;      /* Both read-only */
1299 }
1300
1301 /**
1302  * Gleans volumeSummary information by asking the fileserver
1303  *
1304  * @param[in] singleVolumeNumber  the volume we're salvaging. 0 if we're
1305  *                                salvaging a whole partition
1306  *
1307  * @return whether we obtained the volume summary information or not
1308  *  @retval 0  success; we obtained the volume summary information
1309  *  @retval -1 we raced with a fileserver restart; volume locks and checkout
1310  *             must be retried
1311  *  @retval 1  we did not get the volume summary information; either the
1312  *             fileserver responded with an error, or we are not supposed to
1313  *             ask the fileserver for the information (e.g. we are salvaging
1314  *             the entire partition or we are not the salvageserver)
1315  *
1316  * @note for non-DAFS, always returns 1
1317  */
1318 static int
1319 AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1320 {
1321     afs_int32 code = 1;
1322 #if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
1323     if (programType == salvageServer) {
1324         if (singleVolumeNumber) {
1325             FSSYNC_VGQry_response_t q_res;
1326             SYNC_response res;
1327             struct VolumeSummary *vsp;
1328             int i;
1329             struct VolumeDiskHeader diskHdr;
1330
1331             memset(&res, 0, sizeof(res));
1332
1333             code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1334
1335             /*
1336              * We must wait for the partition to finish scanning before
1337              * can continue, since we will not know if we got the entire
1338              * VG membership unless the partition is fully scanned.
1339              * We could, in theory, just scan the partition ourselves if
1340              * the VG cache is not ready, but we would be doing the exact
1341              * same scan the fileserver is doing; it will almost always
1342              * be faster to wait for the fileserver. The only exceptions
1343              * are if the partition does not take very long to scan, and
1344              * in that case it's fast either way, so who cares?
1345              */
1346             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1347                 Log("waiting for fileserver to finish scanning partition %s...\n",
1348                     salvinfo->fileSysPartition->name);
1349
1350                 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1351                     /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1352                      * just so small partitions don't need to wait over 10
1353                      * seconds every time, and large partitions are generally
1354                      * polled only once every ten seconds. */
1355                     sleep((i > 10) ? (i = 10) : i);
1356
1357                     code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1358                 }
1359             }
1360
1361             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1362                 /* This can happen if there's no header for the volume
1363                  * we're salvaging, or no headers exist for the VG (if
1364                  * we're salvaging an RW). Act as if we got a response
1365                  * with no VG members. The headers may be created during
1366                  * salvaging, if there are inodes in this VG. */
1367                 code = 0;
1368                 memset(&q_res, 0, sizeof(q_res));
1369                 q_res.rw = singleVolumeNumber;
1370             }
1371
1372             if (code) {
1373                 Log("fileserver refused VGCQuery request for volume %lu on "
1374                     "partition %s, code %ld reason %ld\n",
1375                     afs_printable_uint32_lu(singleVolumeNumber),
1376                     salvinfo->fileSysPartition->name,
1377                     afs_printable_int32_ld(code),
1378                     afs_printable_int32_ld(res.hdr.reason));
1379                 goto done;
1380             }
1381
1382             if (q_res.rw != singleVolumeNumber) {
1383                 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1384                     afs_printable_uint32_lu(singleVolumeNumber),
1385                     afs_printable_uint32_lu(q_res.rw));
1386 #ifdef SALVSYNC_BUILD_CLIENT
1387                 if (SALVSYNC_LinkVolume(q_res.rw,
1388                                        singleVolumeNumber,
1389                                        salvinfo->fileSysPartition->name,
1390                                        NULL) != SYNC_OK) {
1391                     Log("schedule request failed\n");
1392                 }
1393 #endif /* SALVSYNC_BUILD_CLIENT */
1394                 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1395             }
1396
1397             salvinfo->volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
1398             osi_Assert(salvinfo->volumeSummaryp != NULL);
1399
1400             salvinfo->nVolumes = 0;
1401             vsp = salvinfo->volumeSummaryp;
1402
1403             for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1404                 char name[VMAXPATHLEN];
1405
1406                 if (!q_res.children[i]) {
1407                     continue;
1408                 }
1409
1410                 /* AskOffline for singleVolumeNumber was called much earlier */
1411                 if (q_res.children[i] != singleVolumeNumber) {
1412                     AskOffline(salvinfo, q_res.children[i]);
1413                     if (LockVolume(salvinfo, q_res.children[i])) {
1414                         /* need to retry */
1415                         return -1;
1416                     }
1417                 }
1418
1419                 code = VReadVolumeDiskHeader(q_res.children[i], salvinfo->fileSysPartition, &diskHdr);
1420                 if (code) {
1421                     Log("Cannot read header for %lu; trying to salvage group anyway\n",
1422                         afs_printable_uint32_lu(q_res.children[i]));
1423                     code = 0;
1424                     continue;
1425                 }
1426
1427                 DiskToVolumeHeader(&vsp->header, &diskHdr);
1428                 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1429                 vsp->fileName = ToString(name);
1430                 salvinfo->nVolumes++;
1431                 vsp++;
1432             }
1433
1434             qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1435                   CompareVolumes);
1436         }
1437       done:
1438         if (code) {
1439             Log("Cannot get volume summary from fileserver; falling back to scanning "
1440                 "entire partition\n");
1441         }
1442     }
1443 #endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
1444     return code;
1445 }
1446
1447 /**
1448  * count how many volume headers are found by VWalkVolumeHeaders.
1449  *
1450  * @param[in] dp   the disk partition (unused)
1451  * @param[in] name full path to the .vol header (unused)
1452  * @param[in] hdr  the header data (unused)
1453  * @param[in] last whether this is the last try or not (unused)
1454  * @param[in] rock actually an afs_int32*; the running count of how many
1455  *                 volumes we have found
1456  *
1457  * @retval 0 always
1458  */
1459 static int
1460 CountHeader(struct DiskPartition64 *dp, const char *name,
1461             struct VolumeDiskHeader *hdr, int last, void *rock)
1462 {
1463     afs_int32 *nvols = (afs_int32 *)rock;
1464     (*nvols)++;
1465     return 0;
1466 }
1467
1468 /**
1469  * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1470  * data.
1471  */
1472 struct SalvageScanParams {
1473     VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1474                                   * vol id of the VG we're salvaging */
1475     struct VolumeSummary *vsp;   /**< ptr to the current volume summary object
1476                                   * we're filling in */
1477     afs_int32 nVolumes;          /**< # of vols we've encountered */
1478     afs_int32 totalVolumes;      /**< max # of vols we should encounter (the
1479                                   * # of vols we've alloc'd memory for) */
1480     int retry;  /**< do we need to retry vol lock/checkout? */
1481     struct SalvInfo *salvinfo; /**< salvage job info */
1482 };
1483
1484 /**
1485  * records volume summary info found from VWalkVolumeHeaders.
1486  *
1487  * Found volumes are also taken offline if they are in the specific volume
1488  * group we are looking for.
1489  *
1490  * @param[in] dp   the disk partition
1491  * @param[in] name full path to the .vol header
1492  * @param[in] hdr  the header data
1493  * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1494  * @param[in] rock actually a struct SalvageScanParams*, containing the
1495  *                 information needed to record the volume summary data
1496  *
1497  * @return operation status
1498  *  @retval 0  success
1499  *  @retval -1 volume locking raced with fileserver restart; checking out
1500  *             and locking volumes needs to be retried
1501  *  @retval 1  volume header is mis-named and should be deleted
1502  */
1503 static int
1504 RecordHeader(struct DiskPartition64 *dp, const char *name,
1505              struct VolumeDiskHeader *hdr, int last, void *rock)
1506 {
1507     char nameShouldBe[64];
1508     struct SalvageScanParams *params;
1509     struct VolumeSummary summary;
1510     VolumeId singleVolumeNumber;
1511     struct SalvInfo *salvinfo;
1512
1513     params = (struct SalvageScanParams *)rock;
1514
1515     singleVolumeNumber = params->singleVolumeNumber;
1516     salvinfo = params->salvinfo;
1517
1518     DiskToVolumeHeader(&summary.header, hdr);
1519
1520     if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1521         && summary.header.parent != singleVolumeNumber) {
1522
1523         if (programType == salvageServer) {
1524 #ifdef SALVSYNC_BUILD_CLIENT
1525             Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1526                 summary.header.id, summary.header.parent);
1527             if (SALVSYNC_LinkVolume(summary.header.parent,
1528                                     summary.header.id,
1529                                     dp->name,
1530                                     NULL) != SYNC_OK) {
1531                 Log("schedule request failed\n");
1532             }
1533 #endif
1534             Exit(SALSRV_EXIT_VOLGROUP_LINK);
1535
1536         } else {
1537             Log("%u is a read-only volume; not salvaged\n",
1538                 singleVolumeNumber);
1539             Exit(1);
1540         }
1541     }
1542
1543     if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1544         || summary.header.parent == singleVolumeNumber) {
1545
1546         /* check if the header file is incorrectly named */
1547         int badname = 0;
1548         const char *base = strrchr(name, '/');
1549         if (base) {
1550             base++;
1551         } else {
1552             base = name;
1553         }
1554
1555         (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1556                            VFORMAT, afs_printable_uint32_lu(summary.header.id));
1557
1558
1559         if (strcmp(nameShouldBe, base)) {
1560             /* .vol file has wrong name; retry/delete */
1561             badname = 1;
1562         }
1563
1564         if (!badname || last) {
1565             /* only offline the volume if the header is good, or if this is
1566              * the last try looking at it; avoid AskOffline'ing the same vol
1567              * multiple times */
1568
1569             if (singleVolumeNumber
1570                 && summary.header.id != singleVolumeNumber) {
1571                 /* don't offline singleVolumeNumber; we already did that
1572                  * earlier */
1573
1574                 AskOffline(salvinfo, summary.header.id);
1575
1576 #ifdef AFS_DEMAND_ATTACH_FS
1577                 if (!badname) {
1578                     /* don't lock the volume if the header is bad, since we're
1579                      * about to delete it anyway. */
1580                     if (LockVolume(salvinfo, summary.header.id)) {
1581                         params->retry = 1;
1582                         return -1;
1583                     }
1584                 }
1585 #endif /* AFS_DEMAND_ATTACH_FS */
1586             }
1587         }
1588         if (badname) {
1589             if (last && !Showmode) {
1590                 Log("Volume header file %s is incorrectly named (should be %s "
1591                     "not %s); %sdeleted (it will be recreated later, if "
1592                     "necessary)\n", name, nameShouldBe, base,
1593                     (Testing ? "it would have been " : ""));
1594             }
1595             return 1;
1596         }
1597
1598         summary.fileName = ToString(base);
1599         params->nVolumes++;
1600
1601         if (params->nVolumes > params->totalVolumes) {
1602             /* We found more volumes than we found on the first partition walk;
1603              * apparently something created a volume while we were
1604              * partition-salvaging, or we found more than 20 vols when salvaging a
1605              * particular volume. Abort if we detect this, since other programs
1606              * supposed to not touch the partition while it is partition-salvaging,
1607              * and we shouldn't find more than 20 vols in a VG.
1608              */
1609             Abort("Found %ld vol headers, but should have found at most %ld! "
1610                   "Make sure the volserver/fileserver are not running at the "
1611                   "same time as a partition salvage\n",
1612                   afs_printable_int32_ld(params->nVolumes),
1613                   afs_printable_int32_ld(params->totalVolumes));
1614         }
1615
1616         memcpy(params->vsp, &summary, sizeof(summary));
1617         params->vsp++;
1618     }
1619
1620     return 0;
1621 }
1622
1623 /**
1624  * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1625  *
1626  * If the header could not be read in at all, the header is always unlinked.
1627  * If instead RecordHeader said the header was bad (that is, the header file
1628  * is mis-named), we only unlink if we are doing a partition salvage, as
1629  * opposed to salvaging a specific volume group.
1630  *
1631  * @param[in] dp   the disk partition
1632  * @param[in] name full path to the .vol header
1633  * @param[in] hdr  header data, or NULL if the header could not be read
1634  * @param[in] rock actually a struct SalvageScanParams*, with some information
1635  *                 about the scan
1636  */
1637 static void
1638 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1639              struct VolumeDiskHeader *hdr, void *rock)
1640 {
1641     struct SalvageScanParams *params;
1642     int dounlink = 0;
1643
1644     params = (struct SalvageScanParams *)rock;
1645
1646     if (!hdr) {
1647         /* no header; header is too bogus to read in at all */
1648         if (!Showmode) {
1649             Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1650         }
1651         if (!Testing) {
1652             dounlink = 1;
1653         }
1654
1655     } else if (!params->singleVolumeNumber) {
1656         /* We were able to read in a header, but RecordHeader said something
1657          * was wrong with it. We only unlink those if we are doing a partition
1658          * salvage. */
1659         if (!Testing) {
1660             dounlink = 1;
1661         }
1662     }
1663
1664     if (dounlink && unlink(name)) {
1665         Log("Error %d while trying to unlink %s\n", errno, name);
1666     }
1667 }
1668
1669 /**
1670  * Populates salvinfo->volumeSummaryp with volume summary information, either by asking
1671  * the fileserver for VG information, or by scanning the /vicepX partition.
1672  *
1673  * @param[in] singleVolumeNumber  the volume ID of the single volume group we
1674  *                                are salvaging, or 0 if this is a partition
1675  *                                salvage
1676  *
1677  * @return operation status
1678  *  @retval 0  success
1679  *  @retval -1 we raced with a fileserver restart; checking out and locking
1680  *             volumes must be retried
1681  */
1682 int
1683 GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1684 {
1685     afs_int32 nvols = 0;
1686     struct SalvageScanParams params;
1687     int code;
1688
1689     code = AskVolumeSummary(salvinfo, singleVolumeNumber);
1690     if (code == 0) {
1691         /* we successfully got the vol information from the fileserver; no
1692          * need to scan the partition */
1693         return 0;
1694     }
1695     if (code < 0) {
1696         /* we need to retry volume checkout */
1697         return code;
1698     }
1699
1700     if (!singleVolumeNumber) {
1701         /* Count how many volumes we have in /vicepX */
1702         code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, CountHeader,
1703                                   NULL, &nvols);
1704         if (code < 0) {
1705             Abort("Can't read directory %s; not salvaged\n", salvinfo->fileSysPath);
1706         }
1707         if (!nvols)
1708             nvols = 1;
1709     } else {
1710         nvols = VOL_VG_MAX_VOLS;
1711     }
1712
1713     salvinfo->volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
1714     osi_Assert(salvinfo->volumeSummaryp != NULL);
1715
1716     params.singleVolumeNumber = singleVolumeNumber;
1717     params.vsp = salvinfo->volumeSummaryp;
1718     params.nVolumes = 0;
1719     params.totalVolumes = nvols;
1720     params.retry = 0;
1721     params.salvinfo = salvinfo;
1722
1723     /* walk the partition directory of volume headers and record the info
1724      * about them; unlinking invalid headers */
1725     code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, RecordHeader,
1726                               UnlinkHeader, &params);
1727     if (params.retry) {
1728         /* we apparently need to retry checking-out/locking volumes */
1729         return -1;
1730     }
1731     if (code < 0) {
1732         Abort("Failed to get volume header summary\n");
1733     }
1734     salvinfo->nVolumes = params.nVolumes;
1735
1736     qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1737           CompareVolumes);
1738
1739     return 0;
1740 }
1741
1742 /* Find the link table. This should be associated with the RW volume or, if
1743  * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1744  */
1745 Inode
1746 FindLinkHandle(struct InodeSummary *isp, int nVols,
1747                struct ViceInodeInfo *allInodes)
1748 {
1749     int i, j;
1750     struct ViceInodeInfo *ip;
1751
1752     for (i = 0; i < nVols; i++) {
1753         ip = allInodes + isp[i].index;
1754         for (j = 0; j < isp[i].nSpecialInodes; j++) {
1755             if (ip[j].u.special.type == VI_LINKTABLE)
1756                 return ip[j].inodeNumber;
1757         }
1758     }
1759     return (Inode) - 1;
1760 }
1761
1762 int
1763 CreateLinkTable(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
1764 {
1765     struct versionStamp version;
1766     FdHandle_t *fdP;
1767
1768     if (!VALID_INO(ino))
1769         ino =
1770             IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
1771                       INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1772     if (!VALID_INO(ino))
1773         Abort
1774             ("Unable to allocate link table inode for volume %u (error = %d)\n",
1775              isp->RWvolumeId, errno);
1776     IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
1777     fdP = IH_OPEN(salvinfo->VGLinkH);
1778     if (fdP == NULL)
1779         Abort("Can't open link table for volume %u (error = %d)\n",
1780               isp->RWvolumeId, errno);
1781
1782     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1783         Abort("Can't truncate link table for volume %u (error = %d)\n",
1784               isp->RWvolumeId, errno);
1785
1786     version.magic = LINKTABLEMAGIC;
1787     version.version = LINKTABLEVERSION;
1788
1789     if (FDH_PWRITE(fdP, (char *)&version, sizeof(version), 0)
1790         != sizeof(version))
1791         Abort("Can't truncate link table for volume %u (error = %d)\n",
1792               isp->RWvolumeId, errno);
1793
1794     FDH_REALLYCLOSE(fdP);
1795
1796     /* If the volume summary exits (i.e.,  the V*.vol header file exists),
1797      * then set this inode there as well.
1798      */
1799     if (isp->volSummary)
1800         isp->volSummary->header.linkTable = ino;
1801
1802     return 0;
1803 }
1804
1805 #ifdef AFS_NT40_ENV
1806 void *
1807 nt_SVG(void *arg)
1808 {
1809     SVGParms_t *parms = (SVGParms_t *) arg;
1810     DoSalvageVolumeGroup(parms->svgp_salvinfo, parms->svgp_inodeSummaryp, parms->svgp_count);
1811     return NULL;
1812 }
1813
1814 void
1815 SalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
1816 {
1817     pthread_t tid;
1818     pthread_attr_t tattr;
1819     int code;
1820     SVGParms_t parms;
1821
1822     /* Initialize per volume global variables, even if later code does so */
1823     salvinfo->VolumeChanged = 0;
1824     salvinfo->VGLinkH = NULL;
1825     salvinfo->VGLinkH_cnt = 0;
1826     memset(&salvinfo->VolInfo, 0, sizeof(salvinfo->VolInfo));
1827
1828     parms.svgp_inodeSummaryp = isp;
1829     parms.svgp_count = nVols;
1830     parms.svgp_salvinfo = salvinfo;
1831     code = pthread_attr_init(&tattr);
1832     if (code) {
1833         Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1834             isp->RWvolumeId);
1835         return;
1836     }
1837     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1838     if (code) {
1839         Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1840         return;
1841     }
1842     code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1843     if (code) {
1844         Log("Failed to create thread to salvage volume group %u\n",
1845             isp->RWvolumeId);
1846         return;
1847     }
1848     (void)pthread_join(tid, NULL);
1849 }
1850 #endif /* AFS_NT40_ENV */
1851
1852 void
1853 DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
1854 {
1855     struct ViceInodeInfo *inodes, *allInodes, *ip;
1856     int i, totalInodes, size, salvageTo;
1857     int haveRWvolume;
1858     int check;
1859     Inode ino;
1860     int dec_VGLinkH = 0;
1861     int VGLinkH_p1 =0;
1862     FdHandle_t *fdP = NULL;
1863
1864     salvinfo->VGLinkH_cnt = 0;
1865     haveRWvolume = (isp->volumeId == isp->RWvolumeId
1866                     && isp->nSpecialInodes > 0);
1867     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1868         if (!ForceSalvage && QuickCheck(salvinfo, isp, nVols))
1869             return;
1870     }
1871     if (ShowMounts && !haveRWvolume)
1872         return;
1873     if (canfork && !debug && Fork() != 0) {
1874         (void)Wait("Salvage volume group");
1875         return;
1876     }
1877     for (i = 0, totalInodes = 0; i < nVols; i++)
1878         totalInodes += isp[i].nInodes;
1879     size = totalInodes * sizeof(struct ViceInodeInfo);
1880     inodes = (struct ViceInodeInfo *)malloc(size);
1881     allInodes = inodes - isp->index;    /* this would the base of all the inodes
1882                                          * for the partition, if all the inodes
1883                                          * had been read into memory */
1884     osi_Assert(afs_lseek
1885            (salvinfo->inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1886             SEEK_SET) != -1);
1887     osi_Assert(read(salvinfo->inodeFd, inodes, size) == size);
1888
1889     /* Don't try to salvage a read write volume if there isn't one on this
1890      * partition */
1891     salvageTo = haveRWvolume ? 0 : 1;
1892
1893 #ifdef AFS_NAMEI_ENV
1894     ino = FindLinkHandle(isp, nVols, allInodes);
1895     if (VALID_INO(ino)) {
1896         IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
1897         fdP = IH_OPEN(salvinfo->VGLinkH);
1898     }
1899     if (!VALID_INO(ino) || fdP == NULL) {
1900         Log("%s link table for volume %u.\n",
1901             Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1902         if (Testing) {
1903             IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
1904         } else {
1905             int i, j;
1906             struct ViceInodeInfo *ip;
1907             CreateLinkTable(salvinfo, isp, ino);
1908             fdP = IH_OPEN(salvinfo->VGLinkH);
1909             /* Sync fake 1 link counts to the link table, now that it exists */
1910             if (fdP) {
1911                 for (i = 0; i < nVols; i++) {
1912                         ip = allInodes + isp[i].index;
1913                          for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1914                                  namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1915                     }
1916                 }
1917             }
1918         }
1919     }
1920     if (fdP)
1921         FDH_REALLYCLOSE(fdP);
1922 #else
1923     IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
1924 #endif
1925
1926     /* Salvage in reverse order--read/write volume last; this way any
1927      * Inodes not referenced by the time we salvage the read/write volume
1928      * can be picked up by the read/write volume */
1929     /* ACTUALLY, that's not done right now--the inodes just vanish */
1930     for (i = nVols - 1; i >= salvageTo; i--) {
1931         int rw = (i == 0);
1932         struct InodeSummary *lisp = &isp[i];
1933 #ifdef AFS_NAMEI_ENV
1934         /* If only the RO is present on this partition, the link table
1935          * shows up as a RW volume special file. Need to make sure the
1936          * salvager doesn't try to salvage the non-existent RW.
1937          */
1938         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1939             /* If this only special inode is the link table, continue */
1940             if (inodes->u.special.type == VI_LINKTABLE) {
1941                 haveRWvolume = 0;
1942                 continue;
1943             }
1944         }
1945 #endif
1946         if (!Showmode)
1947             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1948                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1949         /* Check inodes twice.  The second time do things seriously.  This
1950          * way the whole RO volume can be deleted, below, if anything goes wrong */
1951         for (check = 1; check >= 0; check--) {
1952             int deleteMe;
1953             if (SalvageVolumeHeaderFile(salvinfo, lisp, allInodes, rw, check, &deleteMe)
1954                 == -1) {
1955                 MaybeZapVolume(salvinfo, lisp, "Volume header", deleteMe, check);
1956                 if (rw && deleteMe) {
1957                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
1958                                          * volume won't be called */
1959                     break;
1960                 }
1961                 if (!rw)
1962                     break;
1963             }
1964             if (rw && check == 1)
1965                 continue;
1966             if (SalvageVnodes(salvinfo, isp, lisp, allInodes, check) == -1) {
1967                 MaybeZapVolume(salvinfo, lisp, "Vnode index", 0, check);
1968                 break;
1969             }
1970         }
1971     }
1972
1973     /* Fix actual inode counts */
1974     if (!Showmode) {
1975         afs_ino_str_t stmp;
1976         Log("totalInodes %d\n",totalInodes);
1977         for (ip = inodes; totalInodes; ip++, totalInodes--) {
1978             static int TraceBadLinkCounts = 0;
1979 #ifdef AFS_NAMEI_ENV
1980             if (salvinfo->VGLinkH->ih_ino == ip->inodeNumber) {
1981                 dec_VGLinkH = ip->linkCount - salvinfo->VGLinkH_cnt;
1982                 VGLinkH_p1 = ip->u.param[0];
1983                 continue;       /* Deal with this last. */
1984             }
1985 #endif
1986             if (ip->linkCount != 0 && TraceBadLinkCounts) {
1987                 TraceBadLinkCounts--;   /* Limit reports, per volume */
1988                 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]);
1989             }
1990             while (ip->linkCount > 0) {
1991                 /* below used to assert, not break */
1992                 if (!Testing) {
1993                     if (IH_DEC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1994                         Log("idec failed. inode %s errno %d\n",
1995                             PrintInode(stmp, ip->inodeNumber), errno);
1996                         break;
1997                     }
1998                 }
1999                 ip->linkCount--;
2000             }
2001             while (ip->linkCount < 0) {
2002                 /* these used to be asserts */
2003                 if (!Testing) {
2004                     if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2005                         Log("iinc failed. inode %s errno %d\n",
2006                             PrintInode(stmp, ip->inodeNumber), errno);
2007                         break;
2008                     }
2009                 }
2010                 ip->linkCount++;
2011             }
2012         }
2013 #ifdef AFS_NAMEI_ENV
2014         while (dec_VGLinkH > 0) {
2015             if (IH_DEC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2016                 Log("idec failed on link table, errno = %d\n", errno);
2017             }
2018             dec_VGLinkH--;
2019         }
2020         while (dec_VGLinkH < 0) {
2021             if (IH_INC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2022                 Log("iinc failed on link table, errno = %d\n", errno);
2023             }
2024             dec_VGLinkH++;
2025         }
2026 #endif
2027     }
2028     free(inodes);
2029     /* Directory consistency checks on the rw volume */
2030     if (haveRWvolume)
2031         SalvageVolume(salvinfo, isp, salvinfo->VGLinkH);
2032     IH_RELEASE(salvinfo->VGLinkH);
2033
2034     if (canfork && !debug) {
2035         ShowLog = 0;
2036         Exit(0);
2037     }
2038 }
2039
2040 int
2041 QuickCheck(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
2042 {
2043     /* Check headers BEFORE forking */
2044     int i;
2045     IHandle_t *h;
2046
2047     for (i = 0; i < nVols; i++) {
2048         struct VolumeSummary *vs = isp[i].volSummary;
2049         VolumeDiskData volHeader;
2050         if (!vs) {
2051             /* Don't salvage just because phantom rw volume is there... */
2052             /* (If a read-only volume exists, read/write inodes must also exist) */
2053             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2054                 continue;
2055             return 0;
2056         }
2057         IH_INIT(h, salvinfo->fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2058         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2059             == sizeof(volHeader)
2060             && volHeader.stamp.magic == VOLUMEINFOMAGIC
2061             && volHeader.dontSalvage == DONT_SALVAGE
2062             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2063             if (volHeader.inUse != 0) {
2064                 volHeader.inUse = 0;
2065                 volHeader.inService = 1;
2066                 if (!Testing) {
2067                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2068                         != sizeof(volHeader)) {
2069                         IH_RELEASE(h);
2070                         return 0;
2071                     }
2072                 }
2073             }
2074             IH_RELEASE(h);
2075         } else {
2076             IH_RELEASE(h);
2077             return 0;
2078         }
2079     }
2080     return 1;
2081 }
2082
2083
2084 /* SalvageVolumeHeaderFile
2085  *
2086  * Salvage the top level V*.vol header file. Make sure the special files
2087  * exist and that there are no duplicates.
2088  *
2089  * Calls SalvageHeader for each possible type of volume special file.
2090  */
2091
2092 int
2093 SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
2094                         struct ViceInodeInfo *inodes, int RW,
2095                         int check, int *deleteMe)
2096 {
2097     int i;
2098     struct ViceInodeInfo *ip;
2099     int allinodesobsolete = 1;
2100     struct VolumeDiskHeader diskHeader;
2101     afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
2102     int *skip;
2103     struct VolumeHeader tempHeader;
2104     struct afs_inode_info stuff[MAXINODETYPE];
2105
2106     /* keeps track of special inodes that are probably 'good'; they are
2107      * referenced in the vol header, and are included in the given inodes
2108      * array */
2109     struct {
2110         int valid;
2111         Inode inode;
2112     } goodspecial[MAXINODETYPE];
2113
2114     if (deleteMe)
2115         *deleteMe = 0;
2116
2117     memset(goodspecial, 0, sizeof(goodspecial));
2118
2119     skip = malloc(isp->nSpecialInodes * sizeof(*skip));
2120     if (skip) {
2121         memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
2122     } else {
2123         Log("cannot allocate memory for inode skip array when salvaging "
2124             "volume %lu; not performing duplicate special inode recovery\n",
2125             afs_printable_uint32_lu(isp->volumeId));
2126         /* still try to perform the salvage; the skip array only does anything
2127          * if we detect duplicate special inodes */
2128     }
2129
2130     init_inode_info(&tempHeader, stuff);
2131
2132     /*
2133      * First, look at the special inodes and see if any are referenced by
2134      * the existing volume header. If we find duplicate special inodes, we
2135      * can use this information to use the referenced inode (it's more
2136      * likely to be the 'good' one), and throw away the duplicates.
2137      */
2138     if (isp->volSummary && skip) {
2139         /* use tempHeader, so we can use the stuff[] array to easily index
2140          * into the isp->volSummary special inodes */
2141         memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2142
2143         for (i = 0; i < isp->nSpecialInodes; i++) {
2144             ip = &inodes[isp->index + i];
2145             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2146                 /* will get taken care of in a later loop */
2147                 continue;
2148             }
2149             if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2150                 goodspecial[ip->u.special.type-1].valid = 1;
2151                 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2152             }
2153         }
2154     }
2155
2156     memset(&tempHeader, 0, sizeof(tempHeader));
2157     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2158     tempHeader.stamp.version = VOLUMEHEADERVERSION;
2159     tempHeader.id = isp->volumeId;
2160     tempHeader.parent = isp->RWvolumeId;
2161
2162     /* Check for duplicates (inodes are sorted by type field) */
2163     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2164         ip = &inodes[isp->index + i];
2165         if (ip->u.special.type == (ip + 1)->u.special.type) {
2166             afs_ino_str_t stmp1, stmp2;
2167
2168             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2169                 /* Will be caught in the loop below */
2170                 continue;
2171             }
2172             if (!Showmode) {
2173                 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2174                     ip->u.special.type, isp->volumeId,
2175                     PrintInode(stmp1, ip->inodeNumber),
2176                     PrintInode(stmp2, (ip+1)->inodeNumber));
2177             }
2178             if (skip && goodspecial[ip->u.special.type-1].valid) {
2179                 Inode gi = goodspecial[ip->u.special.type-1].inode;
2180
2181                 if (!Showmode) {
2182                     Log("using special inode referenced by vol header (%s)\n",
2183                         PrintInode(stmp1, gi));
2184                 }
2185
2186                 /* the volume header references some special inode of
2187                  * this type in the inodes array; are we it? */
2188                 if (ip->inodeNumber != gi) {
2189                     skip[i] = 1;
2190                 } else if ((ip+1)->inodeNumber != gi) {
2191                     /* in case this is the last iteration; we need to
2192                      * make sure we check ip+1, too */
2193                     skip[i+1] = 1;
2194                 }
2195             } else {
2196                 if (!Showmode)
2197                     Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2198                 if (skip) {
2199                     free(skip);
2200                 }
2201                 return -1;
2202             }
2203         }
2204     }
2205     for (i = 0; i < isp->nSpecialInodes; i++) {
2206         afs_ino_str_t stmp;
2207         ip = &inodes[isp->index + i];
2208         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2209             if (check) {
2210                 Log("Rubbish header inode %s of type %d\n",
2211                     PrintInode(stmp, ip->inodeNumber),
2212                     ip->u.special.type);
2213                 if (skip) {
2214                     free(skip);
2215                 }
2216                 return -1;
2217             }
2218             Log("Rubbish header inode %s of type %d; deleted\n",
2219                 PrintInode(stmp, ip->inodeNumber),
2220                 ip->u.special.type);
2221         } else if (!stuff[ip->u.special.type - 1].obsolete) {
2222             if (skip && skip[i]) {
2223                 if (orphans == ORPH_REMOVE) {
2224                     Log("Removing orphan special inode %s of type %d\n",
2225                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2226                     continue;
2227                 } else {
2228                     Log("Ignoring orphan special inode %s of type %d\n",
2229                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2230                     /* fall through to the ip->linkCount--; line below */
2231                 }
2232             } else {
2233                 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2234                 allinodesobsolete = 0;
2235             }
2236             if (!check && ip->u.special.type != VI_LINKTABLE)
2237                 ip->linkCount--;        /* Keep the inode around */
2238         }
2239     }
2240     if (skip) {
2241         free(skip);
2242     }
2243     skip = NULL;
2244
2245     if (allinodesobsolete) {
2246         if (deleteMe)
2247             *deleteMe = 1;
2248         return -1;
2249     }
2250
2251     if (!check)
2252         salvinfo->VGLinkH_cnt++;                /* one for every header. */
2253
2254     if (!RW && !check && isp->volSummary) {
2255         ClearROInUseBit(isp->volSummary);
2256         return 0;
2257     }
2258
2259     for (i = 0; i < MAXINODETYPE; i++) {
2260         if (stuff[i].inodeType == VI_LINKTABLE) {
2261             /* Gross hack: SalvageHeader does a bcmp on the volume header.
2262              * And we may have recreated the link table earlier, so set the
2263              * RW header as well.
2264              */
2265             if (VALID_INO(salvinfo->VGLinkH->ih_ino)) {
2266                 *stuff[i].inode = salvinfo->VGLinkH->ih_ino;
2267             }
2268             continue;
2269         }
2270         if (SalvageHeader(salvinfo, &stuff[i], isp, check, deleteMe) == -1 && check)
2271             return -1;
2272     }
2273
2274     if (isp->volSummary == NULL) {
2275         char path[64];
2276         char headerName[64];
2277         (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2278         (void)afs_snprintf(path, sizeof path, "%s/%s", salvinfo->fileSysPath, headerName);
2279         if (check) {
2280             Log("No header file for volume %u\n", isp->volumeId);
2281             return -1;
2282         }
2283         if (!Showmode)
2284             Log("No header file for volume %u; %screating %s\n",
2285                 isp->volumeId, (Testing ? "it would have been " : ""),
2286                 path);
2287         isp->volSummary = (struct VolumeSummary *)
2288             malloc(sizeof(struct VolumeSummary));
2289         isp->volSummary->fileName = ToString(headerName);
2290
2291         writefunc = VCreateVolumeDiskHeader;
2292     } else {
2293         char path[64];
2294         char headerName[64];
2295         /* hack: these two fields are obsolete... */
2296         isp->volSummary->header.volumeAcl = 0;
2297         isp->volSummary->header.volumeMountTable = 0;
2298
2299         if (memcmp
2300             (&isp->volSummary->header, &tempHeader,
2301              sizeof(struct VolumeHeader))) {
2302             /* We often remove the name before calling us, so we make a fake one up */
2303             if (isp->volSummary->fileName) {
2304                 strcpy(headerName, isp->volSummary->fileName);
2305             } else {
2306                 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2307                 isp->volSummary->fileName = ToString(headerName);
2308             }
2309             (void)afs_snprintf(path, sizeof path, "%s/%s", salvinfo->fileSysPath, headerName);
2310
2311             Log("Header file %s is damaged or no longer valid%s\n", path,
2312                 (check ? "" : "; repairing"));
2313             if (check)
2314                 return -1;
2315
2316             writefunc = VWriteVolumeDiskHeader;
2317         }
2318     }
2319     if (writefunc) {
2320         memcpy(&isp->volSummary->header, &tempHeader,
2321                sizeof(struct VolumeHeader));
2322         if (Testing) {
2323             if (!Showmode)
2324                 Log("It would have written a new header file for volume %u\n",
2325                     isp->volumeId);
2326         } else {
2327             afs_int32 code;
2328             VolumeHeaderToDisk(&diskHeader, &tempHeader);
2329             code = (*writefunc)(&diskHeader, salvinfo->fileSysPartition);
2330             if (code) {
2331                 Log("Error %ld writing volume header file for volume %lu\n",
2332                     afs_printable_int32_ld(code),
2333                     afs_printable_uint32_lu(diskHeader.id));
2334                 return -1;
2335             }
2336         }
2337     }
2338     IH_INIT(isp->volSummary->volumeInfoHandle, salvinfo->fileSysDevice, isp->RWvolumeId,
2339             isp->volSummary->header.volumeInfo);
2340     return 0;
2341 }
2342
2343 int
2344 SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
2345               struct InodeSummary *isp, int check, int *deleteMe)
2346 {
2347     union {
2348         VolumeDiskData volumeInfo;
2349         struct versionStamp fileHeader;
2350     } header;
2351     IHandle_t *specH;
2352     int recreate = 0;
2353     ssize_t nBytes;
2354     FdHandle_t *fdP;
2355
2356     if (sp->obsolete)
2357         return 0;
2358 #ifndef AFS_NAMEI_ENV
2359     if (sp->inodeType == VI_LINKTABLE)
2360         return 0;
2361 #endif
2362     if (*(sp->inode) == 0) {
2363         if (check) {
2364             Log("Missing inode in volume header (%s)\n", sp->description);
2365             return -1;
2366         }
2367         if (!Showmode)
2368             Log("Missing inode in volume header (%s); %s\n", sp->description,
2369                 (Testing ? "it would have recreated it" : "recreating"));
2370         if (!Testing) {
2371             *(sp->inode) =
2372                 IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
2373                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2374             if (!VALID_INO(*(sp->inode)))
2375                 Abort
2376                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2377                      sp->description, errno);
2378         }
2379         recreate = 1;
2380     }
2381
2382     IH_INIT(specH, salvinfo->fileSysDevice, isp->RWvolumeId, *(sp->inode));
2383     fdP = IH_OPEN(specH);
2384     if (OKToZap && (fdP == NULL) && BadError(errno)) {
2385         /* bail out early and destroy the volume */
2386         if (!Showmode)
2387             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2388         if (deleteMe)
2389             *deleteMe = 1;
2390         IH_RELEASE(specH);
2391         return -1;
2392     }
2393     if (fdP == NULL)
2394         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2395               sp->description, errno);
2396
2397     if (!recreate
2398         && (FDH_PREAD(fdP, (char *)&header, sp->size, 0) != sp->size
2399             || header.fileHeader.magic != sp->stamp.magic)) {
2400         if (check) {
2401             Log("Part of the header (%s) is corrupted\n", sp->description);
2402             FDH_REALLYCLOSE(fdP);
2403             IH_RELEASE(specH);
2404             return -1;
2405         }
2406         Log("Part of the header (%s) is corrupted; recreating\n",
2407             sp->description);
2408         recreate = 1;
2409         /* header can be garbage; make sure we don't read garbage data from
2410          * it below */
2411         memset(&header, 0, sizeof(header));
2412     }
2413     if (sp->inodeType == VI_VOLINFO
2414         && header.volumeInfo.destroyMe == DESTROY_ME) {
2415         if (deleteMe)
2416             *deleteMe = 1;
2417         FDH_REALLYCLOSE(fdP);
2418         IH_RELEASE(specH);
2419         return -1;
2420     }
2421     if (recreate && !Testing) {
2422         if (check)
2423             Abort
2424                 ("Internal error: recreating volume header (%s) in check mode\n",
2425                  sp->description);
2426         nBytes = FDH_TRUNC(fdP, 0);
2427         if (nBytes == -1)
2428             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2429                   sp->description, errno);
2430
2431         /* The following code should be moved into vutil.c */
2432         if (sp->inodeType == VI_VOLINFO) {
2433             struct timeval tp;
2434             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2435             header.volumeInfo.stamp = sp->stamp;
2436             header.volumeInfo.id = isp->volumeId;
2437             header.volumeInfo.parentId = isp->RWvolumeId;
2438             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2439             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2440                 isp->volumeId, isp->volumeId);
2441             header.volumeInfo.inService = 0;
2442             header.volumeInfo.blessed = 0;
2443             /* The + 1000 is a hack in case there are any files out in venus caches */
2444             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2445             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
2446             header.volumeInfo.needsCallback = 0;
2447             gettimeofday(&tp, 0);
2448             header.volumeInfo.creationDate = tp.tv_sec;
2449             nBytes =
2450                 FDH_PWRITE(fdP, (char *)&header.volumeInfo,
2451                            sizeof(header.volumeInfo), 0);
2452             if (nBytes != sizeof(header.volumeInfo)) {
2453                 if (nBytes < 0)
2454                     Abort
2455                         ("Unable to write volume header file (%s) (errno = %d)\n",
2456                          sp->description, errno);
2457                 Abort("Unable to write entire volume header file (%s)\n",
2458                       sp->description);
2459             }
2460         } else {
2461             nBytes = FDH_PWRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp), 0);
2462             if (nBytes != sizeof(sp->stamp)) {
2463                 if (nBytes < 0)
2464                     Abort
2465                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2466                          sp->description, errno);
2467                 Abort
2468                     ("Unable to write entire version stamp in volume header file (%s)\n",
2469                      sp->description);
2470             }
2471         }
2472     }
2473     FDH_REALLYCLOSE(fdP);
2474     IH_RELEASE(specH);
2475     if (sp->inodeType == VI_VOLINFO) {
2476         salvinfo->VolInfo = header.volumeInfo;
2477         if (check) {
2478             char update[25];
2479
2480             if (salvinfo->VolInfo.updateDate) {
2481                 strcpy(update, TimeStamp(salvinfo->VolInfo.updateDate, 0));
2482                 if (!Showmode)
2483                     Log("%s (%u) %supdated %s\n", salvinfo->VolInfo.name,
2484                         salvinfo->VolInfo.id,
2485                         (Testing ? "it would have been " : ""), update);
2486             } else {
2487                 strcpy(update, TimeStamp(salvinfo->VolInfo.creationDate, 0));
2488                 if (!Showmode)
2489                     Log("%s (%u) not updated (created %s)\n",
2490                         salvinfo->VolInfo.name, salvinfo->VolInfo.id, update);
2491             }
2492
2493         }
2494     }
2495
2496     return 0;
2497 }
2498
2499 int
2500 SalvageVnodes(struct SalvInfo *salvinfo,
2501               struct InodeSummary *rwIsp,
2502               struct InodeSummary *thisIsp,
2503               struct ViceInodeInfo *inodes, int check)
2504 {
2505     int ilarge, ismall, ioffset, RW, nInodes;
2506     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
2507     if (Showmode)
2508         return 0;
2509     RW = (rwIsp == thisIsp);
2510     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2511     ismall =
2512         SalvageIndex(salvinfo, thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2513                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2514     if (check && ismall == -1)
2515         return -1;
2516     ilarge =
2517         SalvageIndex(salvinfo, thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2518                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2519     return (ilarge == 0 && ismall == 0 ? 0 : -1);
2520 }
2521
2522 int
2523 SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
2524              struct ViceInodeInfo *ip, int nInodes,
2525              struct VolumeSummary *volSummary, int check)
2526 {
2527     char buf[SIZEOF_LARGEDISKVNODE];
2528     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2529     int err = 0;
2530     StreamHandle_t *file;
2531     struct VnodeClassInfo *vcp;
2532     afs_sfsize_t size;
2533     afs_sfsize_t nVnodes;
2534     afs_fsize_t vnodeLength;
2535     int vnodeIndex;
2536     afs_ino_str_t stmp1, stmp2;
2537     IHandle_t *handle;
2538     FdHandle_t *fdP;
2539
2540     IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
2541     fdP = IH_OPEN(handle);
2542     osi_Assert(fdP != NULL);
2543     file = FDH_FDOPEN(fdP, "r+");
2544     osi_Assert(file != NULL);
2545     vcp = &VnodeClassInfo[class];
2546     size = OS_SIZE(fdP->fd_fd);
2547     osi_Assert(size != -1);
2548     nVnodes = (size / vcp->diskSize) - 1;
2549     if (nVnodes > 0) {
2550         osi_Assert((nVnodes + 1) * vcp->diskSize == size);
2551         osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
2552     } else {
2553         nVnodes = 0;
2554     }
2555     for (vnodeIndex = 0;
2556          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2557          nVnodes--, vnodeIndex++) {
2558         if (vnode->type != vNull) {
2559             int vnodeChanged = 0;
2560             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2561             if (VNDISK_GET_INO(vnode) == 0) {
2562                 if (RW) {
2563                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2564                     memset(vnode, 0, vcp->diskSize);
2565                     vnodeChanged = 1;
2566                 }
2567             } else {
2568                 if (vcp->magic != vnode->vnodeMagic) {
2569                     /* bad magic #, probably partially created vnode */
2570                     Log("Partially allocated vnode %d deleted.\n",
2571                         vnodeNumber);
2572                     memset(vnode, 0, vcp->diskSize);
2573                     vnodeChanged = 1;
2574                     goto vnodeDone;
2575                 }
2576                 /* ****** Should do a bit more salvage here:  e.g. make sure
2577                  * vnode type matches what it should be given the index */
2578                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2579 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2580  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2581  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2582  *                  }
2583  */
2584                     ip++;
2585                     nInodes--;
2586                 }
2587                 if (!RW) {
2588                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2589                         /* The following doesn't work, because the version number
2590                          * is not maintained correctly by the file server */
2591                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2592                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2593                          * break; */
2594                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2595                             break;
2596                         ip++;
2597                         nInodes--;
2598                     }
2599                 } else {
2600                     /* For RW volume, look for vnode with matching inode number;
2601                      * if no such match, take the first determined by our sort
2602                      * order */
2603                     struct ViceInodeInfo *lip = ip;
2604                     int lnInodes = nInodes;
2605                     while (lnInodes
2606                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2607                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2608                             ip = lip;
2609                             nInodes = lnInodes;
2610                             break;
2611                         }
2612                         lip++;
2613                         lnInodes--;
2614                     }
2615                 }
2616                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2617                     /* "Matching" inode */
2618                     if (RW) {
2619                         Unique vu, iu;
2620                         FileVersion vd, id;
2621                         vu = vnode->uniquifier;
2622                         iu = ip->u.vnode.vnodeUniquifier;
2623                         vd = vnode->dataVersion;
2624                         id = ip->u.vnode.inodeDataVersion;
2625                         /*
2626                          * Because of the possibility of the uniquifier overflows (> 4M)
2627                          * we compare them modulo the low 22-bits; we shouldn't worry
2628                          * about mismatching since they shouldn't to many old
2629                          * uniquifiers of the same vnode...
2630                          */
2631                         if (IUnique(vu) != IUnique(iu)) {
2632                             if (!Showmode) {
2633                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2634                             }
2635
2636                             vnode->uniquifier = iu;
2637 #ifdef  AFS_3DISPARES
2638                             vnode->dataVersion = (id >= vd ?
2639                                                   /* 90% of 2.1M */
2640                                                   ((id - vd) >
2641                                                    1887437 ? vd : id) :
2642                                                   /* 90% of 2.1M */
2643                                                   ((vd - id) >
2644                                                    1887437 ? id : vd));
2645 #else
2646 #if defined(AFS_SGI_EXMAG)
2647                             vnode->dataVersion = (id >= vd ?
2648                                                   /* 90% of 16M */
2649                                                   ((id - vd) >
2650                                                    15099494 ? vd : id) :
2651                                                   /* 90% of 16M */
2652                                                   ((vd - id) >
2653                                                    15099494 ? id : vd));
2654 #else
2655                             vnode->dataVersion = (id > vd ? id : vd);
2656 #endif /* AFS_SGI_EXMAG */
2657 #endif /* AFS_3DISPARES */
2658                             vnodeChanged = 1;
2659                         } else {
2660                             /* don't bother checking for vd > id any more, since
2661                              * partial file transfers always result in this state,
2662                              * and you can't do much else anyway (you've already
2663                              * found the best data you can) */
2664 #ifdef  AFS_3DISPARES
2665                             if (!vnodeIsDirectory(vnodeNumber)
2666                                 && ((vd < id && (id - vd) < 1887437)
2667                                     || ((vd > id && (vd - id) > 1887437)))) {
2668 #else
2669 #if defined(AFS_SGI_EXMAG)
2670                             if (!vnodeIsDirectory(vnodeNumber)
2671                                 && ((vd < id && (id - vd) < 15099494)
2672                                     || ((vd > id && (vd - id) > 15099494)))) {
2673 #else
2674                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2675 #endif /* AFS_SGI_EXMAG */
2676 #endif
2677                                 if (!Showmode)
2678                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2679                                 vnode->dataVersion = id;
2680                                 vnodeChanged = 1;
2681                             }
2682                         }
2683                     }
2684                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2685                         if (check) {
2686                             if (!Showmode) {
2687                                 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);
2688                             }
2689                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2690                             err = -1;
2691                             goto zooks;
2692                         }
2693                         if (!Showmode) {
2694                             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);
2695                         }
2696                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2697                         vnodeChanged = 1;
2698                     }
2699                     VNDISK_GET_LEN(vnodeLength, vnode);
2700                     if (ip->byteCount != vnodeLength) {
2701                         if (check) {
2702                             if (!Showmode)
2703                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2704                             err = -1;
2705                             goto zooks;
2706                         }
2707                         if (!Showmode)
2708                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2709                         VNDISK_SET_LEN(vnode, ip->byteCount);
2710                         vnodeChanged = 1;
2711                     }
2712                     if (!check)
2713                         ip->linkCount--;        /* Keep the inode around */
2714                     ip++;
2715                     nInodes--;
2716                 } else {        /* no matching inode */
2717                     afs_ino_str_t stmp;
2718                     if (VNDISK_GET_INO(vnode) != 0
2719                         || vnode->type == vDirectory) {
2720                         /* No matching inode--get rid of the vnode */
2721                         if (check) {
2722                             if (VNDISK_GET_INO(vnode)) {
2723                                 if (!Showmode) {
2724                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)));
2725                                 }
2726                             } else {
2727                                 if (!Showmode)
2728                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2729                             }
2730                             err = -1;
2731                             goto zooks;
2732                         }
2733                         if (VNDISK_GET_INO(vnode)) {
2734                             if (!Showmode) {
2735                                 time_t serverModifyTime = vnode->serverModifyTime;
2736                                 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));
2737                             }
2738                         } else {
2739                             if (!Showmode) {
2740                                 time_t serverModifyTime = vnode->serverModifyTime;
2741                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2742                             }
2743                         }
2744                         memset(vnode, 0, vcp->diskSize);
2745                         vnodeChanged = 1;
2746                     } else {
2747                         /* Should not reach here becuase we checked for
2748                          * (inodeNumber == 0) above. And where we zero the vnode,
2749                          * we also goto vnodeDone.
2750                          */
2751                     }
2752                 }
2753                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2754                     ip++;
2755                     nInodes--;
2756                 }
2757             }                   /* VNDISK_GET_INO(vnode) != 0 */
2758           vnodeDone:
2759             osi_Assert(!(vnodeChanged && check));
2760             if (vnodeChanged && !Testing) {
2761                 osi_Assert(IH_IWRITE
2762                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2763                         (char *)vnode, vcp->diskSize)
2764                        == vcp->diskSize);
2765                 salvinfo->VolumeChanged = 1;    /* For break call back */
2766             }
2767         }
2768     }
2769   zooks:
2770     STREAM_CLOSE(file);
2771     FDH_CLOSE(fdP);
2772     IH_RELEASE(handle);
2773     return err;
2774 }
2775
2776 struct VnodeEssence *
2777 CheckVnodeNumber(struct SalvInfo *salvinfo, VnodeId vnodeNumber)
2778 {
2779     VnodeClass class;
2780     struct VnodeInfo *vip;
2781     int offset;
2782
2783     class = vnodeIdToClass(vnodeNumber);
2784     vip = &salvinfo->vnodeInfo[class];
2785     offset = vnodeIdToBitNumber(vnodeNumber);
2786     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2787 }
2788
2789 void
2790 CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
2791 {
2792     /* Copy the directory unconditionally if we are going to change it:
2793      * not just if was cloned.
2794      */
2795     struct VnodeDiskObject vnode;
2796     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2797     Inode oldinode, newinode;
2798     afs_sfsize_t code;
2799
2800     if (dir->copied || Testing)
2801         return;
2802     DFlush();                   /* Well justified paranoia... */
2803
2804     code =
2805         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2806                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2807                  sizeof(vnode));
2808     osi_Assert(code == sizeof(vnode));
2809     oldinode = VNDISK_GET_INO(&vnode);
2810     /* Increment the version number by a whole lot to avoid problems with
2811      * clients that were promised new version numbers--but the file server
2812      * crashed before the versions were written to disk.
2813      */
2814     newinode =
2815         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2816                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2817                   200);
2818     osi_Assert(VALID_INO(newinode));
2819     osi_Assert(CopyInode(salvinfo->fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2820     vnode.cloned = 0;
2821     VNDISK_SET_INO(&vnode, newinode);
2822     code =
2823         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
2824                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2825                   sizeof(vnode));
2826     osi_Assert(code == sizeof(vnode));
2827
2828     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2829                         salvinfo->fileSysDevice, newinode,
2830                         &salvinfo->VolumeChanged);
2831     /* Don't delete the original inode right away, because the directory is
2832      * still being scanned.
2833      */
2834     dir->copied = 1;
2835 }
2836
2837 /*
2838  * This function should either successfully create a new dir, or give up
2839  * and leave things the way they were.  In particular, if it fails to write
2840  * the new dir properly, it should return w/o changing the reference to the
2841  * old dir.
2842  */
2843 void
2844 CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
2845 {
2846     struct VnodeDiskObject vnode;
2847     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2848     Inode oldinode, newinode;
2849     DirHandle newdir;
2850     FdHandle_t *fdP;
2851     afs_int32 code;
2852     afs_sfsize_t lcode;
2853     afs_int32 parentUnique = 1;
2854     struct VnodeEssence *vnodeEssence;
2855     afs_fsize_t length;
2856
2857     if (Testing)
2858         return;
2859     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2860     lcode =
2861         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2862                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2863                  sizeof(vnode));
2864     osi_Assert(lcode == sizeof(vnode));
2865     oldinode = VNDISK_GET_INO(&vnode);
2866     /* Increment the version number by a whole lot to avoid problems with
2867      * clients that were promised new version numbers--but the file server
2868      * crashed before the versions were written to disk.
2869      */
2870     newinode =
2871         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2872                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2873                   200);
2874     osi_Assert(VALID_INO(newinode));
2875     SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
2876                         &salvinfo->VolumeChanged);
2877
2878     /* Assign . and .. vnode numbers from dir and vnode.parent.
2879      * The uniquifier for . is in the vnode.
2880      * The uniquifier for .. might be set to a bogus value of 1 and
2881      * the salvager will later clean it up.
2882      */
2883     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(salvinfo, vnode.parent))) {
2884         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2885     }
2886     code =
2887         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2888                    vnode.uniquifier,
2889                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2890                    parentUnique);
2891     if (code == 0)
2892         code = DFlush();
2893     if (code) {
2894         /* didn't really build the new directory properly, let's just give up. */
2895         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2896         Log("Directory salvage returned code %d, continuing.\n", code);
2897         if (code) {
2898             Log("also failed to decrement link count on new inode");
2899         }
2900         osi_Assert(1 == 2);
2901     }
2902     Log("Checking the results of the directory salvage...\n");
2903     if (!DirOK(&newdir)) {
2904         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2905         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2906         osi_Assert(code == 0);
2907         osi_Assert(1 == 2);
2908     }
2909     vnode.cloned = 0;
2910     VNDISK_SET_INO(&vnode, newinode);
2911     length = Length(&newdir);
2912     VNDISK_SET_LEN(&vnode, length);
2913     lcode =
2914         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
2915                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2916                   sizeof(vnode));
2917     osi_Assert(lcode == sizeof(vnode));
2918 #if 0
2919 #ifdef AFS_NT40_ENV
2920     nt_sync(salvinfo->fileSysDevice);
2921 #else
2922     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2923                                  * an open FD on the file itself to fsync.
2924                                  */
2925 #endif
2926 #else
2927     salvinfo->vnodeInfo[vLarge].handle->ih_synced = 1;
2928 #endif
2929     /* make sure old directory file is really closed */
2930     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2931     FDH_REALLYCLOSE(fdP);
2932
2933     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2934     osi_Assert(code == 0);
2935     dir->dirHandle = newdir;
2936 }
2937
2938 /**
2939  * arguments for JudgeEntry.
2940  */
2941 struct judgeEntry_params {
2942     struct DirSummary *dir;    /**< directory we're examining entries in */
2943     struct SalvInfo *salvinfo; /**< SalvInfo for the current salvage job */
2944 };
2945
2946 int
2947 JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
2948            afs_int32 unique)
2949 {
2950     struct judgeEntry_params *params = arock;
2951     struct DirSummary *dir = params->dir;
2952     struct SalvInfo *salvinfo = params->salvinfo;
2953     struct VnodeEssence *vnodeEssence;
2954     afs_int32 dirOrphaned, todelete;
2955
2956     dirOrphaned = IsVnodeOrphaned(salvinfo, dir->vnodeNumber);
2957
2958     vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
2959     if (vnodeEssence == NULL) {
2960         if (!Showmode) {
2961             Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2962         }
2963         if (!Testing) {
2964             CopyOnWrite(salvinfo, dir);
2965             osi_Assert(Delete(&dir->dirHandle, name) == 0);
2966         }
2967         return 0;
2968     }
2969 #ifdef AFS_AIX_ENV
2970 #ifndef AFS_NAMEI_ENV
2971     /* On AIX machines, don't allow entries to point to inode 0. That is a special
2972      * mount inode for the partition. If this inode were deleted, it would crash
2973      * the machine.
2974      */
2975     if (vnodeEssence->InodeNumber == 0) {
2976         Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
2977         if (!Testing) {
2978             CopyOnWrite(salvinfo, dir);
2979             osi_Assert(Delete(&dir->dirHandle, name) == 0);
2980         }
2981         return 0;
2982     }
2983 #endif
2984 #endif
2985
2986     if (!(vnodeNumber & 1) && !Showmode
2987         && !(vnodeEssence->count || vnodeEssence->unique
2988              || vnodeEssence->modeBits)) {
2989         Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2990             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2991             vnodeNumber, unique,
2992             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2993              ""));
2994         if (!unique) {
2995             if (!Testing) {
2996                 CopyOnWrite(salvinfo, dir);
2997                 osi_Assert(Delete(&dir->dirHandle, name) == 0);
2998             }
2999             return 0;
3000         }
3001     }
3002
3003     /* Check if the Uniquifiers match. If not, change the directory entry
3004      * so its unique matches the vnode unique. Delete if the unique is zero
3005      * or if the directory is orphaned.
3006      */
3007     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
3008         if (!vnodeEssence->unique
3009             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
3010             /* This is an orphaned directory. Don't delete the . or ..
3011              * entry. Otherwise, it will get created in the next
3012              * salvage and deleted again here. So Just skip it.
3013              */
3014             return 0;
3015         }
3016
3017         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
3018
3019         if (!Showmode) {
3020             Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
3021         }
3022         if (!Testing) {
3023             AFSFid fid;
3024             fid.Vnode = vnodeNumber;
3025             fid.Unique = vnodeEssence->unique;
3026             CopyOnWrite(salvinfo, dir);
3027             osi_Assert(Delete(&dir->dirHandle, name) == 0);
3028             if (!todelete)
3029                 osi_Assert(Create(&dir->dirHandle, name, &fid) == 0);
3030         }
3031         if (todelete)
3032             return 0;           /* no need to continue */
3033     }
3034
3035     if (strcmp(name, ".") == 0) {
3036         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
3037             AFSFid fid;
3038             if (!Showmode)
3039                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3040             if (!Testing) {
3041                 CopyOnWrite(salvinfo, dir);
3042                 osi_Assert(Delete(&dir->dirHandle, ".") == 0);
3043                 fid.Vnode = dir->vnodeNumber;
3044                 fid.Unique = dir->unique;
3045                 osi_Assert(Create(&dir->dirHandle, ".", &fid) == 0);
3046             }
3047
3048             vnodeNumber = fid.Vnode;    /* Get the new Essence */
3049             unique = fid.Unique;
3050             vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3051         }
3052         dir->haveDot = 1;
3053     } else if (strcmp(name, "..") == 0) {
3054         AFSFid pa;
3055         if (dir->parent) {
3056             struct VnodeEssence *dotdot;
3057             pa.Vnode = dir->parent;
3058             dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
3059             osi_Assert(dotdot != NULL); /* XXX Should not be assert */
3060             pa.Unique = dotdot->unique;
3061         } else {
3062             pa.Vnode = dir->vnodeNumber;
3063             pa.Unique = dir->unique;
3064         }
3065         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
3066             if (!Showmode)
3067                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3068             if (!Testing) {
3069                 CopyOnWrite(salvinfo, dir);
3070                 osi_Assert(Delete(&dir->dirHandle, "..") == 0);
3071                 osi_Assert(Create(&dir->dirHandle, "..", &pa) == 0);
3072             }
3073
3074             vnodeNumber = pa.Vnode;     /* Get the new Essence */
3075             unique = pa.Unique;
3076             vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3077         }
3078         dir->haveDotDot = 1;
3079     } else if (strncmp(name, ".__afs", 6) == 0) {
3080         if (!Showmode) {
3081             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);
3082         }
3083         if (!Testing) {
3084             CopyOnWrite(salvinfo, dir);
3085             osi_Assert(Delete(&dir->dirHandle, name) == 0);
3086         }
3087         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
3088         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
3089         return 0;
3090     } else {
3091         if (ShowSuid && (vnodeEssence->modeBits & 06000))
3092             Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3093         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
3094             && !(vnodeEssence->modeBits & 0111)) {
3095             ssize_t nBytes;
3096             afs_sfsize_t size;
3097             char buf[1025];
3098             IHandle_t *ihP;
3099             FdHandle_t *fdP;
3100
3101             IH_INIT(ihP, salvinfo->fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3102                     vnodeEssence->InodeNumber);
3103             fdP = IH_OPEN(ihP);
3104             if (fdP == NULL) {
3105                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
3106                 IH_RELEASE(ihP);
3107                 return 0;
3108             }
3109             size = FDH_SIZE(fdP);
3110             if (size < 0) {
3111                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, (int)size, vnodeNumber);
3112                 FDH_REALLYCLOSE(fdP);
3113                 IH_RELEASE(ihP);
3114                 return 0;
3115             }
3116
3117             if (size > 1024)
3118                 size = 1024;
3119             nBytes = FDH_PREAD(fdP, buf, size, 0);
3120             if (nBytes == size) {
3121                 buf[size] = '\0';
3122                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
3123                     Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
3124                         dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
3125                         Testing ? "would convert" : "converted");
3126                     vnodeEssence->modeBits |= 0111;
3127                     vnodeEssence->changed = 1;
3128                 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3129                     dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3130                     dir->name ? dir->name : "??", name, buf);
3131             } else {
3132                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3133                     dir->vname, vnodeNumber, (int)size, (int)nBytes);
3134             }
3135             FDH_REALLYCLOSE(fdP);
3136             IH_RELEASE(ihP);
3137         }
3138         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3139             Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3140         if (vnodeIdToClass(vnodeNumber) == vLarge
3141             && vnodeEssence->name == NULL) {
3142             char *n;
3143             if ((n = (char *)malloc(strlen(name) + 1)))
3144                 strcpy(n, name);
3145             vnodeEssence->name = n;
3146         }
3147
3148         /* The directory entry points to the vnode. Check to see if the
3149          * vnode points back to the directory. If not, then let the
3150          * directory claim it (else it might end up orphaned). Vnodes
3151          * already claimed by another directory are deleted from this
3152          * directory: hardlinks to the same vnode are not allowed
3153          * from different directories.
3154          */
3155         if (vnodeEssence->parent != dir->vnodeNumber) {
3156             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3157                 /* Vnode does not point back to this directory.
3158                  * Orphaned dirs cannot claim a file (it may belong to
3159                  * another non-orphaned dir).
3160                  */
3161                 if (!Showmode) {
3162                     Log("dir vnode %u: %s/%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
3163                 }
3164                 vnodeEssence->parent = dir->vnodeNumber;
3165                 vnodeEssence->changed = 1;
3166             } else {
3167                 /* Vnode was claimed by another directory */
3168                 if (!Showmode) {
3169                     if (dirOrphaned) {
3170                         Log("dir vnode %u: %s/%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3171                     } else if (vnodeNumber == 1) {
3172                         Log("dir vnode %d: %s/%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
3173                     } else {
3174                         Log("dir vnode %u: %s/%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3175                     }
3176                 }
3177                 if (!Testing) {
3178                     CopyOnWrite(salvinfo, dir);
3179                     osi_Assert(Delete(&dir->dirHandle, name) == 0);
3180                 }
3181                 return 0;
3182             }
3183         }
3184         /* This directory claims the vnode */
3185         vnodeEssence->claimed = 1;
3186     }
3187     vnodeEssence->count--;
3188     return 0;
3189 }
3190
3191 void
3192 DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
3193                    VnodeClass class, Inode ino, Unique * maxu)
3194 {
3195     struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
3196     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3197     char buf[SIZEOF_LARGEDISKVNODE];
3198     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3199     afs_sfsize_t size;
3200     StreamHandle_t *file;
3201     int vnodeIndex;
3202     int nVnodes;
3203     FdHandle_t *fdP;
3204
3205     IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
3206     fdP = IH_OPEN(vip->handle);
3207     osi_Assert(fdP != NULL);
3208     file = FDH_FDOPEN(fdP, "r+");
3209     osi_Assert(file != NULL);
3210     size = OS_SIZE(fdP->fd_fd);
3211     osi_Assert(size != -1);
3212     vip->nVnodes = (size / vcp->diskSize) - 1;
3213     if (vip->nVnodes > 0) {
3214         osi_Assert((vip->nVnodes + 1) * vcp->diskSize == size);
3215         osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
3216         osi_Assert((vip->vnodes = (struct VnodeEssence *)
3217                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3218         if (class == vLarge) {
3219             osi_Assert((vip->inodes = (Inode *)
3220                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3221         } else {
3222             vip->inodes = NULL;
3223         }
3224     } else {
3225         vip->nVnodes = 0;
3226         vip->vnodes = NULL;
3227         vip->inodes = NULL;
3228     }
3229     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3230     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3231          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3232          nVnodes--, vnodeIndex++) {
3233         if (vnode->type != vNull) {
3234             struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3235             afs_fsize_t vnodeLength;
3236             vip->nAllocatedVnodes++;
3237             vep->count = vnode->linkCount;
3238             VNDISK_GET_LEN(vnodeLength, vnode);
3239             vep->blockCount = nBlocks(vnodeLength);
3240             vip->volumeBlockCount += vep->blockCount;
3241             vep->parent = vnode->parent;
3242             vep->unique = vnode->uniquifier;
3243             if (*maxu < vnode->uniquifier)
3244                 *maxu = vnode->uniquifier;
3245             vep->modeBits = vnode->modeBits;
3246             vep->InodeNumber = VNDISK_GET_INO(vnode);
3247             vep->type = vnode->type;
3248             vep->author = vnode->author;
3249             vep->owner = vnode->owner;
3250             vep->group = vnode->group;
3251             if (vnode->type == vDirectory) {
3252                 if (class != vLarge) {
3253                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3254                     vip->nAllocatedVnodes--;
3255                     memset(vnode, 0, sizeof(vnode));
3256                     IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3257                               vnodeIndexOffset(vcp, vnodeNumber),
3258                               (char *)&vnode, sizeof(vnode));
3259                     salvinfo->VolumeChanged = 1;
3260                 } else
3261                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3262             }
3263         }
3264     }
3265     STREAM_CLOSE(file);
3266     FDH_CLOSE(fdP);
3267 }
3268
3269 static char *
3270 GetDirName(struct SalvInfo *salvinfo, VnodeId vnode, struct VnodeEssence *vp,
3271            char *path)
3272 {
3273     struct VnodeEssence *parentvp;
3274
3275     if (vnode == 1) {
3276         strcpy(path, ".");
3277         return path;
3278     }
3279     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(salvinfo, vp->parent))
3280         && GetDirName(salvinfo, vp->parent, parentvp, path)) {
3281         strcat(path, "/");
3282         strcat(path, vp->name);
3283         return path;
3284     }
3285     return 0;
3286 }
3287
3288 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3289  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3290  */
3291 static int
3292 IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode)
3293 {
3294     struct VnodeEssence *vep;
3295
3296     if (vnode == 0)
3297         return (1);             /* Vnode zero does not exist */
3298     if (vnode == 1)
3299         return (0);             /* The root dir vnode is always claimed */
3300     vep = CheckVnodeNumber(salvinfo, vnode);    /* Get the vnode essence */
3301     if (!vep || !vep->claimed)
3302         return (1);             /* Vnode is not claimed - it is orphaned */
3303
3304     return (IsVnodeOrphaned(salvinfo, vep->parent));
3305 }
3306
3307 void
3308 SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
3309            struct VnodeInfo *dirVnodeInfo, IHandle_t * alinkH, int i,
3310            struct DirSummary *rootdir, int *rootdirfound)
3311 {
3312     static struct DirSummary dir;
3313     static struct DirHandle dirHandle;
3314     struct VnodeEssence *parent;
3315     static char path[MAXPATHLEN];
3316     int dirok, code;
3317
3318     if (dirVnodeInfo->vnodes[i].salvaged)
3319         return;                 /* already salvaged */
3320
3321     dir.rwVid = rwVid;
3322     dirVnodeInfo->vnodes[i].salvaged = 1;
3323
3324     if (dirVnodeInfo->inodes[i] == 0)
3325         return;                 /* Not allocated to a directory */
3326
3327     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3328         if (dirVnodeInfo->vnodes[i].parent) {
3329             Log("Bad parent, vnode 1; %s...\n",
3330                 (Testing ? "skipping" : "salvaging"));
3331             dirVnodeInfo->vnodes[i].parent = 0;
3332             dirVnodeInfo->vnodes[i].changed = 1;
3333         }
3334     } else {
3335         parent = CheckVnodeNumber(salvinfo, dirVnodeInfo->vnodes[i].parent);
3336         if (parent && parent->salvaged == 0)
3337             SalvageDir(salvinfo, name, rwVid, dirVnodeInfo, alinkH,
3338                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3339                        rootdir, rootdirfound);
3340     }
3341
3342     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3343     dir.unique = dirVnodeInfo->vnodes[i].unique;
3344     dir.copied = 0;
3345     dir.vname = name;
3346     dir.parent = dirVnodeInfo->vnodes[i].parent;
3347     dir.haveDot = dir.haveDotDot = 0;
3348     dir.ds_linkH = alinkH;
3349     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, salvinfo->fileSysDevice,
3350                         dirVnodeInfo->inodes[i], &salvinfo->VolumeChanged);
3351
3352     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3353     if (!dirok) {
3354         if (!RebuildDirs) {
3355             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3356                 (Testing ? "skipping" : "salvaging"));
3357         }
3358         if (!Testing) {
3359             CopyAndSalvage(salvinfo, &dir);
3360             dirok = 1;
3361             dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3362         }
3363     }
3364     dirHandle = dir.dirHandle;
3365
3366     dir.name =
3367         GetDirName(salvinfo, bitNumberToVnodeNumber(i, vLarge),
3368                    &dirVnodeInfo->vnodes[i], path);
3369
3370     if (dirok) {
3371         /* If enumeration failed for random reasons, we will probably delete
3372          * too much stuff, so we guard against this instead.
3373          */
3374         struct judgeEntry_params judge_params;
3375         judge_params.salvinfo = salvinfo;
3376         judge_params.dir = &dir;
3377
3378         osi_Assert(EnumerateDir(&dirHandle, JudgeEntry, &judge_params) == 0);
3379     }
3380
3381     /* Delete the old directory if it was copied in order to salvage.
3382      * CopyOnWrite has written the new inode # to the disk, but we still
3383      * have the old one in our local structure here.  Thus, we idec the
3384      * local dude.
3385      */
3386     DFlush();
3387     if (dir.copied && !Testing) {
3388         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3389         osi_Assert(code == 0);
3390         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3391     }
3392
3393     /* Remember rootdir DirSummary _after_ it has been judged */
3394     if (dir.vnodeNumber == 1 && dir.unique == 1) {
3395         memcpy(rootdir, &dir, sizeof(struct DirSummary));
3396         *rootdirfound = 1;
3397     }
3398
3399     return;
3400 }
3401
3402 /**
3403  * Get a new FID that can be used to create a new file.
3404  *
3405  * @param[in] volHeader vol header for the volume
3406  * @param[in] class     what type of vnode we'll be creating (vLarge or vSmall)
3407  * @param[out] afid     the FID that we can use (only Vnode and Unique are set)
3408  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3409  *                          updated to the new max unique if we create a new
3410  *                          vnode
3411  */
3412 static void
3413 GetNewFID(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3414           VnodeClass class, AFSFid *afid, Unique *maxunique)
3415 {
3416     int i;
3417     for (i = 0; i < salvinfo->vnodeInfo[class].nVnodes; i++) {
3418         if (salvinfo->vnodeInfo[class].vnodes[i].type == vNull) {
3419             break;
3420         }
3421     }
3422     if (i == salvinfo->vnodeInfo[class].nVnodes) {
3423         /* no free vnodes; make a new one */
3424         salvinfo->vnodeInfo[class].nVnodes++;
3425         salvinfo->vnodeInfo[class].vnodes =
3426             realloc(salvinfo->vnodeInfo[class].vnodes,
3427                     sizeof(struct VnodeEssence) * (i+1));
3428
3429         salvinfo->vnodeInfo[class].vnodes[i].type = vNull;
3430     }
3431
3432     afid->Vnode = bitNumberToVnodeNumber(i, class);
3433
3434     if (volHeader->uniquifier < (*maxunique + 1)) {
3435         /* header uniq is bad; it will get bumped by 2000 later */
3436         afid->Unique = *maxunique + 1 + 2000;
3437         (*maxunique)++;
3438     } else {
3439         /* header uniq seems okay; just use that */
3440         afid->Unique = *maxunique = volHeader->uniquifier++;
3441     }
3442 }
3443
3444 /**
3445  * Create a vnode for a README file explaining not to use a recreated-root vol.
3446  *
3447  * @param[in] volHeader vol header for the volume
3448  * @param[in] alinkH    ihandle for i/o for the volume
3449  * @param[in] vid       volume id
3450  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3451  *                          updated to the new max unique if we create a new
3452  *                          vnode
3453  * @param[out] afid     FID for the new readme vnode
3454  * @param[out] ainode   the inode for the new readme file
3455  *
3456  * @return operation status
3457  *  @retval 0 success
3458  *  @retval -1 error
3459  */
3460 static int
3461 CreateReadme(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3462              IHandle_t *alinkH, VolumeId vid, Unique *maxunique, AFSFid *afid,
3463              Inode *ainode)
3464 {
3465     Inode readmeinode;
3466     struct VnodeDiskObject *rvnode = NULL;
3467     afs_sfsize_t bytes;
3468     IHandle_t *readmeH = NULL;
3469     struct VnodeEssence *vep;
3470     afs_fsize_t length;
3471     time_t now = time(NULL);
3472
3473     /* Try to make the note brief, but informative. Only administrators should
3474      * be able to read this file at first, so we can hopefully assume they
3475      * know what AFS is, what a volume is, etc. */
3476     char readme[] =
3477 "This volume has been salvaged, but has lost its original root directory.\n"
3478 "The root directory that exists now has been recreated from orphan files\n"
3479 "from the rest of the volume. This recreated root directory may interfere\n"
3480 "with old cached data on clients, and there is no way the salvager can\n"
3481 "reasonably prevent that. So, it is recommended that you do not continue to\n"
3482 "use this volume, but only copy the salvaged data to a new volume.\n"
3483 "Continuing to use this volume as it exists now may cause some clients to\n"
3484 "behave oddly when accessing this volume.\n"
3485 "\n\t -- Your friendly neighborhood OpenAFS salvager\n";
3486     /* ^ the person reading this probably just lost some data, so they could
3487      * use some cheering up. */
3488
3489     /* -1 for the trailing NUL */
3490     length = sizeof(readme) - 1;
3491
3492     GetNewFID(salvinfo, volHeader, vSmall, afid, maxunique);
3493
3494     vep = &salvinfo->vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
3495
3496     /* create the inode and write the contents */
3497     readmeinode = IH_CREATE(alinkH, salvinfo->fileSysDevice,
3498                             salvinfo->fileSysPath, 0, vid,
3499                             afid->Vnode, afid->Unique, 1);
3500     if (!VALID_INO(readmeinode)) {
3501         Log("CreateReadme: readme IH_CREATE failed\n");
3502         goto error;
3503     }
3504
3505     IH_INIT(readmeH, salvinfo->fileSysDevice, vid, readmeinode);
3506     bytes = IH_IWRITE(readmeH, 0, readme, length);
3507     IH_RELEASE(readmeH);
3508
3509     if (bytes != length) {
3510         Log("CreateReadme: IWRITE failed (%d/%d)\n", (int)bytes,
3511             (int)sizeof(readme));
3512         goto error;
3513     }
3514
3515     /* create the vnode and write it out */
3516     rvnode = calloc(1, SIZEOF_SMALLDISKVNODE);
3517     if (!rvnode) {
3518         Log("CreateRootDir: error alloc'ing memory\n");
3519         goto error;
3520     }
3521
3522     rvnode->type = vFile;
3523     rvnode->cloned = 0;
3524     rvnode->modeBits = 0777;
3525     rvnode->linkCount = 1;
3526     VNDISK_SET_LEN(rvnode, length);
3527     rvnode->uniquifier = afid->Unique;
3528     rvnode->dataVersion = 1;
3529     VNDISK_SET_INO(rvnode, readmeinode);
3530     rvnode->unixModifyTime = rvnode->serverModifyTime = now;
3531     rvnode->author = 0;
3532     rvnode->owner = 0;
3533     rvnode->parent = 1;
3534     rvnode->group = 0;
3535     rvnode->vnodeMagic = VnodeClassInfo[vSmall].magic;
3536
3537     bytes = IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3538                       vnodeIndexOffset(&VnodeClassInfo[vSmall], afid->Vnode),
3539                       (char*)rvnode, SIZEOF_SMALLDISKVNODE);
3540
3541     if (bytes != SIZEOF_SMALLDISKVNODE) {
3542         Log("CreateReadme: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3543             (int)SIZEOF_SMALLDISKVNODE);
3544         goto error;
3545     }
3546
3547     /* update VnodeEssence for new readme vnode */
3548     salvinfo->vnodeInfo[vSmall].nAllocatedVnodes++;
3549     vep->count = 0;
3550     vep->blockCount = nBlocks(length);
3551     salvinfo->vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
3552     vep->parent = rvnode->parent;
3553     vep->unique = rvnode->uniquifier;
3554     vep->modeBits = rvnode->modeBits;
3555     vep->InodeNumber = VNDISK_GET_INO(rvnode);
3556     vep->type = rvnode->type;
3557     vep->author = rvnode->author;
3558     vep->owner = rvnode->owner;
3559     vep->group = rvnode->group;
3560
3561     free(rvnode);
3562     rvnode = NULL;
3563
3564     vep->claimed = 1;
3565     vep->changed = 0;
3566     vep->salvaged = 1;
3567     vep->todelete = 0;
3568
3569     *ainode = readmeinode;
3570
3571     return 0;
3572
3573  error:
3574     if (IH_DEC(alinkH, readmeinode, vid)) {
3575         Log("CreateReadme (recovery): IH_DEC failed\n");
3576     }
3577
3578     if (rvnode) {
3579         free(rvnode);
3580         rvnode = NULL;
3581     }
3582
3583     return -1;
3584 }
3585
3586 /**
3587  * create a root dir for a volume that lacks one.
3588  *
3589  * @param[in] volHeader vol header for the volume
3590  * @param[in] alinkH    ihandle for disk access for this volume group
3591  * @param[in] vid       volume id we're dealing with
3592  * @param[out] rootdir  populated with info about the new root dir
3593  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3594  *                          updated to the new max unique if we create a new
3595  *                          vnode
3596  *
3597  * @return operation status
3598  *  @retval 0  success
3599  *  @retval -1 error
3600  */
3601 static int
3602 CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3603               IHandle_t *alinkH, VolumeId vid, struct DirSummary *rootdir,
3604               Unique *maxunique)
3605 {
3606     FileVersion dv;
3607     int decroot = 0, decreadme = 0;
3608     AFSFid did, readmeid;
3609     afs_fsize_t length;
3610     Inode rootinode;
3611     struct VnodeDiskObject *rootvnode = NULL;
3612     struct acl_accessList *ACL;
3613     Inode *ip;
3614     afs_sfsize_t bytes;
3615     struct VnodeEssence *vep;
3616     Inode readmeinode;
3617     time_t now = time(NULL);
3618
3619     if (!salvinfo->vnodeInfo[vLarge].vnodes && !salvinfo->vnodeInfo[vSmall].vnodes) {
3620         Log("Not creating new root dir; volume appears to lack any vnodes\n");
3621         goto error;
3622     }
3623
3624     if (!salvinfo->vnodeInfo[vLarge].vnodes) {
3625         /* We don't have any large vnodes in the volume; allocate room
3626          * for one so we can recreate the root dir */
3627         salvinfo->vnodeInfo[vLarge].nVnodes = 1;
3628         salvinfo->vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
3629         salvinfo->vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
3630
3631         osi_Assert(salvinfo->vnodeInfo[vLarge].vnodes);
3632         osi_Assert(salvinfo->vnodeInfo[vLarge].inodes);
3633     }
3634
3635     vep = &salvinfo->vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
3636     ip = &salvinfo->vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
3637     if (vep->type != vNull) {
3638         Log("Not creating new root dir; existing vnode 1 is non-null\n");
3639         goto error;
3640     }
3641
3642     if (CreateReadme(salvinfo, volHeader, alinkH, vid, maxunique, &readmeid,
3643                      &readmeinode) != 0) {
3644         goto error;
3645     }
3646     decreadme = 1;
3647
3648     /* set the DV to a very high number, so it is unlikely that we collide
3649      * with a cached DV */
3650     dv = 1 << 30;
3651
3652     rootinode = IH_CREATE(alinkH, salvinfo->fileSysDevice, salvinfo->fileSysPath,
3653                           0, vid, 1, 1, dv);
3654     if (!VALID_INO(rootinode)) {
3655         Log("CreateRootDir: IH_CREATE failed\n");
3656         goto error;
3657     }
3658     decroot = 1;
3659
3660     SetSalvageDirHandle(&rootdir->dirHandle, vid, salvinfo->fileSysDevice,
3661                         rootinode, &salvinfo->VolumeChanged);
3662     did.Volume = vid;
3663     did.Vnode = 1;
3664     did.Unique = 1;
3665     if (MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
3666         Log("CreateRootDir: MakeDir failed\n");
3667         goto error;
3668     }
3669     if (Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
3670         Log("CreateRootDir: Create failed\n");
3671         goto error;
3672     }
3673     DFlush();
3674     length = Length(&rootdir->dirHandle);
3675     DZap((void *)&rootdir->dirHandle);
3676
3677     /* create the new root dir vnode */
3678     rootvnode = calloc(1, SIZEOF_LARGEDISKVNODE);
3679     if (!rootvnode) {
3680         Log("CreateRootDir: malloc failed\n");
3681         goto error;
3682     }
3683
3684     /* only give 'rl' permissions to 'system:administrators'. We do this to
3685      * try to catch the attention of an administrator, that they should not
3686      * be writing to this directory or continue to use it. */
3687     ACL = VVnodeDiskACL(rootvnode);
3688     ACL->size = sizeof(struct acl_accessList);
3689     ACL->version = ACL_ACLVERSION;
3690     ACL->total = 1;
3691     ACL->positive = 1;
3692     ACL->negative = 0;
3693     ACL->entries[0].id = -204; /* system:administrators */
3694     ACL->entries[0].rights = PRSFS_READ | PRSFS_LOOKUP;
3695
3696     rootvnode->type = vDirectory;
3697     rootvnode->cloned = 0;
3698     rootvnode->modeBits = 0777;
3699     rootvnode->linkCount = 2;
3700     VNDISK_SET_LEN(rootvnode, length);
3701     rootvnode->uniquifier = 1;
3702     rootvnode->dataVersion = dv;
3703     VNDISK_SET_INO(rootvnode, rootinode);
3704     rootvnode->unixModifyTime = rootvnode->serverModifyTime = now;
3705     rootvnode->author = 0;
3706     rootvnode->owner = 0;
3707     rootvnode->parent = 0;
3708     rootvnode->group = 0;
3709     rootvnode->vnodeMagic = VnodeClassInfo[vLarge].magic;
3710
3711     /* write it out to disk */
3712     bytes = IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
3713               vnodeIndexOffset(&VnodeClassInfo[vLarge], 1),
3714               (char*)rootvnode, SIZEOF_LARGEDISKVNODE);
3715
3716     if (bytes != SIZEOF_LARGEDISKVNODE) {
3717         /* just cast to int and don't worry about printing real 64-bit ints;
3718          * a large disk vnode isn't anywhere near the 32-bit limit */
3719         Log("CreateRootDir: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3720             (int)SIZEOF_LARGEDISKVNODE);
3721         goto error;
3722     }
3723
3724     /* update VnodeEssence for the new root vnode */
3725     salvinfo->vnodeInfo[vLarge].nAllocatedVnodes++;
3726     vep->count = 0;
3727     vep->blockCount = nBlocks(length);
3728     salvinfo->vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
3729     vep->parent = rootvnode->parent;
3730     vep->unique = rootvnode->uniquifier;
3731     vep->modeBits = rootvnode->modeBits;
3732     vep->InodeNumber = VNDISK_GET_INO(rootvnode);
3733     vep->type = rootvnode->type;
3734     vep->author = rootvnode->author;
3735     vep->owner = rootvnode->owner;
3736     vep->group = rootvnode->group;
3737
3738     free(rootvnode);
3739     rootvnode = NULL;
3740
3741     vep->claimed = 0;
3742     vep->changed = 0;
3743     vep->salvaged = 1;
3744     vep->todelete = 0;
3745
3746     /* update DirSummary for the new root vnode */
3747     rootdir->vnodeNumber = 1;
3748     rootdir->unique = 1;
3749     rootdir->haveDot = 1;
3750     rootdir->haveDotDot = 1;
3751     rootdir->rwVid = vid;
3752     rootdir->copied = 0;
3753     rootdir->parent = 0;
3754     rootdir->name = strdup(".");
3755     rootdir->vname = volHeader->name;
3756     rootdir->ds_linkH = alinkH;
3757
3758     *ip = rootinode;
3759
3760     return 0;
3761
3762  error:
3763     if (decroot && IH_DEC(alinkH, rootinode, vid)) {
3764         Log("CreateRootDir (recovery): IH_DEC (root) failed\n");
3765     }
3766     if (decreadme && IH_DEC(alinkH, readmeinode, vid)) {
3767         Log("CreateRootDir (recovery): IH_DEC (readme) failed\n");
3768     }
3769     if (rootvnode) {
3770         free(rootvnode);
3771         rootvnode = NULL;
3772     }
3773     return -1;
3774 }
3775
3776 /**
3777  * salvage a volume group.
3778  *
3779  * @param[in] salvinfo information for the curent salvage job
3780  * @param[in] rwIsp    inode summary for rw volume
3781  * @param[in] alinkH   link table inode handle
3782  *
3783  * @return operation status
3784  *   @retval 0 success
3785  */
3786 int
3787 SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t * alinkH)
3788 {
3789     /* This routine, for now, will only be called for read-write volumes */
3790     int i, j, code;
3791     int BlocksInVolume = 0, FilesInVolume = 0;
3792     VnodeClass class;
3793     struct DirSummary rootdir, oldrootdir;
3794     struct VnodeInfo *dirVnodeInfo;
3795     struct VnodeDiskObject vnode;
3796     VolumeDiskData volHeader;
3797     VolumeId vid;
3798     int orphaned, rootdirfound = 0;
3799     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
3800     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
3801     struct VnodeEssence *vep;
3802     afs_int32 v, pv;
3803     IHandle_t *h;
3804     afs_sfsize_t nBytes;
3805     AFSFid pa;
3806     VnodeId LFVnode, ThisVnode;
3807     Unique LFUnique, ThisUnique;
3808     char npath[128];
3809     int newrootdir = 0;
3810
3811     vid = rwIsp->volSummary->header.id;
3812     IH_INIT(h, salvinfo->fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3813     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3814     osi_Assert(nBytes == sizeof(volHeader));
3815     osi_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3816     osi_Assert(volHeader.destroyMe != DESTROY_ME);
3817     /* (should not have gotten this far with DESTROY_ME flag still set!) */
3818
3819     DistilVnodeEssence(salvinfo, vid, vLarge,
3820                        rwIsp->volSummary->header.largeVnodeIndex, &maxunique);
3821     DistilVnodeEssence(salvinfo, vid, vSmall,
3822                        rwIsp->volSummary->header.smallVnodeIndex, &maxunique);
3823
3824     dirVnodeInfo = &salvinfo->vnodeInfo[vLarge];
3825     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3826         SalvageDir(salvinfo, volHeader.name, vid, dirVnodeInfo, alinkH, i,
3827                    &rootdir, &rootdirfound);
3828     }
3829 #ifdef AFS_NT40_ENV
3830     nt_sync(salvinfo->fileSysDevice);
3831 #else
3832     sync();                             /* This used to be done lower level, for every dir */
3833 #endif
3834     if (Showmode) {
3835         IH_RELEASE(h);
3836         return 0;
3837     }
3838
3839     if (!rootdirfound && (orphans == ORPH_ATTACH) && !Testing) {
3840
3841         Log("Cannot find root directory for volume %lu; attempting to create "
3842             "a new one\n", afs_printable_uint32_lu(vid));
3843
3844         code = CreateRootDir(salvinfo, &volHeader, alinkH, vid, &rootdir,
3845                              &maxunique);
3846         if (code == 0) {
3847             rootdirfound = 1;
3848             newrootdir = 1;
3849             salvinfo->VolumeChanged = 1;
3850         }
3851     }
3852
3853     /* Parse each vnode looking for orphaned vnodes and
3854      * connect them to the tree as orphaned (if requested).
3855      */
3856     oldrootdir = rootdir;
3857     for (class = 0; class < nVNODECLASSES; class++) {
3858         for (v = 0; v < salvinfo->vnodeInfo[class].nVnodes; v++) {
3859             vep = &(salvinfo->vnodeInfo[class].vnodes[v]);
3860             ThisVnode = bitNumberToVnodeNumber(v, class);
3861             ThisUnique = vep->unique;
3862
3863             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3864                 continue;       /* Ignore unused, claimed, and root vnodes */
3865
3866             /* This vnode is orphaned. If it is a directory vnode, then the '..'
3867              * entry in this vnode had incremented the parent link count (In
3868              * JudgeEntry()). We need to go to the parent and decrement that
3869              * link count. But if the parent's unique is zero, then the parent
3870              * link count was not incremented in JudgeEntry().
3871              */
3872             if (class == vLarge) {      /* directory vnode */
3873                 pv = vnodeIdToBitNumber(vep->parent);
3874                 if (salvinfo->vnodeInfo[vLarge].vnodes[pv].unique != 0) {
3875                     if (vep->parent == 1 && newrootdir) {
3876                         /* this vnode's parent was the volume root, and
3877                          * we just created the volume root. So, the parent
3878                          * dir didn't exist during JudgeEntry, so the link
3879                          * count was not inc'd there, so don't dec it here.
3880                          */
3881
3882                          /* noop */
3883
3884                     } else {
3885                         salvinfo->vnodeInfo[vLarge].vnodes[pv].count++;
3886                     }
3887                 }
3888             }
3889
3890             if (!rootdirfound)
3891                 continue;       /* If no rootdir, can't attach orphaned files */
3892
3893             /* Here we attach orphaned files and directories into the
3894              * root directory, LVVnode, making sure link counts stay correct.
3895              */
3896             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3897                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
3898                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
3899
3900                 /* Update this orphaned vnode's info. Its parent info and
3901                  * link count (do for orphaned directories and files).
3902                  */
3903                 vep->parent = LFVnode;  /* Parent is the root dir */
3904                 vep->unique = LFUnique;
3905                 vep->changed = 1;
3906                 vep->claimed = 1;
3907                 vep->count--;   /* Inc link count (root dir will pt to it) */
3908
3909                 /* If this orphaned vnode is a directory, change '..'.
3910                  * The name of the orphaned dir/file is unknown, so we
3911                  * build a unique name. No need to CopyOnWrite the directory
3912                  * since it is not connected to tree in BK or RO volume and
3913                  * won't be visible there.
3914                  */
3915                 if (class == vLarge) {
3916                     AFSFid pa;
3917                     DirHandle dh;
3918
3919                     /* Remove and recreate the ".." entry in this orphaned directory */
3920                     SetSalvageDirHandle(&dh, vid, salvinfo->fileSysDevice,
3921                                         salvinfo->vnodeInfo[class].inodes[v],
3922                                         &salvinfo->VolumeChanged);
3923                     pa.Vnode = LFVnode;
3924                     pa.Unique = LFUnique;
3925                     osi_Assert(Delete(&dh, "..") == 0);
3926                     osi_Assert(Create(&dh, "..", &pa) == 0);
3927
3928                     /* The original parent's link count was decremented above.
3929                      * Here we increment the new parent's link count.
3930                      */
3931                     pv = vnodeIdToBitNumber(LFVnode);
3932                     salvinfo->vnodeInfo[vLarge].vnodes[pv].count--;
3933
3934                 }
3935
3936                 /* Go to the root dir and add this entry. The link count of the
3937                  * root dir was incremented when ".." was created. Try 10 times.
3938                  */
3939                 for (j = 0; j < 10; j++) {
3940                     pa.Vnode = ThisVnode;
3941                     pa.Unique = ThisUnique;
3942
3943                     (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3944                                        ((class ==
3945                                          vLarge) ? "__ORPHANDIR__" :
3946                                         "__ORPHANFILE__"), ThisVnode,
3947                                        ThisUnique);
3948
3949                     CopyOnWrite(salvinfo, &rootdir);
3950                     code = Create(&rootdir.dirHandle, npath, &pa);
3951                     if (!code)
3952                         break;
3953
3954                     ThisUnique += 50;   /* Try creating a different file */
3955                 }
3956                 osi_Assert(code == 0);
3957                 Log("Attaching orphaned %s to volume's root dir as %s\n",
3958                     ((class == vLarge) ? "directory" : "file"), npath);
3959             }
3960         }                       /* for each vnode in the class */
3961     }                           /* for each class of vnode */
3962
3963     /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3964     DFlush();
3965     if (rootdirfound && !oldrootdir.copied && rootdir.copied) {
3966         code =
3967             IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3968                    oldrootdir.rwVid);
3969         osi_Assert(code == 0);
3970         /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3971     }
3972
3973     DFlush();                   /* Flush the changes */
3974     if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3975         Log("Cannot attach orphaned files and directories: Root directory not found\n");
3976         orphans = ORPH_IGNORE;
3977     }
3978
3979     /* Write out all changed vnodes. Orphaned files and directories
3980      * will get removed here also (if requested).
3981      */
3982     for (class = 0; class < nVNODECLASSES; class++) {
3983         int nVnodes = salvinfo->vnodeInfo[class].nVnodes;
3984         struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3985         struct VnodeEssence *vnodes = salvinfo->vnodeInfo[class].vnodes;
3986         FilesInVolume += salvinfo->vnodeInfo[class].nAllocatedVnodes;
3987         BlocksInVolume += salvinfo->vnodeInfo[class].volumeBlockCount;
3988         for (i = 0; i < nVnodes; i++) {
3989             struct VnodeEssence *vnp = &vnodes[i];
3990             VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3991
3992             /* If the vnode is good but is unclaimed (not listed in
3993              * any directory entries), then it is orphaned.
3994              */
3995             orphaned = -1;
3996             if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber))) {
3997                 vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
3998                 vnp->changed = 1;
3999             }
4000
4001             if (vnp->changed || vnp->count) {
4002                 int oldCount;
4003                 nBytes =
4004                     IH_IREAD(salvinfo->vnodeInfo[class].handle,
4005                              vnodeIndexOffset(vcp, vnodeNumber),
4006                              (char *)&vnode, sizeof(vnode));
4007                 osi_Assert(nBytes == sizeof(vnode));
4008
4009                 vnode.parent = vnp->parent;
4010                 oldCount = vnode.linkCount;
4011                 vnode.linkCount = vnode.linkCount - vnp->count;
4012
4013                 if (orphaned == -1)
4014                     orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber);