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