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