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