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