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