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