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