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