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