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