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