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