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