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