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