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