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