dcc9792e6c9500b576331221b1f01063da98bc8e
[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, ...);
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;
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         while (nInodes) {
1217             CountVolumeInodes(ip, nInodes, &summary);
1218             if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1219                 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1220                 fclose(summaryFile);
1221                 return -1;
1222             }
1223             summary.index += (summary.nInodes);
1224             nInodes -= summary.nInodes;
1225             ip += summary.nInodes;
1226         }
1227         /* Following fflush is not fclose, because if it was debug mode would not work */
1228         if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1229             Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1230             fclose(summaryFile);
1231             return -1;
1232         }
1233         if (canfork && !debug) {
1234             ShowLog = 0;
1235             Exit(0);
1236         }
1237     } else {
1238         if (Wait("Inode summary") == -1) {
1239             fclose(summaryFile);
1240             Exit(1);            /* salvage of this partition aborted */
1241         }
1242     }
1243     assert(afs_fstat(fileno(summaryFile), &status) != -1);
1244     if (status.st_size != 0) {
1245         int ret;
1246         unsigned long st_status=(unsigned long)status.st_size;
1247         inodeSummary = (struct InodeSummary *)malloc(st_status);
1248         assert(inodeSummary != NULL);
1249         /* For GNU we need to do lseek to get the file pointer moved. */
1250         assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1251         ret = read(fileno(summaryFile), inodeSummary, st_status);
1252         assert(ret == st_status);
1253     }
1254     nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1255     Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1256     fclose(summaryFile);
1257     return 0;
1258 }
1259
1260 /* Comparison routine for volume sort.
1261    This is setup so that a read-write volume comes immediately before
1262    any read-only clones of that volume */
1263 int
1264 CompareVolumes(const void *_p1, const void *_p2)
1265 {
1266     register const struct VolumeSummary *p1 = _p1;
1267     register const struct VolumeSummary *p2 = _p2;
1268     if (p1->header.parent != p2->header.parent)
1269         return p1->header.parent < p2->header.parent ? -1 : 1;
1270     if (p1->header.id == p1->header.parent)     /* p1 is rw volume */
1271         return -1;
1272     if (p2->header.id == p2->header.parent)     /* p2 is rw volume */
1273         return 1;
1274     return p1->header.id < p2->header.id ? -1 : 1;      /* Both read-only */
1275 }
1276
1277 /**
1278  * Gleans volumeSummary information by asking the fileserver
1279  *
1280  * @param[in] singleVolumeNumber  the volume we're salvaging. 0 if we're
1281  *                                salvaging a whole partition
1282  *
1283  * @return whether we obtained the volume summary information or not
1284  *  @retval 0  success; we obtained the volume summary information
1285  *  @retval -1 we raced with a fileserver restart; volume locks and checkout
1286  *             must be retried
1287  *  @retval 1  we did not get the volume summary information; either the
1288  *             fileserver responded with an error, or we are not supposed to
1289  *             ask the fileserver for the information (e.g. we are salvaging
1290  *             the entire partition or we are not the salvageserver)
1291  *
1292  * @note for non-DAFS, always returns 1
1293  */
1294 static int
1295 AskVolumeSummary(VolumeId singleVolumeNumber)
1296 {
1297     afs_int32 code = 1;
1298 #if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
1299     if (programType == salvageServer) {
1300         if (singleVolumeNumber) {
1301             FSSYNC_VGQry_response_t q_res;
1302             SYNC_response res;
1303             struct VolumeSummary *vsp;
1304             int i;
1305             struct VolumeDiskHeader diskHdr;
1306
1307             memset(&res, 0, sizeof(res));
1308
1309             code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1310
1311             /*
1312              * We must wait for the partition to finish scanning before
1313              * can continue, since we will not know if we got the entire
1314              * VG membership unless the partition is fully scanned.
1315              * We could, in theory, just scan the partition ourselves if
1316              * the VG cache is not ready, but we would be doing the exact
1317              * same scan the fileserver is doing; it will almost always
1318              * be faster to wait for the fileserver. The only exceptions
1319              * are if the partition does not take very long to scan, and
1320              * in that case it's fast either way, so who cares?
1321              */
1322             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1323                 Log("waiting for fileserver to finish scanning partition %s...\n",
1324                     fileSysPartition->name);
1325
1326                 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1327                     /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1328                      * just so small partitions don't need to wait over 10
1329                      * seconds every time, and large partitions are generally
1330                      * polled only once every ten seconds. */
1331                     sleep((i > 10) ? (i = 10) : i);
1332
1333                     code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1334                 }
1335             }
1336
1337             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1338                 /* This can happen if there's no header for the volume
1339                  * we're salvaging, or no headers exist for the VG (if
1340                  * we're salvaging an RW). Act as if we got a response
1341                  * with no VG members. The headers may be created during
1342                  * salvaging, if there are inodes in this VG. */
1343                 code = 0;
1344                 memset(&q_res, 0, sizeof(q_res));
1345                 q_res.rw = singleVolumeNumber;
1346             }
1347
1348             if (code) {
1349                 Log("fileserver refused VGCQuery request for volume %lu on "
1350                     "partition %s, code %ld reason %ld\n",
1351                     afs_printable_uint32_lu(singleVolumeNumber),
1352                     fileSysPartition->name,
1353                     afs_printable_int32_ld(code),
1354                     afs_printable_int32_ld(res.hdr.reason));
1355                 goto done;
1356             }
1357
1358             if (q_res.rw != singleVolumeNumber) {
1359                 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1360                     afs_printable_uint32_lu(singleVolumeNumber),
1361                     afs_printable_uint32_lu(q_res.rw));
1362 #ifdef SALVSYNC_BUILD_CLIENT
1363                 if (SALVSYNC_LinkVolume(q_res.rw,
1364                                        singleVolumeNumber,
1365                                        fileSysPartition->name,
1366                                        NULL) != SYNC_OK) {
1367                     Log("schedule request failed\n");
1368                 }
1369 #endif /* SALVSYNC_BUILD_CLIENT */
1370                 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1371             }
1372
1373             volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
1374             assert(volumeSummaryp != NULL);
1375
1376             nVolumes = 0;
1377             vsp = volumeSummaryp;
1378
1379             for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1380                 char name[VMAXPATHLEN];
1381
1382                 if (!q_res.children[i]) {
1383                     continue;
1384                 }
1385
1386                 /* AskOffline for singleVolumeNumber was called much earlier */
1387                 if (q_res.children[i] != singleVolumeNumber) {
1388                     AskOffline(q_res.children[i], fileSysPartition->name);
1389                     if (LockVolume(q_res.children[i])) {
1390                         /* need to retry */
1391                         return -1;
1392                     }
1393                 }
1394
1395                 code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
1396                 if (code) {
1397                     Log("Cannot read header for %lu; trying to salvage group anyway\n",
1398                         afs_printable_uint32_lu(q_res.children[i]));
1399                     code = 0;
1400                     continue;
1401                 }
1402
1403                 DiskToVolumeHeader(&vsp->header, &diskHdr);
1404                 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1405                 vsp->fileName = ToString(name);
1406                 nVolumes++;
1407                 vsp++;
1408             }
1409
1410             qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1411                   CompareVolumes);
1412         }
1413       done:
1414         if (code) {
1415             Log("Cannot get volume summary from fileserver; falling back to scanning "
1416                 "entire partition\n");
1417         }
1418     }
1419 #endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
1420     return code;
1421 }
1422
1423 /**
1424  * count how many volume headers are found by VWalkVolumeHeaders.
1425  *
1426  * @param[in] dp   the disk partition (unused)
1427  * @param[in] name full path to the .vol header (unused)
1428  * @param[in] hdr  the header data (unused)
1429  * @param[in] last whether this is the last try or not (unused)
1430  * @param[in] rock actually an afs_int32*; the running count of how many
1431  *                 volumes we have found
1432  *
1433  * @retval 0 always
1434  */
1435 static int
1436 CountHeader(struct DiskPartition64 *dp, const char *name,
1437             struct VolumeDiskHeader *hdr, int last, void *rock)
1438 {
1439     afs_int32 *nvols = (afs_int32 *)rock;
1440     (*nvols)++;
1441     return 0;
1442 }
1443
1444 /**
1445  * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1446  * data.
1447  */
1448 struct SalvageScanParams {
1449     VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1450                                   * vol id of the VG we're salvaging */
1451     struct VolumeSummary *vsp;   /**< ptr to the current volume summary object
1452                                   * we're filling in */
1453     afs_int32 nVolumes;          /**< # of vols we've encountered */
1454     afs_int32 totalVolumes;      /**< max # of vols we should encounter (the
1455                                   * # of vols we've alloc'd memory for) */
1456     int retry;  /**< do we need to retry vol lock/checkout? */
1457 };
1458
1459 /**
1460  * records volume summary info found from VWalkVolumeHeaders.
1461  *
1462  * Found volumes are also taken offline if they are in the specific volume
1463  * group we are looking for.
1464  *
1465  * @param[in] dp   the disk partition
1466  * @param[in] name full path to the .vol header
1467  * @param[in] hdr  the header data
1468  * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1469  * @param[in] rock actually a struct SalvageScanParams*, containing the
1470  *                 information needed to record the volume summary data
1471  *
1472  * @return operation status
1473  *  @retval 0  success
1474  *  @retval -1 volume locking raced with fileserver restart; checking out
1475  *             and locking volumes needs to be retried
1476  *  @retval 1  volume header is mis-named and should be deleted
1477  */
1478 static int
1479 RecordHeader(struct DiskPartition64 *dp, const char *name,
1480              struct VolumeDiskHeader *hdr, int last, void *rock)
1481 {
1482     char nameShouldBe[64];
1483     struct SalvageScanParams *params;
1484     struct VolumeSummary summary;
1485     VolumeId singleVolumeNumber;
1486
1487     params = (struct SalvageScanParams *)rock;
1488
1489     singleVolumeNumber = params->singleVolumeNumber;
1490
1491     DiskToVolumeHeader(&summary.header, hdr);
1492
1493     if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1494         && summary.header.parent != singleVolumeNumber) {
1495
1496         if (programType == salvageServer) {
1497 #ifdef SALVSYNC_BUILD_CLIENT
1498             Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1499                 summary.header.id, summary.header.parent);
1500             if (SALVSYNC_LinkVolume(summary.header.parent,
1501                                     summary.header.id,
1502                                     dp->name,
1503                                     NULL) != SYNC_OK) {
1504                 Log("schedule request failed\n");
1505             }
1506 #endif
1507             Exit(SALSRV_EXIT_VOLGROUP_LINK);
1508
1509         } else {
1510             Log("%u is a read-only volume; not salvaged\n",
1511                 singleVolumeNumber);
1512             Exit(1);
1513         }
1514     }
1515
1516     if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1517         || summary.header.parent == singleVolumeNumber) {
1518
1519         /* check if the header file is incorrectly named */
1520         int badname = 0;
1521         const char *base = strrchr(name, '/');
1522         if (base) {
1523             base++;
1524         } else {
1525             base = name;
1526         }
1527
1528         (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1529                            VFORMAT, afs_printable_uint32_lu(summary.header.id));
1530
1531
1532         if (strcmp(nameShouldBe, base)) {
1533             /* .vol file has wrong name; retry/delete */
1534             badname = 1;
1535         }
1536
1537         if (!badname || last) {
1538             /* only offline the volume if the header is good, or if this is
1539              * the last try looking at it; avoid AskOffline'ing the same vol
1540              * multiple times */
1541
1542             if (singleVolumeNumber 
1543                 && summary.header.id != singleVolumeNumber) {
1544                 /* don't offline singleVolumeNumber; we already did that
1545                  * earlier */
1546
1547                 AskOffline(summary.header.id, fileSysPartition->name);
1548
1549 #ifdef AFS_DEMAND_ATTACH_FS
1550                 if (!badname) {
1551                     /* don't lock the volume if the header is bad, since we're
1552                      * about to delete it anyway. */
1553                     if (LockVolume(summary.header.id)) {
1554                         params->retry = 1;
1555                         return -1;
1556                     }
1557                 }
1558 #endif /* AFS_DEMAND_ATTACH_FS */
1559             }
1560         }
1561         if (badname) {
1562             if (last && !Showmode) {
1563                 Log("Volume header file %s is incorrectly named (should be %s "
1564                     "not %s); %sdeleted (it will be recreated later, if "
1565                     "necessary)\n", name, nameShouldBe, base,
1566                     (Testing ? "it would have been " : ""));
1567             }
1568             return 1;
1569         }
1570
1571         summary.fileName = ToString(base);
1572         params->nVolumes++;
1573
1574         if (params->nVolumes > params->totalVolumes) {
1575             /* We found more volumes than we found on the first partition walk;
1576              * apparently something created a volume while we were
1577              * partition-salvaging, or we found more than 20 vols when salvaging a
1578              * particular volume. Abort if we detect this, since other programs
1579              * supposed to not touch the partition while it is partition-salvaging,
1580              * and we shouldn't find more than 20 vols in a VG.
1581              */
1582             Abort("Found %ld vol headers, but should have found at most %ld! "
1583                   "Make sure the volserver/fileserver are not running at the "
1584                   "same time as a partition salvage\n",
1585                   afs_printable_int32_ld(params->nVolumes),
1586                   afs_printable_int32_ld(params->totalVolumes));
1587         }
1588
1589         memcpy(params->vsp, &summary, sizeof(summary));
1590         params->vsp++;
1591     }
1592
1593     return 0;
1594 }
1595
1596 /**
1597  * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1598  *
1599  * If the header could not be read in at all, the header is always unlinked.
1600  * If instead RecordHeader said the header was bad (that is, the header file
1601  * is mis-named), we only unlink if we are doing a partition salvage, as
1602  * opposed to salvaging a specific volume group.
1603  *
1604  * @param[in] dp   the disk partition
1605  * @param[in] name full path to the .vol header
1606  * @param[in] hdr  header data, or NULL if the header could not be read
1607  * @param[in] rock actually a struct SalvageScanParams*, with some information
1608  *                 about the scan
1609  */
1610 static void
1611 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1612              struct VolumeDiskHeader *hdr, void *rock)
1613 {
1614     struct SalvageScanParams *params;
1615     int dounlink = 0;
1616
1617     params = (struct SalvageScanParams *)rock;
1618
1619     if (!hdr) {
1620         /* no header; header is too bogus to read in at all */
1621         if (!Showmode) {
1622             Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1623         }
1624         if (!Testing) {
1625             dounlink = 1;
1626         }
1627
1628     } else if (!params->singleVolumeNumber) {
1629         /* We were able to read in a header, but RecordHeader said something
1630          * was wrong with it. We only unlink those if we are doing a partition
1631          * salvage. */
1632         if (!Testing) {
1633             dounlink = 1;
1634         }
1635     }
1636
1637     if (dounlink && unlink(name)) {
1638         Log("Error %d while trying to unlink %s\n", errno, name);
1639     }
1640 }
1641
1642 /**
1643  * Populates volumeSummaryp with volume summary information, either by asking
1644  * the fileserver for VG information, or by scanning the /vicepX partition.
1645  *
1646  * @param[in] singleVolumeNumber  the volume ID of the single volume group we
1647  *                                are salvaging, or 0 if this is a partition
1648  *                                salvage
1649  *
1650  * @return operation status
1651  *  @retval 0  success
1652  *  @retval -1 we raced with a fileserver restart; checking out and locking
1653  *             volumes must be retried
1654  */
1655 int
1656 GetVolumeSummary(VolumeId singleVolumeNumber)
1657 {
1658     afs_int32 nvols = 0;
1659     struct SalvageScanParams params;
1660     int code;
1661
1662     code = AskVolumeSummary(singleVolumeNumber);
1663     if (code == 0) {
1664         /* we successfully got the vol information from the fileserver; no
1665          * need to scan the partition */
1666         return 0;
1667     }
1668     if (code < 0) {
1669         /* we need to retry volume checkout */
1670         return code;
1671     }
1672
1673     if (!singleVolumeNumber) {
1674         /* Count how many volumes we have in /vicepX */
1675         code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
1676                                   NULL, &nvols);
1677         if (code < 0) {
1678             Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1679         }
1680         if (!nvols)
1681             nvols = 1;
1682     } else {
1683         nvols = VOL_VG_MAX_VOLS;
1684     }
1685
1686     volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
1687     assert(volumeSummaryp != NULL);
1688
1689     params.singleVolumeNumber = singleVolumeNumber;
1690     params.vsp = volumeSummaryp;
1691     params.nVolumes = 0;
1692     params.totalVolumes = nvols;
1693     params.retry = 0;
1694
1695     /* walk the partition directory of volume headers and record the info
1696      * about them; unlinking invalid headers */
1697     code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
1698                               UnlinkHeader, &params);
1699     if (params.retry) {
1700         /* we apparently need to retry checking-out/locking volumes */
1701         return -1;
1702     }
1703     if (code < 0) {
1704         Abort("Failed to get volume header summary\n");
1705     }
1706     nVolumes = params.nVolumes;
1707
1708     qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1709           CompareVolumes);
1710
1711     return 0;
1712 }
1713
1714 /* Find the link table. This should be associated with the RW volume or, if
1715  * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1716  */
1717 Inode
1718 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1719                struct ViceInodeInfo *allInodes)
1720 {
1721     int i, j;
1722     struct ViceInodeInfo *ip;
1723
1724     for (i = 0; i < nVols; i++) {
1725         ip = allInodes + isp[i].index;
1726         for (j = 0; j < isp[i].nSpecialInodes; j++) {
1727             if (ip[j].u.special.type == VI_LINKTABLE)
1728                 return ip[j].inodeNumber;
1729         }
1730     }
1731     return (Inode) - 1;
1732 }
1733
1734 int
1735 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1736 {
1737     struct versionStamp version;
1738     FdHandle_t *fdP;
1739
1740     if (!VALID_INO(ino))
1741         ino =
1742             IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1743                       INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1744     if (!VALID_INO(ino))
1745         Abort
1746             ("Unable to allocate link table inode for volume %u (error = %d)\n",
1747              isp->RWvolumeId, errno);
1748     IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1749     fdP = IH_OPEN(VGLinkH);
1750     if (fdP == NULL)
1751         Abort("Can't open link table for volume %u (error = %d)\n",
1752               isp->RWvolumeId, errno);
1753
1754     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1755         Abort("Can't truncate link table for volume %u (error = %d)\n",
1756               isp->RWvolumeId, errno);
1757
1758     version.magic = LINKTABLEMAGIC;
1759     version.version = LINKTABLEVERSION;
1760
1761     if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1762         != sizeof(version))
1763         Abort("Can't truncate link table for volume %u (error = %d)\n",
1764               isp->RWvolumeId, errno);
1765
1766     FDH_REALLYCLOSE(fdP);
1767
1768     /* If the volume summary exits (i.e.,  the V*.vol header file exists),
1769      * then set this inode there as well.
1770      */
1771     if (isp->volSummary)
1772         isp->volSummary->header.linkTable = ino;
1773
1774     return 0;
1775 }
1776
1777 #ifdef AFS_NT40_ENV
1778 void *
1779 nt_SVG(void *arg)
1780 {
1781     SVGParms_t *parms = (SVGParms_t *) arg;
1782     DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1783     return NULL;
1784 }
1785
1786 void
1787 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1788 {
1789     pthread_t tid;
1790     pthread_attr_t tattr;
1791     int code;
1792     SVGParms_t parms;
1793
1794     /* Initialize per volume global variables, even if later code does so */
1795     VolumeChanged = 0;
1796     VGLinkH = NULL;
1797     VGLinkH_cnt = 0;
1798     memset(&VolInfo, 0, sizeof(VolInfo));
1799
1800     parms.svgp_inodeSummaryp = isp;
1801     parms.svgp_count = nVols;
1802     code = pthread_attr_init(&tattr);
1803     if (code) {
1804         Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1805             isp->RWvolumeId);
1806         return;
1807     }
1808     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1809     if (code) {
1810         Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1811         return;
1812     }
1813     code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1814     if (code) {
1815         Log("Failed to create thread to salvage volume group %u\n",
1816             isp->RWvolumeId);
1817         return;
1818     }
1819     (void)pthread_join(tid, NULL);
1820 }
1821 #endif /* AFS_NT40_ENV */
1822
1823 void
1824 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1825 {
1826     struct ViceInodeInfo *inodes, *allInodes, *ip;
1827     int i, totalInodes, size, salvageTo;
1828     int haveRWvolume;
1829     int check;
1830     Inode ino;
1831     int dec_VGLinkH = 0;
1832     int VGLinkH_p1 =0;
1833     FdHandle_t *fdP = NULL;
1834
1835     VGLinkH_cnt = 0;
1836     haveRWvolume = (isp->volumeId == isp->RWvolumeId
1837                     && isp->nSpecialInodes > 0);
1838     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1839         if (!ForceSalvage && QuickCheck(isp, nVols))
1840             return;
1841     }
1842     if (ShowMounts && !haveRWvolume)
1843         return;
1844     if (canfork && !debug && Fork() != 0) {
1845         (void)Wait("Salvage volume group");
1846         return;
1847     }
1848     for (i = 0, totalInodes = 0; i < nVols; i++)
1849         totalInodes += isp[i].nInodes;
1850     size = totalInodes * sizeof(struct ViceInodeInfo);
1851     inodes = (struct ViceInodeInfo *)malloc(size);
1852     allInodes = inodes - isp->index;    /* this would the base of all the inodes
1853                                          * for the partition, if all the inodes
1854                                          * had been read into memory */
1855     assert(afs_lseek
1856            (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1857             SEEK_SET) != -1);
1858     assert(read(inodeFd, inodes, size) == size);
1859
1860     /* Don't try to salvage a read write volume if there isn't one on this
1861      * partition */
1862     salvageTo = haveRWvolume ? 0 : 1;
1863
1864 #ifdef AFS_NAMEI_ENV
1865     ino = FindLinkHandle(isp, nVols, allInodes);
1866     if (VALID_INO(ino)) {
1867         IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1868         fdP = IH_OPEN(VGLinkH);
1869     }
1870     if (!VALID_INO(ino) || fdP == NULL) {
1871         Log("%s link table for volume %u.\n",
1872             Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1873         if (Testing) {
1874             IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1875         } else {
1876             int i, j;
1877             struct ViceInodeInfo *ip;
1878             CreateLinkTable(isp, ino);
1879             fdP = IH_OPEN(VGLinkH);
1880             /* Sync fake 1 link counts to the link table, now that it exists */
1881             if (fdP) {
1882                 for (i = 0; i < nVols; i++) {
1883                         ip = allInodes + isp[i].index;
1884                          for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1885 #ifdef AFS_NT40_ENV
1886                                  nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1887 #else
1888                                  namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1889 #endif
1890                     }
1891                 }
1892             }
1893         }
1894     }
1895     if (fdP)
1896         FDH_REALLYCLOSE(fdP);
1897 #else
1898     IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1899 #endif
1900
1901     /* Salvage in reverse order--read/write volume last; this way any
1902      * Inodes not referenced by the time we salvage the read/write volume
1903      * can be picked up by the read/write volume */
1904     /* ACTUALLY, that's not done right now--the inodes just vanish */
1905     for (i = nVols - 1; i >= salvageTo; i--) {
1906         int rw = (i == 0);
1907         struct InodeSummary *lisp = &isp[i];
1908 #ifdef AFS_NAMEI_ENV
1909         /* If only the RO is present on this partition, the link table
1910          * shows up as a RW volume special file. Need to make sure the
1911          * salvager doesn't try to salvage the non-existent RW.
1912          */
1913         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1914             /* If this only special inode is the link table, continue */
1915             if (inodes->u.special.type == VI_LINKTABLE) {
1916                 haveRWvolume = 0;
1917                 continue;
1918             }
1919         }
1920 #endif
1921         if (!Showmode)
1922             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1923                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1924         /* Check inodes twice.  The second time do things seriously.  This
1925          * way the whole RO volume can be deleted, below, if anything goes wrong */
1926         for (check = 1; check >= 0; check--) {
1927             int deleteMe;
1928             if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1929                 == -1) {
1930                 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1931                 if (rw && deleteMe) {
1932                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
1933                                          * volume won't be called */
1934                     break;
1935                 }
1936                 if (!rw)
1937                     break;
1938             }
1939             if (rw && check == 1)
1940                 continue;
1941             if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1942                 MaybeZapVolume(lisp, "Vnode index", 0, check);
1943                 break;
1944             }
1945         }
1946     }
1947
1948     /* Fix actual inode counts */
1949     if (!Showmode) {
1950         Log("totalInodes %d\n",totalInodes);
1951         for (ip = inodes; totalInodes; ip++, totalInodes--) {
1952             static int TraceBadLinkCounts = 0;
1953 #ifdef AFS_NAMEI_ENV
1954             if (VGLinkH->ih_ino == ip->inodeNumber) {
1955                 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1956                 VGLinkH_p1 = ip->u.param[0];
1957                 continue;       /* Deal with this last. */
1958             }
1959 #endif
1960             if (ip->linkCount != 0 && TraceBadLinkCounts) {
1961                 TraceBadLinkCounts--;   /* Limit reports, per volume */
1962                 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]);
1963             }
1964             while (ip->linkCount > 0) {
1965                 /* below used to assert, not break */
1966                 if (!Testing) {
1967                     if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1968                         Log("idec failed. inode %s errno %d\n",
1969                             PrintInode(NULL, ip->inodeNumber), errno);
1970                         break;
1971                     }
1972                 }
1973                 ip->linkCount--;
1974             }
1975             while (ip->linkCount < 0) {
1976                 /* these used to be asserts */
1977                 if (!Testing) {
1978                     if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1979                         Log("iinc failed. inode %s errno %d\n",
1980                             PrintInode(NULL, ip->inodeNumber), errno);
1981                         break;
1982                     }
1983                 }
1984                 ip->linkCount++;
1985             }
1986         }
1987 #ifdef AFS_NAMEI_ENV
1988         while (dec_VGLinkH > 0) {
1989             if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1990                 Log("idec failed on link table, errno = %d\n", errno);
1991             }
1992             dec_VGLinkH--;
1993         }
1994         while (dec_VGLinkH < 0) {
1995             if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1996                 Log("iinc failed on link table, errno = %d\n", errno);
1997             }
1998             dec_VGLinkH++;
1999         }
2000 #endif
2001     }
2002     free(inodes);
2003     /* Directory consistency checks on the rw volume */
2004     if (haveRWvolume)
2005         SalvageVolume(isp, VGLinkH);
2006     IH_RELEASE(VGLinkH);
2007
2008     if (canfork && !debug) {
2009         ShowLog = 0;
2010         Exit(0);
2011     }
2012 }
2013
2014 int
2015 QuickCheck(register struct InodeSummary *isp, int nVols)
2016 {
2017     /* Check headers BEFORE forking */
2018     register int i;
2019     IHandle_t *h;
2020
2021     for (i = 0; i < nVols; i++) {
2022         struct VolumeSummary *vs = isp[i].volSummary;
2023         VolumeDiskData volHeader;
2024         if (!vs) {
2025             /* Don't salvage just because phantom rw volume is there... */
2026             /* (If a read-only volume exists, read/write inodes must also exist) */
2027             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2028                 continue;
2029             return 0;
2030         }
2031         IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2032         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2033             == sizeof(volHeader)
2034             && volHeader.stamp.magic == VOLUMEINFOMAGIC
2035             && volHeader.dontSalvage == DONT_SALVAGE
2036             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2037             if (volHeader.inUse != 0) {
2038                 volHeader.inUse = 0;
2039                 volHeader.inService = 1;
2040                 if (!Testing) {
2041                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2042                         != sizeof(volHeader)) {
2043                         IH_RELEASE(h);
2044                         return 0;
2045                     }
2046                 }
2047             }
2048             IH_RELEASE(h);
2049         } else {
2050             IH_RELEASE(h);
2051             return 0;
2052         }
2053     }
2054     return 1;
2055 }
2056
2057
2058 /* SalvageVolumeHeaderFile
2059  *
2060  * Salvage the top level V*.vol header file. Make sure the special files
2061  * exist and that there are no duplicates.
2062  *
2063  * Calls SalvageHeader for each possible type of volume special file.
2064  */
2065
2066 int
2067 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2068                         register struct ViceInodeInfo *inodes, int RW,
2069                         int check, int *deleteMe)
2070 {
2071     int i;
2072     register struct ViceInodeInfo *ip;
2073     int allinodesobsolete = 1;
2074     struct VolumeDiskHeader diskHeader;
2075     afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
2076     int *skip;
2077
2078     /* keeps track of special inodes that are probably 'good'; they are
2079      * referenced in the vol header, and are included in the given inodes
2080      * array */
2081     struct {
2082         int valid;
2083         Inode inode;
2084     } goodspecial[MAXINODETYPE];
2085
2086     if (deleteMe)
2087         *deleteMe = 0;
2088
2089     memset(goodspecial, 0, sizeof(goodspecial));
2090
2091     skip = malloc(isp->nSpecialInodes * sizeof(*skip));
2092     if (skip) {
2093         memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
2094     } else {
2095         Log("cannot allocate memory for inode skip array when salvaging "
2096             "volume %lu; not performing duplicate special inode recovery\n",
2097             afs_printable_uint32_lu(isp->volumeId));
2098         /* still try to perform the salvage; the skip array only does anything
2099          * if we detect duplicate special inodes */
2100     }
2101
2102     /*
2103      * First, look at the special inodes and see if any are referenced by
2104      * the existing volume header. If we find duplicate special inodes, we
2105      * can use this information to use the referenced inode (it's more
2106      * likely to be the 'good' one), and throw away the duplicates.
2107      */
2108     if (isp->volSummary && skip) {
2109         /* use tempHeader, so we can use the stuff[] array to easily index
2110          * into the isp->volSummary special inodes */
2111         memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2112
2113         for (i = 0; i < isp->nSpecialInodes; i++) {
2114             ip = &inodes[isp->index + i];
2115             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2116                 /* will get taken care of in a later loop */
2117                 continue;
2118             }
2119             if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2120                 goodspecial[ip->u.special.type-1].valid = 1;
2121                 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2122             }
2123         }
2124     }
2125
2126     memset(&tempHeader, 0, sizeof(tempHeader));
2127     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2128     tempHeader.stamp.version = VOLUMEHEADERVERSION;
2129     tempHeader.id = isp->volumeId;
2130     tempHeader.parent = isp->RWvolumeId;
2131
2132     /* Check for duplicates (inodes are sorted by type field) */
2133     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2134         ip = &inodes[isp->index + i];
2135         if (ip->u.special.type == (ip + 1)->u.special.type) {
2136             afs_ino_str_t stmp1, stmp2;
2137
2138             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2139                 /* Will be caught in the loop below */
2140                 continue;
2141             }
2142             if (!Showmode) {
2143                 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2144                     ip->u.special.type, isp->volumeId,
2145                     PrintInode(stmp1, ip->inodeNumber),
2146                     PrintInode(stmp2, (ip+1)->inodeNumber));
2147             }
2148             if (skip && goodspecial[ip->u.special.type-1].valid) {
2149                 Inode gi = goodspecial[ip->u.special.type-1].inode;
2150
2151                 if (!Showmode) {
2152                     Log("using special inode referenced by vol header (%s)\n",
2153                         PrintInode(stmp1, gi));
2154                 }
2155
2156                 /* the volume header references some special inode of
2157                  * this type in the inodes array; are we it? */
2158                 if (ip->inodeNumber != gi) {
2159                     skip[i] = 1;
2160                 } else if ((ip+1)->inodeNumber != gi) {
2161                     /* in case this is the last iteration; we need to
2162                      * make sure we check ip+1, too */
2163                     skip[i+1] = 1;
2164                 }
2165             } else {
2166                 if (!Showmode)
2167                     Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2168                 if (skip) {
2169                     free(skip);
2170                 }
2171                 return -1;
2172             }
2173         }
2174     }
2175     for (i = 0; i < isp->nSpecialInodes; i++) {
2176         ip = &inodes[isp->index + i];
2177         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2178             if (check) {
2179                 Log("Rubbish header inode %s of type %d\n",
2180                     PrintInode(NULL, ip->inodeNumber),
2181                     ip->u.special.type);
2182                 if (skip) {
2183                     free(skip);
2184                 }
2185                 return -1;
2186             }
2187             Log("Rubbish header inode %s of type %d; deleted\n",
2188                 PrintInode(NULL, ip->inodeNumber),
2189                 ip->u.special.type);
2190         } else if (!stuff[ip->u.special.type - 1].obsolete) {
2191             if (skip && skip[i]) {
2192                 if (orphans == ORPH_REMOVE) {
2193                     Log("Removing orphan special inode %s of type %d\n",
2194                         PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2195                     continue;
2196                 } else {
2197                     Log("Ignoring orphan special inode %s of type %d\n",
2198                         PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2199                     /* fall through to the ip->linkCount--; line below */
2200                 }
2201             } else {
2202                 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2203                 allinodesobsolete = 0;
2204             }
2205             if (!check && ip->u.special.type != VI_LINKTABLE)
2206                 ip->linkCount--;        /* Keep the inode around */
2207         }
2208     }
2209     if (skip) {
2210         free(skip);
2211     }
2212     skip = NULL;
2213
2214     if (allinodesobsolete) {
2215         if (deleteMe)
2216             *deleteMe = 1;
2217         return -1;
2218     }
2219
2220     if (!check)
2221         VGLinkH_cnt++;          /* one for every header. */
2222
2223     if (!RW && !check && isp->volSummary) {
2224         ClearROInUseBit(isp->volSummary);
2225         return 0;
2226     }
2227
2228     for (i = 0; i < MAXINODETYPE; i++) {
2229         if (stuff[i].inodeType == VI_LINKTABLE) {
2230             /* Gross hack: SalvageHeader does a bcmp on the volume header.
2231              * And we may have recreated the link table earlier, so set the
2232              * RW header as well.
2233              */
2234             if (VALID_INO(VGLinkH->ih_ino)) {
2235                 *stuff[i].inode = VGLinkH->ih_ino;
2236             }
2237             continue;
2238         }
2239         if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2240             return -1;
2241     }
2242
2243     if (isp->volSummary == NULL) {
2244         char path[64];
2245         char headerName[64];
2246         (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2247         (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2248         if (check) {
2249             Log("No header file for volume %u\n", isp->volumeId);
2250             return -1;
2251         }
2252         if (!Showmode)
2253             Log("No header file for volume %u; %screating %s\n",
2254                 isp->volumeId, (Testing ? "it would have been " : ""),
2255                 path);
2256         isp->volSummary = (struct VolumeSummary *)
2257             malloc(sizeof(struct VolumeSummary));
2258         isp->volSummary->fileName = ToString(headerName);
2259
2260         writefunc = VCreateVolumeDiskHeader;
2261     } else {
2262         char path[64];
2263         char headerName[64];
2264         /* hack: these two fields are obsolete... */
2265         isp->volSummary->header.volumeAcl = 0;
2266         isp->volSummary->header.volumeMountTable = 0;
2267
2268         if (memcmp
2269             (&isp->volSummary->header, &tempHeader,
2270              sizeof(struct VolumeHeader))) {
2271             /* We often remove the name before calling us, so we make a fake one up */
2272             if (isp->volSummary->fileName) {
2273                 strcpy(headerName, isp->volSummary->fileName);
2274             } else {
2275                 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2276                 isp->volSummary->fileName = ToString(headerName);
2277             }
2278             (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2279
2280             Log("Header file %s is damaged or no longer valid%s\n", path,
2281                 (check ? "" : "; repairing"));
2282             if (check)
2283                 return -1;
2284
2285             writefunc = VWriteVolumeDiskHeader;
2286         }
2287     }
2288     if (writefunc) {
2289         memcpy(&isp->volSummary->header, &tempHeader,
2290                sizeof(struct VolumeHeader));
2291         if (Testing) {
2292             if (!Showmode)
2293                 Log("It would have written a new header file for volume %u\n",
2294                     isp->volumeId);
2295         } else {
2296             afs_int32 code;
2297             VolumeHeaderToDisk(&diskHeader, &tempHeader);
2298             code = (*writefunc)(&diskHeader, fileSysPartition);
2299             if (code) {
2300                 Log("Error %ld writing volume header file for volume %lu\n",
2301                     afs_printable_int32_ld(code),
2302                     afs_printable_uint32_lu(diskHeader.id));
2303                 return -1;
2304             }
2305         }
2306     }
2307     IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2308             isp->volSummary->header.volumeInfo);
2309     return 0;
2310 }
2311
2312 int
2313 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2314               int *deleteMe)
2315 {
2316     union {
2317         VolumeDiskData volumeInfo;
2318         struct versionStamp fileHeader;
2319     } header;
2320     IHandle_t *specH;
2321     int recreate = 0;
2322     afs_int32 code;
2323     FdHandle_t *fdP;
2324
2325     if (sp->obsolete)
2326         return 0;
2327 #ifndef AFS_NAMEI_ENV
2328     if (sp->inodeType == VI_LINKTABLE)
2329         return 0;
2330 #endif
2331     if (*(sp->inode) == 0) {
2332         if (check) {
2333             Log("Missing inode in volume header (%s)\n", sp->description);
2334             return -1;
2335         }
2336         if (!Showmode)
2337             Log("Missing inode in volume header (%s); %s\n", sp->description,
2338                 (Testing ? "it would have recreated it" : "recreating"));
2339         if (!Testing) {
2340             *(sp->inode) =
2341                 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2342                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2343             if (!VALID_INO(*(sp->inode)))
2344                 Abort
2345                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2346                      sp->description, errno);
2347         }
2348         recreate = 1;
2349     }
2350
2351     IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2352     fdP = IH_OPEN(specH);
2353     if (OKToZap && (fdP == NULL) && BadError(errno)) {
2354         /* bail out early and destroy the volume */
2355         if (!Showmode)
2356             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2357         if (deleteMe)
2358             *deleteMe = 1;
2359         IH_RELEASE(specH);
2360         return -1;
2361     }
2362     if (fdP == NULL)
2363         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2364               sp->description, errno);
2365
2366     if (!recreate
2367         && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2368             || header.fileHeader.magic != sp->stamp.magic)) {
2369         if (check) {
2370             Log("Part of the header (%s) is corrupted\n", sp->description);
2371             FDH_REALLYCLOSE(fdP);
2372             IH_RELEASE(specH);
2373             return -1;
2374         }
2375         Log("Part of the header (%s) is corrupted; recreating\n",
2376             sp->description);
2377         recreate = 1;
2378     }
2379     if (sp->inodeType == VI_VOLINFO
2380         && header.volumeInfo.destroyMe == DESTROY_ME) {
2381         if (deleteMe)
2382             *deleteMe = 1;
2383         FDH_REALLYCLOSE(fdP);
2384         IH_RELEASE(specH);
2385         return -1;
2386     }
2387     if (recreate && !Testing) {
2388         if (check)
2389             Abort
2390                 ("Internal error: recreating volume header (%s) in check mode\n",
2391                  sp->description);
2392         code = FDH_TRUNC(fdP, 0);
2393         if (code == -1)
2394             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2395                   sp->description, errno);
2396
2397         /* The following code should be moved into vutil.c */
2398         if (sp->inodeType == VI_VOLINFO) {
2399             struct timeval tp;
2400             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2401             header.volumeInfo.stamp = sp->stamp;
2402             header.volumeInfo.id = isp->volumeId;
2403             header.volumeInfo.parentId = isp->RWvolumeId;
2404             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2405             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2406                 isp->volumeId, isp->volumeId);
2407             header.volumeInfo.inService = 0;
2408             header.volumeInfo.blessed = 0;
2409             /* The + 1000 is a hack in case there are any files out in venus caches */
2410             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2411             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
2412             header.volumeInfo.needsCallback = 0;
2413             gettimeofday(&tp, 0);
2414             header.volumeInfo.creationDate = tp.tv_sec;
2415             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2416                 Abort
2417                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2418                      sp->description, errno);
2419             }
2420             code =
2421                 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2422                           sizeof(header.volumeInfo));
2423             if (code != sizeof(header.volumeInfo)) {
2424                 if (code < 0)
2425                     Abort
2426                         ("Unable to write volume header file (%s) (errno = %d)\n",
2427                          sp->description, errno);
2428                 Abort("Unable to write entire volume header file (%s)\n",
2429                       sp->description);
2430             }
2431         } else {
2432             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2433                 Abort
2434                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2435                      sp->description, errno);
2436             }
2437             code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2438             if (code != sizeof(sp->stamp)) {
2439                 if (code < 0)
2440                     Abort
2441                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2442                          sp->description, errno);
2443                 Abort
2444                     ("Unable to write entire version stamp in volume header file (%s)\n",
2445                      sp->description);
2446             }
2447         }
2448     }
2449     FDH_REALLYCLOSE(fdP);
2450     IH_RELEASE(specH);
2451     if (sp->inodeType == VI_VOLINFO) {
2452         VolInfo = header.volumeInfo;
2453         if (check) {
2454             char update[25];
2455             if (VolInfo.updateDate) {
2456                 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2457                 if (!Showmode)
2458                     Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2459                         (Testing ? "it would have been " : ""), update);
2460             } else {
2461                 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2462                 if (!Showmode)
2463                     Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2464                         VolInfo.id, update);
2465             }
2466
2467         }
2468     }
2469
2470     return 0;
2471 }
2472
2473 int
2474 SalvageVnodes(register struct InodeSummary *rwIsp,
2475               register struct InodeSummary *thisIsp,
2476               register struct ViceInodeInfo *inodes, int check)
2477 {
2478     int ilarge, ismall, ioffset, RW, nInodes;
2479     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
2480     if (Showmode)
2481         return 0;
2482     RW = (rwIsp == thisIsp);
2483     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2484     ismall =
2485         SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2486                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2487     if (check && ismall == -1)
2488         return -1;
2489     ilarge =
2490         SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2491                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2492     return (ilarge == 0 && ismall == 0 ? 0 : -1);
2493 }
2494
2495 int
2496 SalvageIndex(Inode ino, VnodeClass class, int RW,
2497              register struct ViceInodeInfo *ip, int nInodes,
2498              struct VolumeSummary *volSummary, int check)
2499 {
2500     VolumeId volumeNumber;
2501     char buf[SIZEOF_LARGEDISKVNODE];
2502     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2503     int err = 0;
2504     StreamHandle_t *file;
2505     struct VnodeClassInfo *vcp;
2506     afs_sfsize_t size;
2507     afs_fsize_t vnodeLength;
2508     int vnodeIndex, nVnodes;
2509     afs_ino_str_t stmp1, stmp2;
2510     IHandle_t *handle;
2511     FdHandle_t *fdP;
2512
2513     volumeNumber = volSummary->header.id;
2514     IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2515     fdP = IH_OPEN(handle);
2516     assert(fdP != NULL);
2517     file = FDH_FDOPEN(fdP, "r+");
2518     assert(file != NULL);
2519     vcp = &VnodeClassInfo[class];
2520     size = OS_SIZE(fdP->fd_fd);
2521     assert(size != -1);
2522     nVnodes = (size / vcp->diskSize) - 1;
2523     if (nVnodes > 0) {
2524         assert((nVnodes + 1) * vcp->diskSize == size);
2525         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2526     } else {
2527         nVnodes = 0;
2528     }
2529     for (vnodeIndex = 0;
2530          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2531          nVnodes--, vnodeIndex++) {
2532         if (vnode->type != vNull) {
2533             int vnodeChanged = 0;
2534             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2535             /* Log programs that belong to root (potentially suid root);
2536              * don't bother for read-only or backup volumes */
2537 #ifdef  notdef                  /* This is done elsewhere */
2538             if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2539                 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);
2540 #endif
2541             if (VNDISK_GET_INO(vnode) == 0) {
2542                 if (RW) {
2543                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2544                     memset(vnode, 0, vcp->diskSize);
2545                     vnodeChanged = 1;
2546                 }
2547             } else {
2548                 if (vcp->magic != vnode->vnodeMagic) {
2549                     /* bad magic #, probably partially created vnode */
2550                     Log("Partially allocated vnode %d deleted.\n",
2551                         vnodeNumber);
2552                     memset(vnode, 0, vcp->diskSize);
2553                     vnodeChanged = 1;
2554                     goto vnodeDone;
2555                 }
2556                 /* ****** Should do a bit more salvage here:  e.g. make sure
2557                  * vnode type matches what it should be given the index */
2558                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2559 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2560  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2561  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2562  *                  }
2563  */
2564                     ip++;
2565                     nInodes--;
2566                 }
2567                 if (!RW) {
2568                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2569                         /* The following doesn't work, because the version number
2570                          * is not maintained correctly by the file server */
2571                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2572                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2573                          * break; */
2574                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2575                             break;
2576                         ip++;
2577                         nInodes--;
2578                     }
2579                 } else {
2580                     /* For RW volume, look for vnode with matching inode number;
2581                      * if no such match, take the first determined by our sort
2582                      * order */
2583                     register struct ViceInodeInfo *lip = ip;
2584                     register int lnInodes = nInodes;
2585                     while (lnInodes
2586                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2587                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2588                             ip = lip;
2589                             nInodes = lnInodes;
2590                             break;
2591                         }
2592                         lip++;
2593                         lnInodes--;
2594                     }
2595                 }
2596                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2597                     /* "Matching" inode */
2598                     if (RW) {
2599                         Unique vu, iu;
2600                         FileVersion vd, id;
2601                         vu = vnode->uniquifier;
2602                         iu = ip->u.vnode.vnodeUniquifier;
2603                         vd = vnode->dataVersion;
2604                         id = ip->u.vnode.inodeDataVersion;
2605                         /*
2606                          * Because of the possibility of the uniquifier overflows (> 4M)
2607                          * we compare them modulo the low 22-bits; we shouldn't worry
2608                          * about mismatching since they shouldn't to many old 
2609                          * uniquifiers of the same vnode...
2610                          */
2611                         if (IUnique(vu) != IUnique(iu)) {
2612                             if (!Showmode) {
2613                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2614                             }
2615
2616                             vnode->uniquifier = iu;
2617 #ifdef  AFS_3DISPARES
2618                             vnode->dataVersion = (id >= vd ?
2619                                                   /* 90% of 2.1M */
2620                                                   ((id - vd) >
2621                                                    1887437 ? vd : id) :
2622                                                   /* 90% of 2.1M */
2623                                                   ((vd - id) >
2624                                                    1887437 ? id : vd));
2625 #else
2626 #if defined(AFS_SGI_EXMAG)
2627                             vnode->dataVersion = (id >= vd ?
2628                                                   /* 90% of 16M */
2629                                                   ((id - vd) >
2630                                                    15099494 ? vd : id) :
2631                                                   /* 90% of 16M */
2632                                                   ((vd - id) >
2633                                                    15099494 ? id : vd));
2634 #else
2635                             vnode->dataVersion = (id > vd ? id : vd);
2636 #endif /* AFS_SGI_EXMAG */
2637 #endif /* AFS_3DISPARES */
2638                             vnodeChanged = 1;
2639                         } else {
2640                             /* don't bother checking for vd > id any more, since
2641                              * partial file transfers always result in this state,
2642                              * and you can't do much else anyway (you've already
2643                              * found the best data you can) */
2644 #ifdef  AFS_3DISPARES
2645                             if (!vnodeIsDirectory(vnodeNumber)
2646                                 && ((vd < id && (id - vd) < 1887437)
2647                                     || ((vd > id && (vd - id) > 1887437)))) {
2648 #else
2649 #if defined(AFS_SGI_EXMAG)
2650                             if (!vnodeIsDirectory(vnodeNumber)
2651                                 && ((vd < id && (id - vd) < 15099494)
2652                                     || ((vd > id && (vd - id) > 15099494)))) {
2653 #else
2654                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2655 #endif /* AFS_SGI_EXMAG */
2656 #endif
2657                                 if (!Showmode)
2658                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2659                                 vnode->dataVersion = id;
2660                                 vnodeChanged = 1;
2661                             }
2662                         }
2663                     }
2664                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2665                         if (check) {
2666                             if (!Showmode) {
2667                                 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);
2668                             }
2669                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2670                             err = -1;
2671                             goto zooks;
2672                         }
2673                         if (!Showmode) {
2674                             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);
2675                         }
2676                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2677                         vnodeChanged = 1;
2678                     }
2679                     VNDISK_GET_LEN(vnodeLength, vnode);
2680                     if (ip->byteCount != vnodeLength) {
2681                         if (check) {
2682                             if (!Showmode)
2683                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2684                             err = -1;
2685                             goto zooks;
2686                         }
2687                         if (!Showmode)
2688                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2689                         VNDISK_SET_LEN(vnode, ip->byteCount);
2690                         vnodeChanged = 1;
2691                     }
2692                     if (!check)
2693                         ip->linkCount--;        /* Keep the inode around */
2694                     ip++;
2695                     nInodes--;
2696                 } else {        /* no matching inode */
2697                     if (VNDISK_GET_INO(vnode) != 0
2698                         || vnode->type == vDirectory) {
2699                         /* No matching inode--get rid of the vnode */
2700                         if (check) {
2701                             if (VNDISK_GET_INO(vnode)) {
2702                                 if (!Showmode) {
2703                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2704                                 }
2705                             } else {
2706                                 if (!Showmode)
2707                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2708                             }
2709                             err = -1;
2710                             goto zooks;
2711                         }
2712                         if (VNDISK_GET_INO(vnode)) {
2713                             if (!Showmode) {
2714                                 time_t serverModifyTime = vnode->serverModifyTime;
2715                                 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));
2716                             }
2717                         } else {
2718                             if (!Showmode) {
2719                                 time_t serverModifyTime = vnode->serverModifyTime;
2720                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2721                             }
2722                         }
2723                         memset(vnode, 0, vcp->diskSize);
2724                         vnodeChanged = 1;
2725                     } else {
2726                         /* Should not reach here becuase we checked for 
2727                          * (inodeNumber == 0) above. And where we zero the vnode,
2728                          * we also goto vnodeDone.
2729                          */
2730                     }
2731                 }
2732                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2733                     ip++;
2734                     nInodes--;
2735                 }
2736             }                   /* VNDISK_GET_INO(vnode) != 0 */
2737           vnodeDone:
2738             assert(!(vnodeChanged && check));
2739             if (vnodeChanged && !Testing) {
2740                 assert(IH_IWRITE
2741                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2742                         (char *)vnode, vcp->diskSize)
2743                        == vcp->diskSize);
2744                 VolumeChanged = 1;      /* For break call back */
2745             }
2746         }
2747     }
2748   zooks:
2749     STREAM_CLOSE(file);
2750     FDH_CLOSE(fdP);
2751     IH_RELEASE(handle);
2752     return err;
2753 }
2754
2755 struct VnodeEssence *
2756 CheckVnodeNumber(VnodeId vnodeNumber)
2757 {
2758     VnodeClass class;
2759     struct VnodeInfo *vip;
2760     int offset;
2761
2762     class = vnodeIdToClass(vnodeNumber);
2763     vip = &vnodeInfo[class];
2764     offset = vnodeIdToBitNumber(vnodeNumber);
2765     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2766 }
2767
2768 void
2769 CopyOnWrite(register struct DirSummary *dir)
2770 {
2771     /* Copy the directory unconditionally if we are going to change it:
2772      * not just if was cloned.
2773      */
2774     struct VnodeDiskObject vnode;
2775     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2776     Inode oldinode, newinode;
2777     afs_sfsize_t code;
2778
2779     if (dir->copied || Testing)
2780         return;
2781     DFlush();                   /* Well justified paranoia... */
2782
2783     code =
2784         IH_IREAD(vnodeInfo[vLarge].handle,
2785                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2786                  sizeof(vnode));
2787     assert(code == sizeof(vnode));
2788     oldinode = VNDISK_GET_INO(&vnode);
2789     /* Increment the version number by a whole lot to avoid problems with
2790      * clients that were promised new version numbers--but the file server
2791      * crashed before the versions were written to disk.
2792      */
2793     newinode =
2794         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2795                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2796                   200);
2797     assert(VALID_INO(newinode));
2798     assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2799     vnode.cloned = 0;
2800     VNDISK_SET_INO(&vnode, newinode);
2801     code =
2802         IH_IWRITE(vnodeInfo[vLarge].handle,
2803                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2804                   sizeof(vnode));
2805     assert(code == sizeof(vnode));
2806
2807     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2808                         fileSysDevice, newinode);
2809     /* Don't delete the original inode right away, because the directory is
2810      * still being scanned.
2811      */
2812     dir->copied = 1;
2813 }
2814
2815 /*
2816  * This function should either successfully create a new dir, or give up 
2817  * and leave things the way they were.  In particular, if it fails to write 
2818  * the new dir properly, it should return w/o changing the reference to the 
2819  * old dir.
2820  */
2821 void
2822 CopyAndSalvage(register struct DirSummary *dir)
2823 {
2824     struct VnodeDiskObject vnode;
2825     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2826     Inode oldinode, newinode;
2827     DirHandle newdir;
2828     FdHandle_t *fdP;
2829     afs_int32 code;
2830     afs_sfsize_t lcode;
2831     afs_int32 parentUnique = 1;
2832     struct VnodeEssence *vnodeEssence;
2833     afs_fsize_t length;
2834
2835     if (Testing)
2836         return;
2837     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2838     lcode =
2839         IH_IREAD(vnodeInfo[vLarge].handle,
2840                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2841                  sizeof(vnode));
2842     assert(lcode == sizeof(vnode));
2843     oldinode = VNDISK_GET_INO(&vnode);
2844     /* Increment the version number by a whole lot to avoid problems with
2845      * clients that were promised new version numbers--but the file server
2846      * crashed before the versions were written to disk.
2847      */
2848     newinode =
2849         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2850                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2851                   200);
2852     assert(VALID_INO(newinode));
2853     SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2854
2855     /* Assign . and .. vnode numbers from dir and vnode.parent. 
2856      * The uniquifier for . is in the vnode.
2857      * The uniquifier for .. might be set to a bogus value of 1 and 
2858      * the salvager will later clean it up.
2859      */
2860     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2861         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2862     }
2863     code =
2864         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2865                    vnode.uniquifier,
2866                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2867                    parentUnique);
2868     if (code == 0)
2869         code = DFlush();
2870     if (code) {
2871         /* didn't really build the new directory properly, let's just give up. */
2872         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2873         Log("Directory salvage returned code %d, continuing.\n", code);
2874         if (code) {
2875             Log("also failed to decrement link count on new inode");
2876         }
2877         assert(1 == 2);
2878     }
2879     Log("Checking the results of the directory salvage...\n");
2880     if (!DirOK(&newdir)) {
2881         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2882         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2883         assert(code == 0);
2884         assert(1 == 2);
2885     }
2886     vnode.cloned = 0;
2887     VNDISK_SET_INO(&vnode, newinode);
2888     length = Length(&newdir);
2889     VNDISK_SET_LEN(&vnode, length);
2890     lcode =
2891         IH_IWRITE(vnodeInfo[vLarge].handle,
2892                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2893                   sizeof(vnode));
2894     assert(lcode == sizeof(vnode));
2895 #if 0
2896 #ifdef AFS_NT40_ENV
2897     nt_sync(fileSysDevice);
2898 #else
2899     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2900                                  * an open FD on the file itself to fsync.
2901                                  */
2902 #endif
2903 #else
2904     vnodeInfo[vLarge].handle->ih_synced = 1;
2905 #endif
2906     /* make sure old directory file is really closed */
2907     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2908     FDH_REALLYCLOSE(fdP);
2909     
2910     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2911     assert(code == 0);
2912     dir->dirHandle = newdir;
2913 }
2914
2915 int
2916 JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
2917            afs_int32 unique)
2918 {
2919     struct DirSummary *dir = (struct DirSummary *)dirVal;
2920     struct VnodeEssence *vnodeEssence;
2921     afs_int32 dirOrphaned, todelete;
2922
2923     dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2924
2925     vnodeEssence = CheckVnodeNumber(vnodeNumber);
2926     if (vnodeEssence == NULL) {
2927         if (!Showmode) {
2928             Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2929         }
2930         if (!Testing) {
2931             CopyOnWrite(dir);
2932             assert(Delete(&dir->dirHandle, name) == 0);
2933         }
2934         return 0;
2935     }
2936 #ifdef AFS_AIX_ENV
2937 #ifndef AFS_NAMEI_ENV
2938     /* On AIX machines, don't allow entries to point to inode 0. That is a special 
2939      * mount inode for the partition. If this inode were deleted, it would crash
2940      * the machine.
2941      */
2942     if (vnodeEssence->InodeNumber == 0) {
2943         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"));
2944         if (!Testing) {
2945             CopyOnWrite(dir);
2946             assert(Delete(&dir->dirHandle, name) == 0);
2947         }
2948         return 0;
2949     }
2950 #endif
2951 #endif
2952
2953     if (!(vnodeNumber & 1) && !Showmode
2954         && !(vnodeEssence->count || vnodeEssence->unique
2955              || vnodeEssence->modeBits)) {
2956         Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2957             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2958             vnodeNumber, unique,
2959             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2960              ""));
2961         if (!unique) {
2962             if (!Testing) {
2963                 CopyOnWrite(dir);
2964                 assert(Delete(&dir->dirHandle, name) == 0);
2965             }
2966             return 0;
2967         }
2968     }
2969
2970     /* Check if the Uniquifiers match. If not, change the directory entry
2971      * so its unique matches the vnode unique. Delete if the unique is zero
2972      * or if the directory is orphaned.
2973      */
2974     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2975         if (!vnodeEssence->unique
2976             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2977             /* This is an orphaned directory. Don't delete the . or ..
2978              * entry. Otherwise, it will get created in the next 
2979              * salvage and deleted again here. So Just skip it.
2980              */
2981             return 0;
2982         }
2983
2984         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2985
2986         if (!Showmode) {
2987             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")));
2988         }
2989         if (!Testing) {
2990             AFSFid fid;
2991             fid.Vnode = vnodeNumber;
2992             fid.Unique = vnodeEssence->unique;
2993             CopyOnWrite(dir);
2994             assert(Delete(&dir->dirHandle, name) == 0);
2995             if (!todelete)
2996                 assert(Create(&dir->dirHandle, name, &fid) == 0);
2997         }
2998         if (todelete)
2999             return 0;           /* no need to continue */
3000     }
3001
3002     if (strcmp(name, ".") == 0) {
3003         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
3004             AFSFid fid;
3005             if (!Showmode)
3006                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3007             if (!Testing) {
3008                 CopyOnWrite(dir);
3009                 assert(Delete(&dir->dirHandle, ".") == 0);
3010                 fid.Vnode = dir->vnodeNumber;
3011                 fid.Unique = dir->unique;
3012                 assert(Create(&dir->dirHandle, ".", &fid) == 0);
3013             }
3014
3015             vnodeNumber = fid.Vnode;    /* Get the new Essence */
3016             unique = fid.Unique;
3017             vnodeEssence = CheckVnodeNumber(vnodeNumber);
3018         }
3019         dir->haveDot = 1;
3020     } else if (strcmp(name, "..") == 0) {
3021         AFSFid pa;
3022         if (dir->parent) {
3023             struct VnodeEssence *dotdot;
3024             pa.Vnode = dir->parent;
3025             dotdot = CheckVnodeNumber(pa.Vnode);
3026             assert(dotdot != NULL);     /* XXX Should not be assert */
3027             pa.Unique = dotdot->unique;
3028         } else {
3029             pa.Vnode = dir->vnodeNumber;
3030             pa.Unique = dir->unique;
3031         }
3032         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
3033             if (!Showmode)
3034                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3035             if (!Testing) {
3036                 CopyOnWrite(dir);
3037                 assert(Delete(&dir->dirHandle, "..") == 0);
3038                 assert(Create(&dir->dirHandle, "..", &pa) == 0);
3039             }
3040
3041             vnodeNumber = pa.Vnode;     /* Get the new Essence */
3042             unique = pa.Unique;
3043             vnodeEssence = CheckVnodeNumber(vnodeNumber);
3044         }
3045         dir->haveDotDot = 1;
3046     } else if (strncmp(name, ".__afs", 6) == 0) {
3047         if (!Showmode) {
3048             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);
3049         }
3050         if (!Testing) {
3051             CopyOnWrite(dir);
3052             assert(Delete(&dir->dirHandle, name) == 0);
3053         }
3054         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
3055         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
3056         return 0;
3057     } else {
3058         if (ShowSuid && (vnodeEssence->modeBits & 06000))
3059             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);
3060         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
3061             && !(vnodeEssence->modeBits & 0111)) {
3062             int code, size;
3063             char buf[1025];
3064             IHandle_t *ihP;
3065             FdHandle_t *fdP;
3066
3067             IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3068                     vnodeEssence->InodeNumber);
3069             fdP = IH_OPEN(ihP);
3070             if (fdP == NULL) {
3071                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
3072                 IH_RELEASE(ihP);
3073                 return 0;
3074             }
3075             size = FDH_SIZE(fdP);
3076             if (size < 0) {
3077                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
3078                 FDH_REALLYCLOSE(fdP);
3079                 IH_RELEASE(ihP);
3080                 return 0;
3081             }
3082         
3083             if (size > 1024)
3084                 size = 1024;
3085             code = FDH_READ(fdP, buf, size);
3086             if (code == size) {
3087                 buf[size] = '\0';
3088                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
3089                     Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
3090                         dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
3091                         Testing ? "would convert" : "converted");
3092                     vnodeEssence->modeBits |= 0111;
3093                     vnodeEssence->changed = 1;
3094                 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3095                     dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3096                     dir->name ? dir->name : "??", name, buf);
3097             } else {
3098                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3099                     dir->vname, vnodeNumber, size, code);
3100             }
3101             FDH_REALLYCLOSE(fdP);
3102             IH_RELEASE(ihP);
3103         }
3104         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3105             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);
3106         if (vnodeIdToClass(vnodeNumber) == vLarge
3107             && vnodeEssence->name == NULL) {
3108             char *n;
3109             if ((n = (char *)malloc(strlen(name) + 1)))
3110                 strcpy(n, name);
3111             vnodeEssence->name = n;
3112         }
3113
3114         /* The directory entry points to the vnode. Check to see if the
3115          * vnode points back to the directory. If not, then let the 
3116          * directory claim it (else it might end up orphaned). Vnodes 
3117          * already claimed by another directory are deleted from this
3118          * directory: hardlinks to the same vnode are not allowed
3119          * from different directories.
3120          */
3121         if (vnodeEssence->parent != dir->vnodeNumber) {
3122             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3123                 /* Vnode does not point back to this directory.
3124                  * Orphaned dirs cannot claim a file (it may belong to
3125                  * another non-orphaned dir).
3126                  */
3127                 if (!Showmode) {
3128                     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);
3129                 }
3130                 vnodeEssence->parent = dir->vnodeNumber;
3131                 vnodeEssence->changed = 1;
3132             } else {
3133                 /* Vnode was claimed by another directory */
3134                 if (!Showmode) {
3135                     if (dirOrphaned) {
3136                         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 " : ""));
3137                     } else if (vnodeNumber == 1) {
3138                         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 " : ""));
3139                     } else {
3140                         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 " : ""));
3141                     }
3142                 }
3143                 if (!Testing) {
3144                     CopyOnWrite(dir);
3145                     assert(Delete(&dir->dirHandle, name) == 0);
3146                 }
3147                 return 0;
3148             }
3149         }
3150         /* This directory claims the vnode */
3151         vnodeEssence->claimed = 1;
3152     }
3153     vnodeEssence->count--;
3154     return 0;
3155 }
3156
3157 void
3158 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3159 {
3160     register struct VnodeInfo *vip = &vnodeInfo[class];
3161     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3162     char buf[SIZEOF_LARGEDISKVNODE];
3163     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3164     afs_sfsize_t size;
3165     StreamHandle_t *file;
3166     int vnodeIndex;
3167     int nVnodes;
3168     FdHandle_t *fdP;
3169
3170     IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3171     fdP = IH_OPEN(vip->handle);
3172     assert(fdP != NULL);
3173     file = FDH_FDOPEN(fdP, "r+");
3174     assert(file != NULL);
3175     size = OS_SIZE(fdP->fd_fd);
3176     assert(size != -1);
3177     vip->nVnodes = (size / vcp->diskSize) - 1;
3178     if (vip->nVnodes > 0) {
3179         assert((vip->nVnodes + 1) * vcp->diskSize == size);
3180         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3181         assert((vip->vnodes = (struct VnodeEssence *)
3182                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3183         if (class == vLarge) {
3184             assert((vip->inodes = (Inode *)
3185                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3186         } else {
3187             vip->inodes = NULL;
3188         }
3189     } else {
3190         vip->nVnodes = 0;
3191         vip->vnodes = NULL;
3192         vip->inodes = NULL;
3193     }
3194     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3195     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3196          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3197          nVnodes--, vnodeIndex++) {
3198         if (vnode->type != vNull) {
3199             register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3200             afs_fsize_t vnodeLength;
3201             vip->nAllocatedVnodes++;
3202             vep->count = vnode->linkCount;
3203             VNDISK_GET_LEN(vnodeLength, vnode);
3204             vep->blockCount = nBlocks(vnodeLength);
3205             vip->volumeBlockCount += vep->blockCount;
3206             vep->parent = vnode->parent;
3207             vep->unique = vnode->uniquifier;
3208             if (*maxu < vnode->uniquifier)
3209                 *maxu = vnode->uniquifier;
3210             vep->modeBits = vnode->modeBits;
3211             vep->InodeNumber = VNDISK_GET_INO(vnode);
3212             vep->type = vnode->type;
3213             vep->author = vnode->author;
3214             vep->owner = vnode->owner;
3215             vep->group = vnode->group;
3216             if (vnode->type == vDirectory) {
3217                 if (class != vLarge) {
3218                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3219                     vip->nAllocatedVnodes--;
3220                     memset(vnode, 0, sizeof(vnode));
3221                     IH_IWRITE(vnodeInfo[vSmall].handle,
3222                               vnodeIndexOffset(vcp, vnodeNumber),
3223                               (char *)&vnode, sizeof(vnode));
3224                     VolumeChanged = 1;
3225                 } else
3226                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3227             }
3228         }
3229     }
3230     STREAM_CLOSE(file);
3231     FDH_CLOSE(fdP);
3232 }
3233
3234 static char *
3235 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3236 {
3237     struct VnodeEssence *parentvp;
3238
3239     if (vnode == 1) {
3240         strcpy(path, ".");
3241         return path;
3242     }
3243     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3244         && GetDirName(vp->parent, parentvp, path)) {
3245         strcat(path, "/");
3246         strcat(path, vp->name);
3247         return path;
3248     }
3249     return 0;
3250 }
3251
3252 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3253  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3254  */
3255 static int
3256 IsVnodeOrphaned(VnodeId vnode)
3257 {
3258     struct VnodeEssence *vep;
3259
3260     if (vnode == 0)
3261         return (1);             /* Vnode zero does not exist */
3262     if (vnode == 1)
3263         return (0);             /* The root dir vnode is always claimed */
3264     vep = CheckVnodeNumber(vnode);      /* Get the vnode essence */
3265     if (!vep || !vep->claimed)
3266         return (1);             /* Vnode is not claimed - it is orphaned */
3267
3268     return (IsVnodeOrphaned(vep->parent));
3269 }
3270
3271 void
3272 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3273            IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3274            int *rootdirfound)
3275 {
3276     static struct DirSummary dir;
3277     static struct DirHandle dirHandle;
3278     struct VnodeEssence *parent;
3279     static char path[MAXPATHLEN];
3280     int dirok, code;
3281
3282     if (dirVnodeInfo->vnodes[i].salvaged)
3283         return;                 /* already salvaged */
3284
3285     dir.rwVid = rwVid;
3286     dirVnodeInfo->vnodes[i].salvaged = 1;
3287
3288     if (dirVnodeInfo->inodes[i] == 0)
3289         return;                 /* Not allocated to a directory */
3290
3291     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3292         if (dirVnodeInfo->vnodes[i].parent) {
3293             Log("Bad parent, vnode 1; %s...\n",
3294                 (Testing ? "skipping" : "salvaging"));
3295             dirVnodeInfo->vnodes[i].parent = 0;
3296             dirVnodeInfo->vnodes[i].changed = 1;
3297         }
3298     } else {
3299         parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3300         if (parent && parent->salvaged == 0)
3301             SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3302                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3303                        rootdir, rootdirfound);
3304     }
3305
3306     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3307     dir.unique = dirVnodeInfo->vnodes[i].unique;
3308     dir.copied = 0;
3309     dir.vname = name;
3310     dir.parent = dirVnodeInfo->vnodes[i].parent;
3311     dir.haveDot = dir.haveDotDot = 0;
3312     dir.ds_linkH = alinkH;
3313     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3314                         dirVnodeInfo->inodes[i]);
3315
3316     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3317     if (!dirok) {
3318         if (!RebuildDirs) {
3319             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3320                 (Testing ? "skipping" : "salvaging"));
3321         }
3322         if (!Testing) {
3323             CopyAndSalvage(&dir);
3324             dirok = 1;
3325         }
3326     }
3327     dirHandle = dir.dirHandle;
3328
3329     dir.name =
3330         GetDirName(bitNumberToVnodeNumber(i, vLarge),
3331                    &dirVnodeInfo->vnodes[i], path);
3332
3333     if (dirok) {
3334         /* If enumeration failed for random reasons, we will probably delete
3335          * too much stuff, so we guard against this instead.
3336          */
3337         assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3338     }
3339
3340     /* Delete the old directory if it was copied in order to salvage.
3341      * CopyOnWrite has written the new inode # to the disk, but we still
3342      * have the old one in our local structure here.  Thus, we idec the
3343      * local dude.
3344      */
3345     DFlush();
3346     if (dir.copied && !Testing) {
3347         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3348         assert(code == 0);
3349         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3350     }
3351
3352     /* Remember rootdir DirSummary _after_ it has been judged */
3353     if (dir.vnodeNumber == 1 && dir.unique == 1) {
3354         memcpy(rootdir, &dir, sizeof(struct DirSummary));
3355         *rootdirfound = 1;
3356     }
3357
3358     return;
3359 }
3360
3361 /**
3362  * Get a new FID that can be used to create a new file.
3363  *
3364  * @param[in] volHeader vol header for the volume
3365  * @param[in] class     what type of vnode we'll be creating (vLarge or vSmall)
3366  * @param[out] afid     the FID that we can use (only Vnode and Unique are set)
3367  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3368  *                          updated to the new max unique if we create a new
3369  *                          vnode
3370  */
3371 static void
3372 GetNewFID(VolumeDiskData *volHeader, VnodeClass class, AFSFid *afid,
3373           Unique *maxunique)
3374 {
3375     int i;
3376     for (i = 0; i < vnodeInfo[class].nVnodes; i++) {
3377         if (vnodeInfo[class].vnodes[i].type == vNull) {
3378             break;
3379         }
3380     }
3381     if (i == vnodeInfo[class].nVnodes) {
3382         /* no free vnodes; make a new one */
3383         vnodeInfo[class].nVnodes++;
3384         vnodeInfo[class].vnodes = realloc(vnodeInfo[class].vnodes,
3385                                           sizeof(struct VnodeEssence) * (i+1));
3386         vnodeInfo[class].vnodes[i].type = vNull;
3387     }
3388
3389     afid->Vnode = bitNumberToVnodeNumber(i, class);
3390
3391     if (volHeader->uniquifier < (*maxunique + 1)) {
3392         /* header uniq is bad; it will get bumped by 2000 later */
3393         afid->Unique = *maxunique + 1 + 2000;
3394         (*maxunique)++;
3395     } else {
3396         /* header uniq seems okay; just use that */
3397         afid->Unique = *maxunique = volHeader->uniquifier++;
3398     }
3399 }
3400
3401 /**
3402  * Create a vnode for a README file explaining not to use a recreated-root vol.
3403  *
3404  * @param[in] volHeader vol header for the volume
3405  * @param[in] alinkH    ihandle for i/o for the volume
3406  * @param[in] vid       volume id
3407  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3408  *                          updated to the new max unique if we create a new
3409  *                          vnode
3410  * @param[out] afid     FID for the new readme vnode
3411  * @param[out] ainode   the inode for the new readme file
3412  *
3413  * @return operation status
3414  *  @retval 0 success
3415  *  @retval -1 error
3416  */
3417 static int
3418 CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
3419              VolumeId vid, Unique *maxunique, AFSFid *afid, Inode *ainode)
3420 {
3421     Inode readmeinode;
3422     struct VnodeDiskObject *rvnode = NULL;
3423     afs_sfsize_t bytes;
3424     IHandle_t *readmeH = NULL;
3425     struct VnodeEssence *vep;
3426     afs_fsize_t length;
3427     time_t now = time(NULL);
3428
3429     /* Try to make the note brief, but informative. Only administrators should
3430      * be able to read this file at first, so we can hopefully assume they
3431      * know what AFS is, what a volume is, etc. */
3432     char readme[] =
3433 "This volume has been salvaged, but has lost its original root directory.\n"