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