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