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