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