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