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