e5daf80fb10ca6e98df107f7f781291e7d5cea49
[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 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
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         if (unlink(path)) {
884             Log("Unable to unlink %s (errno = %d)\n", path, errno);
885         }
886     }
887     vsp->fileName = 0;
888 }
889
890 int
891 CompareInodes(const void *_p1, const void *_p2)
892 {
893     register const struct ViceInodeInfo *p1 = _p1;
894     register const struct ViceInodeInfo *p2 = _p2;
895     if (p1->u.vnode.vnodeNumber == INODESPECIAL
896         || p2->u.vnode.vnodeNumber == INODESPECIAL) {
897         VolumeId p1rwid, p2rwid;
898         p1rwid =
899             (p1->u.vnode.vnodeNumber ==
900              INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
901         p2rwid =
902             (p2->u.vnode.vnodeNumber ==
903              INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
904         if (p1rwid < p2rwid)
905             return -1;
906         if (p1rwid > p2rwid)
907             return 1;
908         if (p1->u.vnode.vnodeNumber == INODESPECIAL
909             && p2->u.vnode.vnodeNumber == INODESPECIAL) {
910             if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
911                 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
912             if (p1->u.vnode.volumeId == p1rwid)
913                 return -1;
914             if (p2->u.vnode.volumeId == p2rwid)
915                 return 1;
916             return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
917         }
918         if (p1->u.vnode.vnodeNumber != INODESPECIAL)
919             return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
920         return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
921     }
922     if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
923         return -1;
924     if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
925         return 1;
926     if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
927         return -1;
928     if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
929         return 1;
930     /* The following tests are reversed, so that the most desirable
931      * of several similar inodes comes first */
932     if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
933 #ifdef  AFS_3DISPARES
934         if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
935             p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
936             return 1;
937 #endif
938 #ifdef  AFS_SGI_EXMAG
939         if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
940             p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
941             return 1;
942 #endif
943         return -1;
944     }
945     if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
946 #ifdef  AFS_3DISPARES
947         if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
948             p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
949             return -1;
950 #endif
951 #ifdef  AFS_SGI_EXMAG
952         if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
953             p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
954             return 1;
955 #endif
956         return 1;
957     }
958     if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
959 #ifdef  AFS_3DISPARES
960         if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
961             p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
962             return 1;
963 #endif
964 #ifdef  AFS_SGI_EXMAG
965         if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
966             p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
967             return 1;
968 #endif
969         return -1;
970     }
971     if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
972 #ifdef  AFS_3DISPARES
973         if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
974             p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
975             return -1;
976 #endif
977 #ifdef  AFS_SGI_EXMAG
978         if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
979             p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
980             return 1;
981 #endif
982         return 1;
983     }
984     return 0;
985 }
986
987 void
988 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
989                   register struct InodeSummary *summary)
990 {
991     VolumeId volume = ip->u.vnode.volumeId;
992     VolumeId rwvolume = volume;
993     register int n, nSpecial;
994     register Unique maxunique;
995     n = nSpecial = 0;
996     maxunique = 0;
997     while (maxInodes-- && volume == ip->u.vnode.volumeId) {
998         n++;
999         if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1000             nSpecial++;
1001             rwvolume = ip->u.special.parentId;
1002             /* This isn't quite right, as there could (in error) be different
1003              * parent inodes in different special vnodes */
1004         } else {
1005             if (maxunique < ip->u.vnode.vnodeUniquifier)
1006                 maxunique = ip->u.vnode.vnodeUniquifier;
1007         }
1008         ip++;
1009     }
1010     summary->volumeId = volume;
1011     summary->RWvolumeId = rwvolume;
1012     summary->nInodes = n;
1013     summary->nSpecialInodes = nSpecial;
1014     summary->maxUniquifier = maxunique;
1015 }
1016
1017 int
1018 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1019 {
1020     if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1021         return (inodeinfo->u.special.parentId == singleVolumeNumber);
1022     return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1023 }
1024
1025 /* GetInodeSummary
1026  *
1027  * Collect list of inodes in file named by path. If a truly fatal error,
1028  * unlink the file and abort. For lessor errors, return -1. The file will
1029  * be unlinked by the caller.
1030  */
1031 int
1032 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1033 {
1034     struct afs_stat status;
1035     int forceSal, err;
1036     struct ViceInodeInfo *ip;
1037     struct InodeSummary summary;
1038     char summaryFileName[50];
1039     FILE *summaryFile;
1040 #ifdef AFS_NT40_ENV
1041     char *dev = fileSysPath;
1042     char *wpath = fileSysPath;
1043 #else
1044     char *dev = fileSysDeviceName;
1045     char *wpath = filesysfulldev;
1046 #endif
1047     char *part = fileSysPath;
1048     char *tdir;
1049
1050     /* This file used to come from vfsck; cobble it up ourselves now... */
1051     if ((err =
1052          ListViceInodes(dev, fileSysPath, path,
1053                         singleVolumeNumber ? OnlyOneVolume : 0,
1054                         singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1055         if (err == -2) {
1056             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);
1057             return -1;
1058         }
1059         unlink(path);
1060         Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1061     }
1062     if (forceSal && !ForceSalvage) {
1063         Log("***Forced salvage of all volumes on this partition***\n");
1064         ForceSalvage = 1;
1065     }
1066     inodeFd = afs_open(path, O_RDWR);
1067     if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1068         unlink(path);
1069         Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1070     }
1071     tdir = (tmpdir ? tmpdir : part);
1072 #ifdef AFS_NT40_ENV
1073     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
1074     (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1075 #else
1076     (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1077                        "%s/salvage.temp.%d", tdir, getpid());
1078 #endif
1079     summaryFile = afs_fopen(summaryFileName, "a+");
1080     if (summaryFile == NULL) {
1081         close(inodeFd);
1082         unlink(path);
1083         Abort("Unable to create inode summary file\n");
1084     }
1085     if (!canfork || debug || Fork() == 0) {
1086         int nInodes;
1087         unsigned long st_size=(unsigned long) status.st_size;
1088         nInodes = st_size / sizeof(struct ViceInodeInfo);
1089         if (nInodes == 0) {
1090             fclose(summaryFile);
1091             close(inodeFd);
1092             unlink(summaryFileName);
1093             if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
1094                 RemoveTheForce(fileSysPath);
1095             else {
1096                 struct VolumeSummary *vsp;
1097                 int i;
1098
1099                 GetVolumeSummary(singleVolumeNumber);
1100
1101                 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1102                     if (vsp->fileName)
1103                         DeleteExtraVolumeHeaderFile(vsp);
1104                 }
1105             }
1106             Log("%s vice inodes on %s; not salvaged\n",
1107                 singleVolumeNumber ? "No applicable" : "No", dev);
1108             return -1;
1109         }
1110         ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1111         if (ip == NULL) {
1112             fclose(summaryFile);
1113             close(inodeFd);
1114             unlink(path);
1115             unlink(summaryFileName);
1116             Abort
1117                 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1118                  dev);
1119         }
1120         if (read(inodeFd, ip, st_size) != st_size) {
1121             fclose(summaryFile);
1122             close(inodeFd);
1123             unlink(path);
1124             unlink(summaryFileName);
1125             Abort("Unable to read inode table; %s not salvaged\n", dev);
1126         }
1127         qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1128         if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1129             || write(inodeFd, ip, st_size) != st_size) {
1130             fclose(summaryFile);
1131             close(inodeFd);
1132             unlink(path);
1133             unlink(summaryFileName);
1134             Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1135         }
1136         summary.index = 0;
1137         while (nInodes) {
1138             CountVolumeInodes(ip, nInodes, &summary);
1139             if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1140                 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1141                 fclose(summaryFile);
1142                 close(inodeFd);
1143                 return -1;
1144             }
1145             summary.index += (summary.nInodes);
1146             nInodes -= summary.nInodes;
1147             ip += summary.nInodes;
1148         }
1149         /* Following fflush is not fclose, because if it was debug mode would not work */
1150         if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1151             Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1152             fclose(summaryFile);
1153             close(inodeFd);
1154             return -1;
1155         }
1156         if (canfork && !debug) {
1157             ShowLog = 0;
1158             Exit(0);
1159         }
1160     } else {
1161         if (Wait("Inode summary") == -1) {
1162             fclose(summaryFile);
1163             close(inodeFd);
1164             unlink(path);
1165             unlink(summaryFileName);
1166             Exit(1);            /* salvage of this partition aborted */
1167         }
1168     }
1169     assert(afs_fstat(fileno(summaryFile), &status) != -1);
1170     if (status.st_size != 0) {
1171         int ret;
1172         unsigned long st_status=(unsigned long)status.st_size;
1173         inodeSummary = (struct InodeSummary *)malloc(st_status);
1174         assert(inodeSummary != NULL);
1175         /* For GNU we need to do lseek to get the file pointer moved. */
1176         assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1177         ret = read(fileno(summaryFile), inodeSummary, st_status);
1178         assert(ret == st_status);
1179     }
1180     nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1181     Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1182     fclose(summaryFile);
1183     close(inodeFd);
1184     unlink(summaryFileName);
1185     return 0;
1186 }
1187
1188 /* Comparison routine for volume sort.
1189    This is setup so that a read-write volume comes immediately before
1190    any read-only clones of that volume */
1191 int
1192 CompareVolumes(const void *_p1, const void *_p2)
1193 {
1194     register const struct VolumeSummary *p1 = _p1;
1195     register const struct VolumeSummary *p2 = _p2;
1196     if (p1->header.parent != p2->header.parent)
1197         return p1->header.parent < p2->header.parent ? -1 : 1;
1198     if (p1->header.id == p1->header.parent)     /* p1 is rw volume */
1199         return -1;
1200     if (p2->header.id == p2->header.parent)     /* p2 is rw volume */
1201         return 1;
1202     return p1->header.id < p2->header.id ? -1 : 1;      /* Both read-only */
1203 }
1204
1205 void
1206 GetVolumeSummary(VolumeId singleVolumeNumber)
1207 {
1208     DIR *dirp = NULL;
1209     afs_int32 nvols = 0;
1210     struct VolumeSummary *vsp, vs;
1211     struct VolumeDiskHeader diskHeader;
1212     struct dirent *dp;
1213
1214     /* Get headers from volume directory */
1215     dirp = opendir(fileSysPath);
1216     if (dirp  == NULL)
1217         Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1218     if (!singleVolumeNumber) {
1219         while ((dp = readdir(dirp))) {
1220             char *p = dp->d_name;
1221             p = strrchr(dp->d_name, '.');
1222             if (p != NULL && strcmp(p, VHDREXT) == 0) {
1223                 int fd;
1224                 char name[64];
1225                 sprintf(name, "%s/%s", fileSysPath, dp->d_name);
1226                 if ((fd = afs_open(name, O_RDONLY)) != -1
1227                     && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1228                     == sizeof(diskHeader)
1229                     && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1230                     DiskToVolumeHeader(&vs.header, &diskHeader);
1231                     nvols++;
1232                 }
1233                 close(fd);
1234             }
1235         }
1236 #ifdef AFS_NT40_ENV
1237         closedir(dirp);
1238         dirp = opendir(".");    /* No rewinddir for NT */
1239 #else
1240         rewinddir(dirp);
1241 #endif
1242         if (!nvols)
1243             nvols = 1;
1244         volumeSummaryp =
1245             (struct VolumeSummary *)malloc(nvols *
1246                                            sizeof(struct VolumeSummary));
1247     } else
1248         volumeSummaryp =
1249             (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1250     assert(volumeSummaryp != NULL);
1251
1252     nVolumes = 0;
1253     vsp = volumeSummaryp;
1254     while ((dp = readdir(dirp))) {
1255         char *p = dp->d_name;
1256         p = strrchr(dp->d_name, '.');
1257         if (p != NULL && strcmp(p, VHDREXT) == 0) {
1258             int error = 0;
1259             int fd;
1260             char name[64];
1261             sprintf(name, "%s/%s", fileSysPath, dp->d_name);
1262             if ((fd = afs_open(name, O_RDONLY)) == -1
1263                 || read(fd, &diskHeader, sizeof(diskHeader))
1264                 != sizeof(diskHeader)
1265                 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1266                 error = 1;
1267             }
1268             close(fd);
1269             if (error) {
1270                 if (!singleVolumeNumber) {
1271                     if (!Showmode)
1272                         Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1273                     if (!Testing) {
1274                         if (unlink(name)) {
1275                             Log("Unable to unlink %s (errno = %d)\n", name, errno);
1276                         }
1277                     }
1278                 }
1279             } else {
1280                 char nameShouldBe[64];
1281                 DiskToVolumeHeader(&vsp->header, &diskHeader);
1282                 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1283                     && vsp->header.parent != singleVolumeNumber) {
1284                     if (programType == salvageServer) {
1285 #ifdef SALVSYNC_BUILD_CLIENT
1286                         Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1287                             vsp->header.id, vsp->header.parent);
1288                         if (SALVSYNC_LinkVolume(vsp->header.parent,
1289                                                 vsp->header.id,
1290                                                 fileSysPartition->name,
1291                                                 NULL) != SYNC_OK) {
1292                             Log("schedule request failed\n");
1293                         }
1294 #endif
1295                         Exit(SALSRV_EXIT_VOLGROUP_LINK);
1296                     } else {
1297                         Log("%u is a read-only volume; not salvaged\n",
1298                             singleVolumeNumber);
1299                         Exit(1);
1300                     }
1301                 }
1302                 if (!singleVolumeNumber
1303                     || (vsp->header.id == singleVolumeNumber
1304                         || vsp->header.parent == singleVolumeNumber)) {
1305                     (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1306                                        VFORMAT, afs_printable_uint32_lu(vsp->header.id));
1307                     if (singleVolumeNumber 
1308                         && vsp->header.id != singleVolumeNumber)
1309                         AskOffline(vsp->header.id, fileSysPartition->name);
1310                     if (strcmp(nameShouldBe, dp->d_name)) {
1311                         if (!Showmode)
1312                             Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", name, (Testing ? "it would have been " : ""));
1313                         if (!Testing) {
1314                             if (unlink(name)) {
1315                                 Log("Unable to unlink %s (errno = %d)\n", name, errno);
1316                             }
1317                         }
1318                     } else {
1319                         vsp->fileName = ToString(dp->d_name);
1320                         nVolumes++;
1321                         vsp++;
1322                     }
1323                 }
1324             }
1325             close(fd);
1326         }
1327     }
1328     closedir(dirp);
1329     qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1330           CompareVolumes);
1331 }
1332
1333 /* Find the link table. This should be associated with the RW volume or, if
1334  * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1335  */
1336 Inode
1337 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1338                struct ViceInodeInfo *allInodes)
1339 {
1340     int i, j;
1341     struct ViceInodeInfo *ip;
1342
1343     for (i = 0; i < nVols; i++) {
1344         ip = allInodes + isp[i].index;
1345         for (j = 0; j < isp[i].nSpecialInodes; j++) {
1346             if (ip[j].u.special.type == VI_LINKTABLE)
1347                 return ip[j].inodeNumber;
1348         }
1349     }
1350     return (Inode) - 1;
1351 }
1352
1353 int
1354 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1355 {
1356     struct versionStamp version;
1357     FdHandle_t *fdP;
1358
1359     if (!VALID_INO(ino))
1360         ino =
1361             IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1362                       INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1363     if (!VALID_INO(ino))
1364         Abort
1365             ("Unable to allocate link table inode for volume %u (error = %d)\n",
1366              isp->RWvolumeId, errno);
1367     IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1368     fdP = IH_OPEN(VGLinkH);
1369     if (fdP == NULL)
1370         Abort("Can't open link table for volume %u (error = %d)\n",
1371               isp->RWvolumeId, errno);
1372
1373     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1374         Abort("Can't truncate link table for volume %u (error = %d)\n",
1375               isp->RWvolumeId, errno);
1376
1377     version.magic = LINKTABLEMAGIC;
1378     version.version = LINKTABLEVERSION;
1379
1380     if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1381         != sizeof(version))
1382         Abort("Can't truncate link table for volume %u (error = %d)\n",
1383               isp->RWvolumeId, errno);
1384
1385     FDH_REALLYCLOSE(fdP);
1386
1387     /* If the volume summary exits (i.e.,  the V*.vol header file exists),
1388      * then set this inode there as well.
1389      */
1390     if (isp->volSummary)
1391         isp->volSummary->header.linkTable = ino;
1392
1393     return 0;
1394 }
1395
1396 #ifdef AFS_NT40_ENV
1397 void *
1398 nt_SVG(void *arg)
1399 {
1400     SVGParms_t *parms = (SVGParms_t *) arg;
1401     DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1402     return NULL;
1403 }
1404
1405 void
1406 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1407 {
1408     pthread_t tid;
1409     pthread_attr_t tattr;
1410     int code;
1411     SVGParms_t parms;
1412
1413     /* Initialize per volume global variables, even if later code does so */
1414     VolumeChanged = 0;
1415     VGLinkH = NULL;
1416     VGLinkH_cnt = 0;
1417     memset(&VolInfo, 0, sizeof(VolInfo));
1418
1419     parms.svgp_inodeSummaryp = isp;
1420     parms.svgp_count = nVols;
1421     code = pthread_attr_init(&tattr);
1422     if (code) {
1423         Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1424             isp->RWvolumeId);
1425         return;
1426     }
1427     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1428     if (code) {
1429         Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1430         return;
1431     }
1432     code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1433     if (code) {
1434         Log("Failed to create thread to salvage volume group %u\n",
1435             isp->RWvolumeId);
1436         return;
1437     }
1438     (void)pthread_join(tid, NULL);
1439 }
1440 #endif /* AFS_NT40_ENV */
1441
1442 void
1443 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1444 {
1445     struct ViceInodeInfo *inodes, *allInodes, *ip;
1446     int i, totalInodes, size, salvageTo;
1447     int haveRWvolume;
1448     int check;
1449     Inode ino;
1450     int dec_VGLinkH = 0;
1451     int VGLinkH_p1 =0;
1452     FdHandle_t *fdP = NULL;
1453
1454     VGLinkH_cnt = 0;
1455     haveRWvolume = (isp->volumeId == isp->RWvolumeId
1456                     && isp->nSpecialInodes > 0);
1457     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1458         if (!ForceSalvage && QuickCheck(isp, nVols))
1459             return;
1460     }
1461     if (ShowMounts && !haveRWvolume)
1462         return;
1463     if (canfork && !debug && Fork() != 0) {
1464         (void)Wait("Salvage volume group");
1465         return;
1466     }
1467     for (i = 0, totalInodes = 0; i < nVols; i++)
1468         totalInodes += isp[i].nInodes;
1469     size = totalInodes * sizeof(struct ViceInodeInfo);
1470     inodes = (struct ViceInodeInfo *)malloc(size);
1471     allInodes = inodes - isp->index;    /* this would the base of all the inodes
1472                                          * for the partition, if all the inodes
1473                                          * had been read into memory */
1474     assert(afs_lseek
1475            (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1476             SEEK_SET) != -1);
1477     assert(read(inodeFd, inodes, size) == size);
1478
1479     /* Don't try to salvage a read write volume if there isn't one on this
1480      * partition */
1481     salvageTo = haveRWvolume ? 0 : 1;
1482
1483 #ifdef AFS_NAMEI_ENV
1484     ino = FindLinkHandle(isp, nVols, allInodes);
1485     if (VALID_INO(ino)) {
1486         IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1487         fdP = IH_OPEN(VGLinkH);
1488     }
1489     if (!VALID_INO(ino) || fdP == NULL) {
1490         Log("%s link table for volume %u.\n",
1491             Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1492         if (Testing) {
1493             IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1494         } else {
1495             int i, j;
1496             struct ViceInodeInfo *ip;
1497             CreateLinkTable(isp, ino);
1498             fdP = IH_OPEN(VGLinkH);
1499             /* Sync fake 1 link counts to the link table, now that it exists */
1500             if (fdP) {
1501                 for (i = 0; i < nVols; i++) {
1502                         ip = allInodes + isp[i].index;
1503                          for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1504 #ifdef AFS_NT40_ENV
1505                                  nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1506 #else
1507                                  namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1508 #endif
1509                     }
1510                 }
1511             }
1512         }
1513     }
1514     if (fdP)
1515         FDH_REALLYCLOSE(fdP);
1516 #else
1517     IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1518 #endif
1519
1520     /* Salvage in reverse order--read/write volume last; this way any
1521      * Inodes not referenced by the time we salvage the read/write volume
1522      * can be picked up by the read/write volume */
1523     /* ACTUALLY, that's not done right now--the inodes just vanish */
1524     for (i = nVols - 1; i >= salvageTo; i--) {
1525         int rw = (i == 0);
1526         struct InodeSummary *lisp = &isp[i];
1527 #ifdef AFS_NAMEI_ENV
1528         /* If only the RO is present on this partition, the link table
1529          * shows up as a RW volume special file. Need to make sure the
1530          * salvager doesn't try to salvage the non-existent RW.
1531          */
1532         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1533             /* If this only special inode is the link table, continue */
1534             if (inodes->u.special.type == VI_LINKTABLE) {
1535                 haveRWvolume = 0;
1536                 continue;
1537             }
1538         }
1539 #endif
1540         if (!Showmode)
1541             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1542                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1543         /* Check inodes twice.  The second time do things seriously.  This
1544          * way the whole RO volume can be deleted, below, if anything goes wrong */
1545         for (check = 1; check >= 0; check--) {
1546             int deleteMe;
1547             if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1548                 == -1) {
1549                 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1550                 if (rw && deleteMe) {
1551                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
1552                                          * volume won't be called */
1553                     break;
1554                 }
1555                 if (!rw)
1556                     break;
1557             }
1558             if (rw && check == 1)
1559                 continue;
1560             if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1561                 MaybeZapVolume(lisp, "Vnode index", 0, check);
1562                 break;
1563             }
1564         }
1565     }
1566
1567     /* Fix actual inode counts */
1568     if (!Showmode) {
1569         Log("totalInodes %d\n",totalInodes);
1570         for (ip = inodes; totalInodes; ip++, totalInodes--) {
1571             static int TraceBadLinkCounts = 0;
1572 #ifdef AFS_NAMEI_ENV
1573             if (VGLinkH->ih_ino == ip->inodeNumber) {
1574                 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1575                 VGLinkH_p1 = ip->u.param[0];
1576                 continue;       /* Deal with this last. */
1577             }
1578 #endif
1579             if (ip->linkCount != 0 && TraceBadLinkCounts) {
1580                 TraceBadLinkCounts--;   /* Limit reports, per volume */
1581                 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]);
1582             }
1583             while (ip->linkCount > 0) {
1584                 /* below used to assert, not break */
1585                 if (!Testing) {
1586                     if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1587                         Log("idec failed. inode %s errno %d\n",
1588                             PrintInode(NULL, ip->inodeNumber), errno);
1589                         break;
1590                     }
1591                 }
1592                 ip->linkCount--;
1593             }
1594             while (ip->linkCount < 0) {
1595                 /* these used to be asserts */
1596                 if (!Testing) {
1597                     if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1598                         Log("iinc failed. inode %s errno %d\n",
1599                             PrintInode(NULL, ip->inodeNumber), errno);
1600                         break;
1601                     }
1602                 }
1603                 ip->linkCount++;
1604             }
1605         }
1606 #ifdef AFS_NAMEI_ENV
1607         while (dec_VGLinkH > 0) {
1608             if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1609                 Log("idec failed on link table, errno = %d\n", errno);
1610             }
1611             dec_VGLinkH--;
1612         }
1613         while (dec_VGLinkH < 0) {
1614             if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1615                 Log("iinc failed on link table, errno = %d\n", errno);
1616             }
1617             dec_VGLinkH++;
1618         }
1619 #endif
1620     }
1621     free(inodes);
1622     /* Directory consistency checks on the rw volume */
1623     if (haveRWvolume)
1624         SalvageVolume(isp, VGLinkH);
1625     IH_RELEASE(VGLinkH);
1626
1627     if (canfork && !debug) {
1628         ShowLog = 0;
1629         Exit(0);
1630     }
1631 }
1632
1633 int
1634 QuickCheck(register struct InodeSummary *isp, int nVols)
1635 {
1636     /* Check headers BEFORE forking */
1637     register int i;
1638     IHandle_t *h;
1639
1640     for (i = 0; i < nVols; i++) {
1641         struct VolumeSummary *vs = isp[i].volSummary;
1642         VolumeDiskData volHeader;
1643         if (!vs) {
1644             /* Don't salvage just because phantom rw volume is there... */
1645             /* (If a read-only volume exists, read/write inodes must also exist) */
1646             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1647                 continue;
1648             return 0;
1649         }
1650         IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1651         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1652             == sizeof(volHeader)
1653             && volHeader.stamp.magic == VOLUMEINFOMAGIC
1654             && volHeader.dontSalvage == DONT_SALVAGE
1655             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1656             if (volHeader.inUse != 0) {
1657                 volHeader.inUse = 0;
1658                 volHeader.inService = 1;
1659                 if (!Testing) {
1660                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1661                         != sizeof(volHeader)) {
1662                         IH_RELEASE(h);
1663                         return 0;
1664                     }
1665                 }
1666             }
1667             IH_RELEASE(h);
1668         } else {
1669             IH_RELEASE(h);
1670             return 0;
1671         }
1672     }
1673     return 1;
1674 }
1675
1676
1677 /* SalvageVolumeHeaderFile
1678  *
1679  * Salvage the top level V*.vol header file. Make sure the special files
1680  * exist and that there are no duplicates.
1681  *
1682  * Calls SalvageHeader for each possible type of volume special file.
1683  */
1684
1685 int
1686 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1687                         register struct ViceInodeInfo *inodes, int RW,
1688                         int check, int *deleteMe)
1689 {
1690     int headerFd = 0;
1691     int i;
1692     register struct ViceInodeInfo *ip;
1693     int allinodesobsolete = 1;
1694     struct VolumeDiskHeader diskHeader;
1695
1696     if (deleteMe)
1697         *deleteMe = 0;
1698     memset(&tempHeader, 0, sizeof(tempHeader));
1699     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1700     tempHeader.stamp.version = VOLUMEHEADERVERSION;
1701     tempHeader.id = isp->volumeId;
1702     tempHeader.parent = isp->RWvolumeId;
1703     /* Check for duplicates (inodes are sorted by type field) */
1704     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1705         ip = &inodes[isp->index + i];
1706         if (ip->u.special.type == (ip + 1)->u.special.type) {
1707             if (!Showmode)
1708                 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1709             return -1;
1710         }
1711     }
1712     for (i = 0; i < isp->nSpecialInodes; i++) {
1713         ip = &inodes[isp->index + i];
1714         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1715             if (check) {
1716                 Log("Rubbish header inode\n");
1717                 return -1;
1718             }
1719             Log("Rubbish header inode; deleted\n");
1720         } else if (!stuff[ip->u.special.type - 1].obsolete) {
1721             *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1722             if (!check && ip->u.special.type != VI_LINKTABLE)
1723                 ip->linkCount--;        /* Keep the inode around */
1724             allinodesobsolete = 0;
1725         }
1726     }
1727
1728     if (allinodesobsolete) {
1729         if (deleteMe)
1730             *deleteMe = 1;
1731         return -1;
1732     }
1733
1734     if (!check)
1735         VGLinkH_cnt++;          /* one for every header. */
1736
1737     if (!RW && !check && isp->volSummary) {
1738         ClearROInUseBit(isp->volSummary);
1739         return 0;
1740     }
1741
1742     for (i = 0; i < MAXINODETYPE; i++) {
1743         if (stuff[i].inodeType == VI_LINKTABLE) {
1744             /* Gross hack: SalvageHeader does a bcmp on the volume header.
1745              * And we may have recreated the link table earlier, so set the
1746              * RW header as well.
1747              */
1748             if (VALID_INO(VGLinkH->ih_ino)) {
1749                 *stuff[i].inode = VGLinkH->ih_ino;
1750             }
1751             continue;
1752         }
1753         if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1754             return -1;
1755     }
1756
1757     if (isp->volSummary == NULL) {
1758         char path[64];
1759         char headerName[64];
1760         (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
1761         (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
1762         if (check) {
1763             Log("No header file for volume %u\n", isp->volumeId);
1764             return -1;
1765         }
1766         if (!Showmode)
1767             Log("No header file for volume %u; %screating %s\n",
1768                 isp->volumeId, (Testing ? "it would have been " : ""),
1769                 path);
1770         headerFd = afs_open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
1771         assert(headerFd != -1);
1772         isp->volSummary = (struct VolumeSummary *)
1773             malloc(sizeof(struct VolumeSummary));
1774         isp->volSummary->fileName = ToString(headerName);
1775     } else {
1776         char path[64];
1777         char headerName[64];
1778         /* hack: these two fields are obsolete... */
1779         isp->volSummary->header.volumeAcl = 0;
1780         isp->volSummary->header.volumeMountTable = 0;
1781
1782         if (memcmp
1783             (&isp->volSummary->header, &tempHeader,
1784              sizeof(struct VolumeHeader))) {
1785             /* We often remove the name before calling us, so we make a fake one up */
1786             if (isp->volSummary->fileName) {
1787                 strcpy(headerName, isp->volSummary->fileName);
1788             } else {
1789                 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
1790                 isp->volSummary->fileName = ToString(headerName);
1791             }
1792             (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
1793
1794             Log("Header file %s is damaged or no longer valid%s\n", path,
1795                 (check ? "" : "; repairing"));
1796             if (check)
1797                 return -1;
1798
1799             headerFd = afs_open(path, O_RDWR | O_TRUNC, 0644);
1800             assert(headerFd != -1);
1801         }
1802     }
1803     if (headerFd) {
1804         memcpy(&isp->volSummary->header, &tempHeader,
1805                sizeof(struct VolumeHeader));
1806         if (Testing) {
1807             if (!Showmode)
1808                 Log("It would have written a new header file for volume %u\n",
1809                     isp->volumeId);
1810         } else {
1811             VolumeHeaderToDisk(&diskHeader, &tempHeader);
1812             if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1813                 != sizeof(struct VolumeDiskHeader)) {
1814                 Log("Couldn't rewrite volume header file!\n");
1815                 close(headerFd);
1816                 return -1;
1817             }
1818         }
1819         close(headerFd);
1820     }
1821     IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1822             isp->volSummary->header.volumeInfo);
1823     return 0;
1824 }
1825
1826 int
1827 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1828               int *deleteMe)
1829 {
1830     union {
1831         VolumeDiskData volumeInfo;
1832         struct versionStamp fileHeader;
1833     } header;
1834     IHandle_t *specH;
1835     int recreate = 0;
1836     afs_int32 code;
1837     FdHandle_t *fdP;
1838
1839     if (sp->obsolete)
1840         return 0;
1841 #ifndef AFS_NAMEI_ENV
1842     if (sp->inodeType == VI_LINKTABLE)
1843         return 0;
1844 #endif
1845     if (*(sp->inode) == 0) {
1846         if (check) {
1847             Log("Missing inode in volume header (%s)\n", sp->description);
1848             return -1;
1849         }
1850         if (!Showmode)
1851             Log("Missing inode in volume header (%s); %s\n", sp->description,
1852                 (Testing ? "it would have recreated it" : "recreating"));
1853         if (!Testing) {
1854             *(sp->inode) =
1855                 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1856                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1857             if (!VALID_INO(*(sp->inode)))
1858                 Abort
1859                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1860                      sp->description, errno);
1861         }
1862         recreate = 1;
1863     }
1864
1865     IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1866     fdP = IH_OPEN(specH);
1867     if (OKToZap && (fdP == NULL) && BadError(errno)) {
1868         /* bail out early and destroy the volume */
1869         if (!Showmode)
1870             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1871         if (deleteMe)
1872             *deleteMe = 1;
1873         IH_RELEASE(specH);
1874         return -1;
1875     }
1876     if (fdP == NULL)
1877         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1878               sp->description, errno);
1879
1880     if (!recreate
1881         && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1882             || header.fileHeader.magic != sp->stamp.magic)) {
1883         if (check) {
1884             Log("Part of the header (%s) is corrupted\n", sp->description);
1885             FDH_REALLYCLOSE(fdP);
1886             IH_RELEASE(specH);
1887             return -1;
1888         }
1889         Log("Part of the header (%s) is corrupted; recreating\n",
1890             sp->description);
1891         recreate = 1;
1892     }
1893     if (sp->inodeType == VI_VOLINFO
1894         && header.volumeInfo.destroyMe == DESTROY_ME) {
1895         if (deleteMe)
1896             *deleteMe = 1;
1897         FDH_REALLYCLOSE(fdP);
1898         IH_RELEASE(specH);
1899         return -1;
1900     }
1901     if (recreate && !Testing) {
1902         if (check)
1903             Abort
1904                 ("Internal error: recreating volume header (%s) in check mode\n",
1905                  sp->description);
1906         code = FDH_TRUNC(fdP, 0);
1907         if (code == -1)
1908             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1909                   sp->description, errno);
1910
1911         /* The following code should be moved into vutil.c */
1912         if (sp->inodeType == VI_VOLINFO) {
1913             struct timeval tp;
1914             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1915             header.volumeInfo.stamp = sp->stamp;
1916             header.volumeInfo.id = isp->volumeId;
1917             header.volumeInfo.parentId = isp->RWvolumeId;
1918             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1919             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1920                 isp->volumeId, isp->volumeId);
1921             header.volumeInfo.inService = 0;
1922             header.volumeInfo.blessed = 0;
1923             /* The + 1000 is a hack in case there are any files out in venus caches */
1924             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1925             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
1926             header.volumeInfo.needsCallback = 0;
1927             gettimeofday(&tp, 0);
1928             header.volumeInfo.creationDate = tp.tv_sec;
1929             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1930                 Abort
1931                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1932                      sp->description, errno);
1933             }
1934             code =
1935                 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1936                           sizeof(header.volumeInfo));
1937             if (code != sizeof(header.volumeInfo)) {
1938                 if (code < 0)
1939                     Abort
1940                         ("Unable to write volume header file (%s) (errno = %d)\n",
1941                          sp->description, errno);
1942                 Abort("Unable to write entire volume header file (%s)\n",
1943                       sp->description);
1944             }
1945         } else {
1946             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1947                 Abort
1948                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1949                      sp->description, errno);
1950             }
1951             code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1952             if (code != sizeof(sp->stamp)) {
1953                 if (code < 0)
1954                     Abort
1955                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1956                          sp->description, errno);
1957                 Abort
1958                     ("Unable to write entire version stamp in volume header file (%s)\n",
1959                      sp->description);
1960             }
1961         }
1962     }
1963     FDH_REALLYCLOSE(fdP);
1964     IH_RELEASE(specH);
1965     if (sp->inodeType == VI_VOLINFO) {
1966         VolInfo = header.volumeInfo;
1967         if (check) {
1968             char update[25];
1969             if (VolInfo.updateDate) {
1970                 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1971                 if (!Showmode)
1972                     Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1973                         (Testing ? "it would have been " : ""), update);
1974             } else {
1975                 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1976                 if (!Showmode)
1977                     Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1978                         VolInfo.id, update);
1979             }
1980
1981         }
1982     }
1983
1984     return 0;
1985 }
1986
1987 int
1988 SalvageVnodes(register struct InodeSummary *rwIsp,
1989               register struct InodeSummary *thisIsp,
1990               register struct ViceInodeInfo *inodes, int check)
1991 {
1992     int ilarge, ismall, ioffset, RW, nInodes;
1993     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
1994     if (Showmode)
1995         return 0;
1996     RW = (rwIsp == thisIsp);
1997     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1998     ismall =
1999         SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2000                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2001     if (check && ismall == -1)
2002         return -1;
2003     ilarge =
2004         SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2005                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2006     return (ilarge == 0 && ismall == 0 ? 0 : -1);
2007 }
2008
2009 int
2010 SalvageIndex(Inode ino, VnodeClass class, int RW,
2011              register struct ViceInodeInfo *ip, int nInodes,
2012              struct VolumeSummary *volSummary, int check)
2013 {
2014     VolumeId volumeNumber;
2015     char buf[SIZEOF_LARGEDISKVNODE];
2016     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2017     int err = 0;
2018     StreamHandle_t *file;
2019     struct VnodeClassInfo *vcp;
2020     afs_sfsize_t size;
2021     afs_fsize_t vnodeLength;
2022     int vnodeIndex, nVnodes;
2023     afs_ino_str_t stmp1, stmp2;
2024     IHandle_t *handle;
2025     FdHandle_t *fdP;
2026
2027     volumeNumber = volSummary->header.id;
2028     IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2029     fdP = IH_OPEN(handle);
2030     assert(fdP != NULL);
2031     file = FDH_FDOPEN(fdP, "r+");
2032     assert(file != NULL);
2033     vcp = &VnodeClassInfo[class];
2034     size = OS_SIZE(fdP->fd_fd);
2035     assert(size != -1);
2036     nVnodes = (size / vcp->diskSize) - 1;
2037     if (nVnodes > 0) {
2038         assert((nVnodes + 1) * vcp->diskSize == size);
2039         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2040     } else {
2041         nVnodes = 0;
2042     }
2043     for (vnodeIndex = 0;
2044          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2045          nVnodes--, vnodeIndex++) {
2046         if (vnode->type != vNull) {
2047             int vnodeChanged = 0;
2048             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2049             /* Log programs that belong to root (potentially suid root);
2050              * don't bother for read-only or backup volumes */
2051 #ifdef  notdef                  /* This is done elsewhere */
2052             if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2053                 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);
2054 #endif
2055             if (VNDISK_GET_INO(vnode) == 0) {
2056                 if (RW) {
2057                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2058                     memset(vnode, 0, vcp->diskSize);
2059                     vnodeChanged = 1;
2060                 }
2061             } else {
2062                 if (vcp->magic != vnode->vnodeMagic) {
2063                     /* bad magic #, probably partially created vnode */
2064                     Log("Partially allocated vnode %d deleted.\n",
2065                         vnodeNumber);
2066                     memset(vnode, 0, vcp->diskSize);
2067                     vnodeChanged = 1;
2068                     goto vnodeDone;
2069                 }
2070                 /* ****** Should do a bit more salvage here:  e.g. make sure
2071                  * vnode type matches what it should be given the index */
2072                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2073 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2074  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2075  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2076  *                  }
2077  */
2078                     ip++;
2079                     nInodes--;
2080                 }
2081                 if (!RW) {
2082                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2083                         /* The following doesn't work, because the version number
2084                          * is not maintained correctly by the file server */
2085                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2086                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2087                          * break; */
2088                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2089                             break;
2090                         ip++;
2091                         nInodes--;
2092                     }
2093                 } else {
2094                     /* For RW volume, look for vnode with matching inode number;
2095                      * if no such match, take the first determined by our sort
2096                      * order */
2097                     register struct ViceInodeInfo *lip = ip;
2098                     register int lnInodes = nInodes;
2099                     while (lnInodes
2100                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2101                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2102                             ip = lip;
2103                             nInodes = lnInodes;
2104                             break;
2105                         }
2106                         lip++;
2107                         lnInodes--;
2108                     }
2109                 }
2110                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2111                     /* "Matching" inode */
2112                     if (RW) {
2113                         Unique vu, iu;
2114                         FileVersion vd, id;
2115                         vu = vnode->uniquifier;
2116                         iu = ip->u.vnode.vnodeUniquifier;
2117                         vd = vnode->dataVersion;
2118                         id = ip->u.vnode.inodeDataVersion;
2119                         /*
2120                          * Because of the possibility of the uniquifier overflows (> 4M)
2121                          * we compare them modulo the low 22-bits; we shouldn't worry
2122                          * about mismatching since they shouldn't to many old 
2123                          * uniquifiers of the same vnode...
2124                          */
2125                         if (IUnique(vu) != IUnique(iu)) {
2126                             if (!Showmode) {
2127                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2128                             }
2129
2130                             vnode->uniquifier = iu;
2131 #ifdef  AFS_3DISPARES
2132                             vnode->dataVersion = (id >= vd ?
2133                                                   /* 90% of 2.1M */
2134                                                   ((id - vd) >
2135                                                    1887437 ? vd : id) :
2136                                                   /* 90% of 2.1M */
2137                                                   ((vd - id) >
2138                                                    1887437 ? id : vd));
2139 #else
2140 #if defined(AFS_SGI_EXMAG)
2141                             vnode->dataVersion = (id >= vd ?
2142                                                   /* 90% of 16M */
2143                                                   ((id - vd) >
2144                                                    15099494 ? vd : id) :
2145                                                   /* 90% of 16M */
2146                                                   ((vd - id) >
2147                                                    15099494 ? id : vd));
2148 #else
2149                             vnode->dataVersion = (id > vd ? id : vd);
2150 #endif /* AFS_SGI_EXMAG */
2151 #endif /* AFS_3DISPARES */
2152                             vnodeChanged = 1;
2153                         } else {
2154                             /* don't bother checking for vd > id any more, since
2155                              * partial file transfers always result in this state,
2156                              * and you can't do much else anyway (you've already
2157                              * found the best data you can) */
2158 #ifdef  AFS_3DISPARES
2159                             if (!vnodeIsDirectory(vnodeNumber)
2160                                 && ((vd < id && (id - vd) < 1887437)
2161                                     || ((vd > id && (vd - id) > 1887437)))) {
2162 #else
2163 #if defined(AFS_SGI_EXMAG)
2164                             if (!vnodeIsDirectory(vnodeNumber)
2165                                 && ((vd < id && (id - vd) < 15099494)
2166                                     || ((vd > id && (vd - id) > 15099494)))) {
2167 #else
2168                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2169 #endif /* AFS_SGI_EXMAG */
2170 #endif
2171                                 if (!Showmode)
2172                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2173                                 vnode->dataVersion = id;
2174                                 vnodeChanged = 1;
2175                             }
2176                         }
2177                     }
2178                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2179                         if (check) {
2180                             if (!Showmode) {
2181                                 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);
2182                             }
2183                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2184                             err = -1;
2185                             goto zooks;
2186                         }
2187                         if (!Showmode) {
2188                             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);
2189                         }
2190                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2191                         vnodeChanged = 1;
2192                     }
2193                     VNDISK_GET_LEN(vnodeLength, vnode);
2194                     if (ip->byteCount != vnodeLength) {
2195                         if (check) {
2196                             if (!Showmode)
2197                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2198                             err = -1;
2199                             goto zooks;
2200                         }
2201                         if (!Showmode)
2202                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2203                         VNDISK_SET_LEN(vnode, ip->byteCount);
2204                         vnodeChanged = 1;
2205                     }
2206                     if (!check)
2207                         ip->linkCount--;        /* Keep the inode around */
2208                     ip++;
2209                     nInodes--;
2210                 } else {        /* no matching inode */
2211                     if (VNDISK_GET_INO(vnode) != 0
2212                         || vnode->type == vDirectory) {
2213                         /* No matching inode--get rid of the vnode */
2214                         if (check) {
2215                             if (VNDISK_GET_INO(vnode)) {
2216                                 if (!Showmode) {
2217                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2218                                 }
2219                             } else {
2220                                 if (!Showmode)
2221                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2222                             }
2223                             err = -1;
2224                             goto zooks;
2225                         }
2226                         if (VNDISK_GET_INO(vnode)) {
2227                             if (!Showmode) {
2228                                 time_t serverModifyTime = vnode->serverModifyTime;
2229                                 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));
2230                             }
2231                         } else {
2232                             if (!Showmode) {
2233                                 time_t serverModifyTime = vnode->serverModifyTime;
2234                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2235                             }
2236                         }
2237                         memset(vnode, 0, vcp->diskSize);
2238                         vnodeChanged = 1;
2239                     } else {
2240                         /* Should not reach here becuase we checked for 
2241                          * (inodeNumber == 0) above. And where we zero the vnode,
2242                          * we also goto vnodeDone.
2243                          */
2244                     }
2245                 }
2246                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2247                     ip++;
2248                     nInodes--;
2249                 }
2250             }                   /* VNDISK_GET_INO(vnode) != 0 */
2251           vnodeDone:
2252             assert(!(vnodeChanged && check));
2253             if (vnodeChanged && !Testing) {
2254                 assert(IH_IWRITE
2255                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2256                         (char *)vnode, vcp->diskSize)
2257                        == vcp->diskSize);
2258                 VolumeChanged = 1;      /* For break call back */
2259             }
2260         }
2261     }
2262   zooks:
2263     STREAM_CLOSE(file);
2264     FDH_CLOSE(fdP);
2265     IH_RELEASE(handle);
2266     return err;
2267 }
2268
2269 struct VnodeEssence *
2270 CheckVnodeNumber(VnodeId vnodeNumber)
2271 {
2272     VnodeClass class;
2273     struct VnodeInfo *vip;
2274     int offset;
2275
2276     class = vnodeIdToClass(vnodeNumber);
2277     vip = &vnodeInfo[class];
2278     offset = vnodeIdToBitNumber(vnodeNumber);
2279     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2280 }
2281
2282 void
2283 CopyOnWrite(register struct DirSummary *dir)
2284 {
2285     /* Copy the directory unconditionally if we are going to change it:
2286      * not just if was cloned.
2287      */
2288     struct VnodeDiskObject vnode;
2289     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2290     Inode oldinode, newinode;
2291     afs_sfsize_t code;
2292
2293     if (dir->copied || Testing)
2294         return;
2295     DFlush();                   /* Well justified paranoia... */
2296
2297     code =
2298         IH_IREAD(vnodeInfo[vLarge].handle,
2299                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2300                  sizeof(vnode));
2301     assert(code == sizeof(vnode));
2302     oldinode = VNDISK_GET_INO(&vnode);
2303     /* Increment the version number by a whole lot to avoid problems with
2304      * clients that were promised new version numbers--but the file server
2305      * crashed before the versions were written to disk.
2306      */
2307     newinode =
2308         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2309                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2310                   200);
2311     assert(VALID_INO(newinode));
2312     assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2313     vnode.cloned = 0;
2314     VNDISK_SET_INO(&vnode, newinode);
2315     code =
2316         IH_IWRITE(vnodeInfo[vLarge].handle,
2317                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2318                   sizeof(vnode));
2319     assert(code == sizeof(vnode));
2320
2321     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2322                         fileSysDevice, newinode);
2323     /* Don't delete the original inode right away, because the directory is
2324      * still being scanned.
2325      */
2326     dir->copied = 1;
2327 }
2328
2329 /*
2330  * This function should either successfully create a new dir, or give up 
2331  * and leave things the way they were.  In particular, if it fails to write 
2332  * the new dir properly, it should return w/o changing the reference to the 
2333  * old dir.
2334  */
2335 void
2336 CopyAndSalvage(register struct DirSummary *dir)
2337 {
2338     struct VnodeDiskObject vnode;
2339     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2340     Inode oldinode, newinode;
2341     DirHandle newdir;
2342     FdHandle_t *fdP;
2343     afs_int32 code;
2344     afs_sfsize_t lcode;
2345     afs_int32 parentUnique = 1;
2346     struct VnodeEssence *vnodeEssence;
2347     afs_fsize_t length;
2348
2349     if (Testing)
2350         return;
2351     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2352     lcode =
2353         IH_IREAD(vnodeInfo[vLarge].handle,
2354                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2355                  sizeof(vnode));
2356     assert(lcode == sizeof(vnode));
2357     oldinode = VNDISK_GET_INO(&vnode);
2358     /* Increment the version number by a whole lot to avoid problems with
2359      * clients that were promised new version numbers--but the file server
2360      * crashed before the versions were written to disk.
2361      */
2362     newinode =
2363         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2364                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2365                   200);
2366     assert(VALID_INO(newinode));
2367     SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2368
2369     /* Assign . and .. vnode numbers from dir and vnode.parent. 
2370      * The uniquifier for . is in the vnode.
2371      * The uniquifier for .. might be set to a bogus value of 1 and 
2372      * the salvager will later clean it up.
2373      */
2374     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2375         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2376     }
2377     code =
2378         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2379                    vnode.uniquifier,
2380                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2381                    parentUnique);
2382     if (code == 0)
2383         code = DFlush();
2384     if (code) {
2385         /* didn't really build the new directory properly, let's just give up. */
2386         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2387         Log("Directory salvage returned code %d, continuing.\n", code);
2388         if (code) {
2389             Log("also failed to decrement link count on new inode");
2390         }
2391         assert(1 == 2);
2392     }
2393     Log("Checking the results of the directory salvage...\n");
2394     if (!DirOK(&newdir)) {
2395         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2396         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2397         assert(code == 0);
2398         assert(1 == 2);
2399     }
2400     vnode.cloned = 0;
2401     VNDISK_SET_INO(&vnode, newinode);
2402     length = Length(&newdir);
2403     VNDISK_SET_LEN(&vnode, length);
2404     lcode =
2405         IH_IWRITE(vnodeInfo[vLarge].handle,
2406                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2407                   sizeof(vnode));
2408     assert(lcode == sizeof(vnode));
2409 #if 0
2410 #ifdef AFS_NT40_ENV
2411     nt_sync(fileSysDevice);
2412 #else
2413     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2414                                  * an open FD on the file itself to fsync.
2415                                  */
2416 #endif
2417 #else
2418     vnodeInfo[vLarge].handle->ih_synced = 1;
2419 #endif
2420     /* make sure old directory file is really closed */
2421     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2422     FDH_REALLYCLOSE(fdP);
2423     
2424     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2425     assert(code == 0);
2426     dir->dirHandle = newdir;
2427 }
2428
2429 int
2430 JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
2431            afs_int32 unique)
2432 {
2433     struct DirSummary *dir = (struct DirSummary *)dirVal;
2434     struct VnodeEssence *vnodeEssence;
2435     afs_int32 dirOrphaned, todelete;
2436
2437     dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2438
2439     vnodeEssence = CheckVnodeNumber(vnodeNumber);
2440     if (vnodeEssence == NULL) {
2441         if (!Showmode) {
2442             Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2443         }
2444         if (!Testing) {
2445             CopyOnWrite(dir);
2446             assert(Delete(&dir->dirHandle, name) == 0);
2447         }
2448         return 0;
2449     }
2450 #ifdef AFS_AIX_ENV
2451 #ifndef AFS_NAMEI_ENV
2452     /* On AIX machines, don't allow entries to point to inode 0. That is a special 
2453      * mount inode for the partition. If this inode were deleted, it would crash
2454      * the machine.
2455      */
2456     if (vnodeEssence->InodeNumber == 0) {
2457         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"));
2458         if (!Testing) {
2459             CopyOnWrite(dir);
2460             assert(Delete(&dir->dirHandle, name) == 0);
2461         }
2462         return 0;
2463     }
2464 #endif
2465 #endif
2466
2467     if (!(vnodeNumber & 1) && !Showmode
2468         && !(vnodeEssence->count || vnodeEssence->unique
2469              || vnodeEssence->modeBits)) {
2470         Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2471             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2472             vnodeNumber, unique,
2473             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2474              ""));
2475         if (!unique) {
2476             if (!Testing) {
2477                 CopyOnWrite(dir);
2478                 assert(Delete(&dir->dirHandle, name) == 0);
2479             }
2480             return 0;
2481         }
2482     }
2483
2484     /* Check if the Uniquifiers match. If not, change the directory entry
2485      * so its unique matches the vnode unique. Delete if the unique is zero
2486      * or if the directory is orphaned.
2487      */
2488     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2489         if (!vnodeEssence->unique
2490             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2491             /* This is an orphaned directory. Don't delete the . or ..
2492              * entry. Otherwise, it will get created in the next 
2493              * salvage and deleted again here. So Just skip it.
2494              */
2495             return 0;
2496         }
2497
2498         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2499
2500         if (!Showmode) {
2501             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")));
2502         }
2503         if (!Testing) {
2504             AFSFid fid;
2505             fid.Vnode = vnodeNumber;
2506             fid.Unique = vnodeEssence->unique;
2507             CopyOnWrite(dir);
2508             assert(Delete(&dir->dirHandle, name) == 0);
2509             if (!todelete)
2510                 assert(Create(&dir->dirHandle, name, &fid) == 0);
2511         }
2512         if (todelete)
2513             return 0;           /* no need to continue */
2514     }
2515
2516     if (strcmp(name, ".") == 0) {
2517         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2518             AFSFid fid;
2519             if (!Showmode)
2520                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2521             if (!Testing) {
2522                 CopyOnWrite(dir);
2523                 assert(Delete(&dir->dirHandle, ".") == 0);
2524                 fid.Vnode = dir->vnodeNumber;
2525                 fid.Unique = dir->unique;
2526                 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2527             }
2528
2529             vnodeNumber = fid.Vnode;    /* Get the new Essence */
2530             unique = fid.Unique;
2531             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2532         }
2533         dir->haveDot = 1;
2534     } else if (strcmp(name, "..") == 0) {
2535         AFSFid pa;
2536         if (dir->parent) {
2537             struct VnodeEssence *dotdot;
2538             pa.Vnode = dir->parent;
2539             dotdot = CheckVnodeNumber(pa.Vnode);
2540             assert(dotdot != NULL);     /* XXX Should not be assert */
2541             pa.Unique = dotdot->unique;
2542         } else {
2543             pa.Vnode = dir->vnodeNumber;
2544             pa.Unique = dir->unique;
2545         }
2546         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2547             if (!Showmode)
2548                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2549             if (!Testing) {
2550                 CopyOnWrite(dir);
2551                 assert(Delete(&dir->dirHandle, "..") == 0);
2552                 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2553             }
2554
2555             vnodeNumber = pa.Vnode;     /* Get the new Essence */
2556             unique = pa.Unique;
2557             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2558         }
2559         dir->haveDotDot = 1;
2560     } else if (strncmp(name, ".__afs", 6) == 0) {
2561         if (!Showmode) {
2562             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);
2563         }
2564         if (!Testing) {
2565             CopyOnWrite(dir);
2566             assert(Delete(&dir->dirHandle, name) == 0);
2567         }
2568         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
2569         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
2570         return 0;
2571     } else {
2572         if (ShowSuid && (vnodeEssence->modeBits & 06000))
2573             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);
2574         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
2575             && !(vnodeEssence->modeBits & 0111)) {
2576             int code, size;
2577             char buf[1025];
2578             IHandle_t *ihP;
2579             FdHandle_t *fdP;
2580
2581             IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2582                     vnodeEssence->InodeNumber);
2583             fdP = IH_OPEN(ihP);
2584             if (fdP == NULL) {
2585                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
2586                 IH_RELEASE(ihP);
2587                 return 0;
2588             }
2589             size = FDH_SIZE(fdP);
2590             if (size < 0) {
2591                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
2592                 FDH_REALLYCLOSE(fdP);
2593                 IH_RELEASE(ihP);
2594                 return 0;
2595             }
2596         
2597             if (size > 1024)
2598                 size = 1024;
2599             code = FDH_READ(fdP, buf, size);
2600             if (code == size) {
2601                 buf[size] = '\0';
2602                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
2603                     Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
2604                         dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
2605                         Testing ? "would convert" : "converted");
2606                     vnodeEssence->modeBits |= 0111;
2607                     vnodeEssence->changed = 1;
2608                 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2609                     dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2610                     dir->name ? dir->name : "??", name, buf);
2611             } else {
2612                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
2613                     dir->vname, vnodeNumber, size, code);
2614             }
2615             FDH_REALLYCLOSE(fdP);
2616             IH_RELEASE(ihP);
2617         }
2618         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2619             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);
2620         if (vnodeIdToClass(vnodeNumber) == vLarge
2621             && vnodeEssence->name == NULL) {
2622             char *n;
2623             if ((n = (char *)malloc(strlen(name) + 1)))
2624                 strcpy(n, name);
2625             vnodeEssence->name = n;
2626         }
2627
2628         /* The directory entry points to the vnode. Check to see if the
2629          * vnode points back to the directory. If not, then let the 
2630          * directory claim it (else it might end up orphaned). Vnodes 
2631          * already claimed by another directory are deleted from this
2632          * directory: hardlinks to the same vnode are not allowed
2633          * from different directories.
2634          */
2635         if (vnodeEssence->parent != dir->vnodeNumber) {
2636             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2637                 /* Vnode does not point back to this directory.
2638                  * Orphaned dirs cannot claim a file (it may belong to
2639                  * another non-orphaned dir).
2640                  */
2641                 if (!Showmode) {
2642                     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);
2643                 }
2644                 vnodeEssence->parent = dir->vnodeNumber;
2645                 vnodeEssence->changed = 1;
2646             } else {
2647                 /* Vnode was claimed by another directory */
2648                 if (!Showmode) {
2649                     if (dirOrphaned) {
2650                         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 " : ""));
2651                     } else if (vnodeNumber == 1) {
2652                         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 " : ""));
2653                     } else {
2654                         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 " : ""));
2655                     }
2656                 }
2657                 if (!Testing) {
2658                     CopyOnWrite(dir);
2659                     assert(Delete(&dir->dirHandle, name) == 0);
2660                 }
2661                 return 0;
2662             }
2663         }
2664         /* This directory claims the vnode */
2665         vnodeEssence->claimed = 1;
2666     }
2667     vnodeEssence->count--;
2668     return 0;
2669 }
2670
2671 void
2672 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2673 {
2674     register struct VnodeInfo *vip = &vnodeInfo[class];
2675     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2676     char buf[SIZEOF_LARGEDISKVNODE];
2677     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2678     afs_sfsize_t size;
2679     StreamHandle_t *file;
2680     int vnodeIndex;
2681     int nVnodes;
2682     FdHandle_t *fdP;
2683
2684     IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2685     fdP = IH_OPEN(vip->handle);
2686     assert(fdP != NULL);
2687     file = FDH_FDOPEN(fdP, "r+");
2688     assert(file != NULL);
2689     size = OS_SIZE(fdP->fd_fd);
2690     assert(size != -1);
2691     vip->nVnodes = (size / vcp->diskSize) - 1;
2692     if (vip->nVnodes > 0) {
2693         assert((vip->nVnodes + 1) * vcp->diskSize == size);
2694         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2695         assert((vip->vnodes = (struct VnodeEssence *)
2696                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2697         if (class == vLarge) {
2698             assert((vip->inodes = (Inode *)
2699                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2700         } else {
2701             vip->inodes = NULL;
2702         }
2703     } else {
2704         vip->nVnodes = 0;
2705         vip->vnodes = NULL;
2706         vip->inodes = NULL;
2707     }
2708     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2709     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2710          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2711          nVnodes--, vnodeIndex++) {
2712         if (vnode->type != vNull) {
2713             register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2714             afs_fsize_t vnodeLength;
2715             vip->nAllocatedVnodes++;
2716             vep->count = vnode->linkCount;
2717             VNDISK_GET_LEN(vnodeLength, vnode);
2718             vep->blockCount = nBlocks(vnodeLength);
2719             vip->volumeBlockCount += vep->blockCount;
2720             vep->parent = vnode->parent;
2721             vep->unique = vnode->uniquifier;
2722             if (*maxu < vnode->uniquifier)
2723                 *maxu = vnode->uniquifier;
2724             vep->modeBits = vnode->modeBits;
2725             vep->InodeNumber = VNDISK_GET_INO(vnode);
2726             vep->type = vnode->type;
2727             vep->author = vnode->author;
2728             vep->owner = vnode->owner;
2729             vep->group = vnode->group;
2730             if (vnode->type == vDirectory) {
2731                 if (class != vLarge) {
2732                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2733                     vip->nAllocatedVnodes--;
2734                     memset(vnode, 0, sizeof(vnode));
2735                     IH_IWRITE(vnodeInfo[vSmall].handle,
2736                               vnodeIndexOffset(vcp, vnodeNumber),
2737                               (char *)&vnode, sizeof(vnode));
2738                     VolumeChanged = 1;
2739                 } else
2740                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2741             }
2742         }
2743     }
2744     STREAM_CLOSE(file);
2745     FDH_CLOSE(fdP);
2746 }
2747
2748 static char *
2749 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2750 {
2751     struct VnodeEssence *parentvp;
2752
2753     if (vnode == 1) {
2754         strcpy(path, ".");
2755         return path;
2756     }
2757     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2758         && GetDirName(vp->parent, parentvp, path)) {
2759         strcat(path, "/");
2760         strcat(path, vp->name);
2761         return path;
2762     }
2763     return 0;
2764 }
2765
2766 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2767  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2768  */
2769 static int
2770 IsVnodeOrphaned(VnodeId vnode)
2771 {
2772     struct VnodeEssence *vep;
2773
2774     if (vnode == 0)
2775         return (1);             /* Vnode zero does not exist */
2776     if (vnode == 1)
2777         return (0);             /* The root dir vnode is always claimed */
2778     vep = CheckVnodeNumber(vnode);      /* Get the vnode essence */
2779     if (!vep || !vep->claimed)
2780         return (1);             /* Vnode is not claimed - it is orphaned */
2781
2782     return (IsVnodeOrphaned(vep->parent));
2783 }
2784
2785 void
2786 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2787            IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2788            int *rootdirfound)
2789 {
2790     static struct DirSummary dir;
2791     static struct DirHandle dirHandle;
2792     struct VnodeEssence *parent;
2793     static char path[MAXPATHLEN];
2794     int dirok, code;
2795
2796     if (dirVnodeInfo->vnodes[i].salvaged)
2797         return;                 /* already salvaged */
2798
2799     dir.rwVid = rwVid;
2800     dirVnodeInfo->vnodes[i].salvaged = 1;
2801
2802     if (dirVnodeInfo->inodes[i] == 0)
2803         return;                 /* Not allocated to a directory */
2804
2805     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2806         if (dirVnodeInfo->vnodes[i].parent) {
2807             Log("Bad parent, vnode 1; %s...\n",
2808                 (Testing ? "skipping" : "salvaging"));
2809             dirVnodeInfo->vnodes[i].parent = 0;
2810             dirVnodeInfo->vnodes[i].changed = 1;
2811         }
2812     } else {
2813         parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2814         if (parent && parent->salvaged == 0)
2815             SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2816                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2817                        rootdir, rootdirfound);
2818     }
2819
2820     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2821     dir.unique = dirVnodeInfo->vnodes[i].unique;
2822     dir.copied = 0;
2823     dir.vname = name;
2824     dir.parent = dirVnodeInfo->vnodes[i].parent;
2825     dir.haveDot = dir.haveDotDot = 0;
2826     dir.ds_linkH = alinkH;
2827     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2828                         dirVnodeInfo->inodes[i]);
2829
2830     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2831     if (!dirok) {
2832         if (!RebuildDirs) {
2833             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2834                 (Testing ? "skipping" : "salvaging"));
2835         }
2836         if (!Testing) {
2837             CopyAndSalvage(&dir);
2838             dirok = 1;
2839         }
2840     }
2841     dirHandle = dir.dirHandle;
2842
2843     dir.name =
2844         GetDirName(bitNumberToVnodeNumber(i, vLarge),
2845                    &dirVnodeInfo->vnodes[i], path);
2846
2847     if (dirok) {
2848         /* If enumeration failed for random reasons, we will probably delete
2849          * too much stuff, so we guard against this instead.
2850          */
2851         assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2852     }
2853
2854     /* Delete the old directory if it was copied in order to salvage.
2855      * CopyOnWrite has written the new inode # to the disk, but we still
2856      * have the old one in our local structure here.  Thus, we idec the
2857      * local dude.
2858      */
2859     DFlush();
2860     if (dir.copied && !Testing) {
2861         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2862         assert(code == 0);
2863         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2864     }
2865
2866     /* Remember rootdir DirSummary _after_ it has been judged */
2867     if (dir.vnodeNumber == 1 && dir.unique == 1) {
2868         memcpy(rootdir, &dir, sizeof(struct DirSummary));
2869         *rootdirfound = 1;
2870     }
2871
2872     return;
2873 }
2874
2875 int
2876 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2877 {
2878     /* This routine, for now, will only be called for read-write volumes */
2879     int i, j, code;
2880     int BlocksInVolume = 0, FilesInVolume = 0;
2881     register VnodeClass class;
2882     struct DirSummary rootdir, oldrootdir;
2883     struct VnodeInfo *dirVnodeInfo;
2884     struct VnodeDiskObject vnode;
2885     VolumeDiskData volHeader;
2886     VolumeId vid;
2887     int orphaned, rootdirfound = 0;
2888     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
2889     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
2890     struct VnodeEssence *vep;
2891     afs_int32 v, pv;
2892     IHandle_t *h;
2893     afs_sfsize_t nBytes;
2894     AFSFid pa;
2895     VnodeId LFVnode, ThisVnode;
2896     Unique LFUnique, ThisUnique;
2897     char npath[128];
2898
2899     vid = rwIsp->volSummary->header.id;
2900     IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2901     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2902     assert(nBytes == sizeof(volHeader));
2903     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2904     assert(volHeader.destroyMe != DESTROY_ME);
2905     /* (should not have gotten this far with DESTROY_ME flag still set!) */
2906
2907     DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2908                        &maxunique);
2909     DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2910                        &maxunique);
2911
2912     dirVnodeInfo = &vnodeInfo[vLarge];
2913     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2914         SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2915                    &rootdirfound);
2916     }
2917 #ifdef AFS_NT40_ENV
2918     nt_sync(fileSysDevice);
2919 #else
2920     sync();                             /* This used to be done lower level, for every dir */
2921 #endif
2922     if (Showmode) {
2923         IH_RELEASE(h);
2924         return 0;
2925     }
2926
2927     /* Parse each vnode looking for orphaned vnodes and
2928      * connect them to the tree as orphaned (if requested).
2929      */
2930     oldrootdir = rootdir;
2931     for (class = 0; class < nVNODECLASSES; class++) {
2932         for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2933             vep = &(vnodeInfo[class].vnodes[v]);
2934             ThisVnode = bitNumberToVnodeNumber(v, class);
2935             ThisUnique = vep->unique;
2936
2937             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2938                 continue;       /* Ignore unused, claimed, and root vnodes */
2939
2940             /* This vnode is orphaned. If it is a directory vnode, then the '..'
2941              * entry in this vnode had incremented the parent link count (In
2942              * JudgeEntry()). We need to go to the parent and decrement that
2943              * link count. But if the parent's unique is zero, then the parent
2944              * link count was not incremented in JudgeEntry().
2945              */
2946             if (class == vLarge) {      /* directory vnode */
2947                 pv = vnodeIdToBitNumber(vep->parent);
2948                 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2949                     vnodeInfo[vLarge].vnodes[pv].count++;
2950             }
2951
2952             if (!rootdirfound)
2953                 continue;       /* If no rootdir, can't attach orphaned files */
2954
2955             /* Here we attach orphaned files and directories into the
2956              * root directory, LVVnode, making sure link counts stay correct.
2957              */
2958             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2959                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
2960                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
2961
2962                 /* Update this orphaned vnode's info. Its parent info and 
2963                  * link count (do for orphaned directories and files).
2964                  */
2965                 vep->parent = LFVnode;  /* Parent is the root dir */
2966                 vep->unique = LFUnique;
2967                 vep->changed = 1;
2968                 vep->claimed = 1;
2969                 vep->count--;   /* Inc link count (root dir will pt to it) */
2970
2971                 /* If this orphaned vnode is a directory, change '..'. 
2972                  * The name of the orphaned dir/file is unknown, so we
2973                  * build a unique name. No need to CopyOnWrite the directory
2974                  * since it is not connected to tree in BK or RO volume and
2975                  * won't be visible there.
2976                  */
2977                 if (class == vLarge) {
2978                     AFSFid pa;
2979                     DirHandle dh;
2980
2981                     /* Remove and recreate the ".." entry in this orphaned directory */
2982                     SetSalvageDirHandle(&dh, vid, fileSysDevice,
2983                                         vnodeInfo[class].inodes[v]);
2984                     pa.Vnode = LFVnode;
2985                     pa.Unique = LFUnique;
2986                     assert(Delete(&dh, "..") == 0);
2987                     assert(Create(&dh, "..", &pa) == 0);
2988
2989                     /* The original parent's link count was decremented above.
2990                      * Here we increment the new parent's link count.
2991                      */
2992                     pv = vnodeIdToBitNumber(LFVnode);
2993                     vnodeInfo[vLarge].vnodes[pv].count--;
2994
2995                 }
2996
2997                 /* Go to the root dir and add this entry. The link count of the
2998                  * root dir was incremented when ".." was created. Try 10 times.
2999                  */
3000                 for (j = 0; j < 10; j++) {
3001                     pa.Vnode = ThisVnode;
3002                     pa.Unique = ThisUnique;
3003
3004                     (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3005                                        ((class ==
3006                                          vLarge) ? "__ORPHANDIR__" :
3007                                         "__ORPHANFILE__"), ThisVnode,
3008                                        ThisUnique);
3009
3010                     CopyOnWrite(&rootdir);
3011                     code = Create(&rootdir.dirHandle, npath, &pa);
3012                     if (!code)
3013                         break;
3014
3015                     ThisUnique += 50;   /* Try creating a different file */
3016                 }
3017                 assert(code == 0);
3018                 Log("Attaching orphaned %s to volume's root dir as %s\n",
3019                     ((class == vLarge) ? "directory" : "file"), npath);
3020             }
3021         }                       /* for each vnode in the class */
3022     }                           /* for each class of vnode */
3023
3024     /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3025     DFlush();
3026     if (!oldrootdir.copied && rootdir.copied) {
3027         code =
3028             IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3029                    oldrootdir.rwVid);
3030         assert(code == 0);
3031         /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3032     }
3033
3034     DFlush();                   /* Flush the changes */
3035     if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3036         Log("Cannot attach orphaned files and directories: Root directory not found\n");
3037         orphans = ORPH_IGNORE;
3038     }
3039
3040     /* Write out all changed vnodes. Orphaned files and directories
3041      * will get removed here also (if requested).
3042      */
3043     for (class = 0; class < nVNODECLASSES; class++) {
3044         int nVnodes = vnodeInfo[class].nVnodes;
3045         struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3046         struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3047         FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3048         BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3049         for (i = 0; i < nVnodes; i++) {
3050             register struct VnodeEssence *vnp = &vnodes[i];
3051             VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3052
3053             /* If the vnode is good but is unclaimed (not listed in
3054              * any directory entries), then it is orphaned.
3055              */
3056             orphaned = -1;
3057             if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3058                 vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
3059                 vnp->changed = 1;
3060             }
3061
3062             if (vnp->changed || vnp->count) {
3063                 int oldCount;
3064                 int code;
3065                 nBytes =
3066                     IH_IREAD(vnodeInfo[class].handle,
3067                              vnodeIndexOffset(vcp, vnodeNumber),
3068                              (char *)&vnode, sizeof(vnode));
3069                 assert(nBytes == sizeof(vnode));
3070
3071                 vnode.parent = vnp->parent;
3072                 oldCount = vnode.linkCount;
3073                 vnode.linkCount = vnode.linkCount - vnp->count;
3074
3075                 if (orphaned == -1)
3076                     orphaned = IsVnodeOrphaned(vnodeNumber);
3077                 if (orphaned) {
3078                     if (!vnp->todelete) {
3079                         /* Orphans should have already been attached (if requested) */
3080                         assert(orphans != ORPH_ATTACH);
3081                         oblocks += vnp->blockCount;
3082                         ofiles++;
3083                     }
3084                     if (((orphans == ORPH_REMOVE) || vnp->todelete)
3085                         && !Testing) {
3086                         BlocksInVolume -= vnp->blockCount;
3087                         FilesInVolume--;
3088                         if (VNDISK_GET_INO(&vnode)) {
3089                             code =
3090                                 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3091                             assert(code == 0);
3092                         }
3093                         memset(&vnode, 0, sizeof(vnode));
3094                     }
3095                 } else if (vnp->count) {
3096                     if (!Showmode) {
3097                         Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3098                     }
3099                 } else {
3100                     vnode.modeBits = vnp->modeBits;
3101                 }
3102
3103                 vnode.dataVersion++;
3104                 if (!Testing) {
3105                     nBytes =
3106                         IH_IWRITE(vnodeInfo[class].handle,
3107                                   vnodeIndexOffset(vcp, vnodeNumber),
3108                                   (char *)&vnode, sizeof(vnode));
3109                     assert(nBytes == sizeof(vnode));
3110                 }
3111                 VolumeChanged = 1;
3112             }
3113         }
3114     }
3115     if (!Showmode && ofiles) {
3116         Log("%s %d orphaned files and directories (approx. %u KB)\n",
3117             (!Testing
3118              && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3119             oblocks);
3120     }
3121
3122     for (class = 0; class < nVNODECLASSES; class++) {
3123         register struct VnodeInfo *vip = &vnodeInfo[class];
3124         for (i = 0; i < vip->nVnodes; i++)
3125             if (vip->vnodes[i].name)
3126                 free(vip->vnodes[i].name);
3127         if (vip->vnodes)
3128             free(vip->vnodes);
3129         if (vip->inodes)
3130             free(vip->inodes);
3131     }
3132
3133     /* Set correct resource utilization statistics */
3134     volHeader.filecount = FilesInVolume;
3135     volHeader.diskused = BlocksInVolume;
3136
3137     /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3138     if (volHeader.uniquifier < (maxunique + 1)) {
3139         if (!Showmode)
3140             Log("Volume uniquifier is too low; fixed\n");
3141         /* Plus 2,000 in case there are workstations out there with
3142          * cached vnodes that have since been deleted
3143          */
3144         volHeader.uniquifier = (maxunique + 1 + 2000);
3145     }
3146
3147     /* Turn off the inUse bit; the volume's been salvaged! */
3148     volHeader.inUse = 0;        /* clear flag indicating inUse@last crash */
3149     volHeader.needsSalvaged = 0;        /* clear 'damaged' flag */
3150     volHeader.inService = 1;    /* allow service again */
3151     volHeader.needsCallback = (VolumeChanged != 0);
3152     volHeader.dontSalvage = DONT_SALVAGE;
3153     VolumeChanged = 0;
3154     if (!Testing) {
3155         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3156         assert(nBytes == sizeof(volHeader));
3157     }
3158     if (!Showmode) {
3159         Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3160             (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3161             FilesInVolume, BlocksInVolume);
3162     }
3163     IH_RELEASE(vnodeInfo[vSmall].handle);
3164     IH_RELEASE(vnodeInfo[vLarge].handle);
3165     IH_RELEASE(h);
3166     return 0;
3167 }
3168
3169 void
3170 ClearROInUseBit(struct VolumeSummary *summary)
3171 {
3172     IHandle_t *h = summary->volumeInfoHandle;
3173     afs_sfsize_t nBytes;
3174
3175     VolumeDiskData volHeader;
3176
3177     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3178     assert(nBytes == sizeof(volHeader));
3179     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3180     volHeader.inUse = 0;
3181     volHeader.needsSalvaged = 0;
3182     volHeader.inService = 1;
3183     volHeader.dontSalvage = DONT_SALVAGE;
3184     if (!Testing) {
3185         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3186         assert(nBytes == sizeof(volHeader));
3187     }
3188 }
3189
3190 /* MaybeZapVolume
3191  * Possible delete the volume.
3192  *
3193  * deleteMe - Always do so, only a partial volume.
3194  */
3195 void
3196 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3197                int check)
3198 {
3199     if (readOnly(isp) || deleteMe) {
3200         if (isp->volSummary && isp->volSummary->fileName) {
3201             if (deleteMe) {
3202                 if (!Showmode)
3203                     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);
3204                 if (!Showmode)
3205                     Log("It will be deleted on this server (you may find it elsewhere)\n");
3206             } else {
3207                 if (!Showmode)
3208                     Log("Volume %u needs to be salvaged.  Since it is read-only, however,\n", isp->volumeId);
3209                 if (!Showmode)
3210                     Log("it will be deleted instead.  It should be recloned.\n");
3211             }
3212             if (!Testing) {
3213                 char path[64];
3214                 sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
3215                 if (unlink(path)) {
3216                     Log("Unable to unlink %s (errno = %d)\n", path, errno);
3217                 }
3218             }
3219         }
3220     } else if (!check) {
3221         Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3222             isp->volumeId);
3223         Abort("Salvage of volume %u aborted\n", isp->volumeId);
3224     }
3225 }
3226
3227
3228 void
3229 AskOffline(VolumeId volumeId, char * partition)
3230 {
3231     afs_int32 code, i;
3232
3233     for (i = 0; i < 3; i++) {
3234         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3235
3236         if (code == SYNC_OK) {
3237             break;
3238         } else if (code == SYNC_DENIED) {
3239 #ifdef DEMAND_ATTACH_ENABLE
3240             Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
3241 #else
3242             Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
3243 #endif
3244             Abort("Salvage aborted\n");
3245         } else if (code == SYNC_BAD_COMMAND) {
3246             Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3247                 FSYNC_VOL_OFF);
3248 #ifdef DEMAND_ATTACH_ENABLE
3249             Log("AskOffline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3250 #else
3251             Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3252 #endif
3253             Abort("Salvage aborted\n");
3254         } else if (i < 2) {
3255             /* try it again */
3256             Log("AskOffline:  request for fileserver to take volume offline failed; trying again...\n");
3257             FSYNC_clientFinis();
3258             FSYNC_clientInit();
3259         }
3260     }
3261     if (code != SYNC_OK) {
3262         Log("AskOffline:  request for fileserver to take volume offline failed; salvage aborting.\n");
3263         Abort("Salvage aborted\n");
3264     }
3265
3266 #ifdef AFS_DEMAND_ATTACH_FS
3267     /* set inUse = programType in the volume header. We do this in case
3268      * the fileserver restarts/crashes while we are salvaging.
3269      * Otherwise, the fileserver could attach the volume again on
3270      * startup while we are salvaging, which would be very bad, or
3271      * schedule another salvage while we are salvaging, which would be
3272      * annoying. */
3273     if (!Testing) {
3274         int fd;
3275         IHandle_t *h;
3276         char name[VMAXPATHLEN];
3277         struct VolumeHeader header;
3278         struct VolumeDiskHeader diskHeader;
3279         struct VolumeDiskData volHeader;
3280
3281         afs_snprintf(name, sizeof(name), "%s/" VFORMAT, fileSysPathName,
3282             afs_printable_uint32_lu(volumeId));
3283
3284         fd = afs_open(name, O_RDONLY);
3285         if (fd < 0) {
3286             return;
3287         }
3288         if (read(fd, &diskHeader, sizeof(diskHeader)) != sizeof(diskHeader) ||
3289             diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
3290
3291             close(fd);
3292             return;
3293         }
3294         close(fd);
3295
3296         DiskToVolumeHeader(&header, &diskHeader);
3297
3298         IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
3299         if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
3300             volHeader.stamp.magic != VOLUMEINFOMAGIC) {
3301
3302             IH_RELEASE(h);
3303             return;
3304         }
3305
3306         volHeader.inUse = programType;
3307
3308         /* If we can't re-write the header, bail out and error. We don't
3309          * assert when reading the header, since it's possible the
3310          * header isn't really there (when there's no data associated
3311          * with the volume; we just delete the vol header file in that
3312          * case). But if it's there enough that we can read it, but
3313          * somehow we cannot write to it to signify we're salvaging it,
3314          * we've got a big problem and we cannot continue. */
3315         assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
3316
3317         IH_RELEASE(h);
3318     }
3319 #endif /* AFS_DEMAND_ATTACH_FS */
3320 }
3321
3322 void
3323 AskOnline(VolumeId volumeId, char *partition)
3324 {
3325     afs_int32 code, i;
3326
3327     for (i = 0; i < 3; i++) {
3328         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3329
3330         if (code == SYNC_OK) {
3331             break;
3332         } else if (code == SYNC_DENIED) {
3333             Log("AskOnline:  file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3334         } else if (code == SYNC_BAD_COMMAND) {
3335             Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
3336                 FSYNC_VOL_ON);
3337 #ifdef DEMAND_ATTACH_ENABLE
3338             Log("AskOnline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3339 #else
3340             Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3341 #endif
3342             break;
3343         } else if (i < 2) {
3344             /* try it again */
3345             Log("AskOnline:  request for fileserver to take volume offline failed; trying again...\n");
3346             FSYNC_clientFinis();
3347             FSYNC_clientInit();
3348         }
3349     }
3350 }
3351
3352 int
3353 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3354 {
3355     /* Volume parameter is passed in case iopen is upgraded in future to
3356      * require a volume Id to be passed
3357      */
3358     char buf[4096];
3359     IHandle_t *srcH, *destH;
3360     FdHandle_t *srcFdP, *destFdP;
3361     register int n = 0;
3362
3363     IH_INIT(srcH, device, rwvolume, inode1);
3364     srcFdP = IH_OPEN(srcH);
3365     assert(srcFdP != NULL);
3366     IH_INIT(destH, device, rwvolume, inode2);
3367     destFdP = IH_OPEN(destH);
3368     assert(n != -1);
3369     while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3370         assert(FDH_WRITE(destFdP, buf, n) == n);
3371     assert(n == 0);
3372     FDH_REALLYCLOSE(srcFdP);
3373     FDH_REALLYCLOSE(destFdP);
3374     IH_RELEASE(srcH);
3375     IH_RELEASE(destH);
3376     return 0;
3377 }
3378
3379 void
3380 PrintInodeList(void)
3381 {
3382     register struct ViceInodeInfo *ip;
3383     struct ViceInodeInfo *buf;
3384     struct afs_stat status;
3385     register int nInodes;
3386
3387     assert(afs_fstat(inodeFd, &status) == 0);
3388     buf = (struct ViceInodeInfo *)malloc(status.st_size);
3389     assert(buf != NULL);
3390     nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3391     assert(read(inodeFd, buf, status.st_size) == status.st_size);
3392     for (ip = buf; nInodes--; ip++) {
3393         Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3394             PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3395             (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3396             ip->u.param[2], ip->u.param[3]);
3397     }
3398     free(buf);
3399 }
3400
3401 void
3402 PrintInodeSummary(void)
3403 {
3404     int i;
3405     struct InodeSummary *isp;
3406
3407     for (i = 0; i < nVolumesInInodeFile; i++) {
3408         isp = &inodeSummary[i];
3409         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);
3410     }
3411 }
3412
3413 void
3414 PrintVolumeSummary(void)
3415 {
3416     int i;
3417     struct VolumeSummary *vsp;
3418
3419     for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3420         Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3421     }
3422 }
3423
3424 int
3425 Fork(void)
3426 {
3427     int f;
3428 #ifdef AFS_NT40_ENV
3429     f = 0;
3430     assert(0);                  /* Fork is never executed in the NT code path */
3431 #else
3432     f = fork();
3433     assert(f >= 0);
3434 #ifdef AFS_DEMAND_ATTACH_FS
3435     if ((f == 0) && (programType == salvageServer)) {
3436         /* we are a salvageserver child */
3437 #ifdef FSSYNC_BUILD_CLIENT
3438         VChildProcReconnectFS_r();
3439 #endif
3440 #ifdef SALVSYNC_BUILD_CLIENT
3441         VReconnectSALV_r();
3442 #endif
3443     }
3444 #endif /* AFS_DEMAND_ATTACH_FS */
3445 #endif /* !AFS_NT40_ENV */
3446     return f;
3447 }
3448
3449 void
3450 Exit(int code)
3451 {
3452     if (ShowLog)
3453         showlog();
3454
3455 #ifdef AFS_DEMAND_ATTACH_FS
3456     if (programType == salvageServer) {
3457 #ifdef SALVSYNC_BUILD_CLIENT
3458         VDisconnectSALV();
3459 #endif
3460 #ifdef FSSYNC_BUILD_CLIENT
3461         VDisconnectFS();
3462 #endif
3463     }
3464 #endif /* AFS_DEMAND_ATTACH_FS */
3465
3466 #ifdef AFS_NT40_ENV
3467     if (main_thread != pthread_self())
3468         pthread_exit((void *)code);
3469     else
3470         exit(code);
3471 #else
3472     exit(code);
3473 #endif
3474 }
3475
3476 int
3477 Wait(char *prog)
3478 {
3479     int status;
3480     int pid;
3481     pid = wait(&status);
3482     assert(pid != -1);
3483     if (WCOREDUMP(status))
3484         Log("\"%s\" core dumped!\n", prog);
3485     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3486         return -1;
3487     return pid;
3488 }
3489
3490 static char *
3491 TimeStamp(time_t clock, int precision)
3492 {
3493     struct tm *lt;
3494     static char timestamp[20];
3495     lt = localtime(&clock);
3496     if (precision)
3497         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
3498     else
3499         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3500     return timestamp;
3501 }
3502
3503 void
3504 CheckLogFile(char * log_path)
3505 {
3506     char oldSlvgLog[AFSDIR_PATH_MAX];
3507
3508 #ifndef AFS_NT40_ENV
3509     if (useSyslog) {
3510         ShowLog = 0;
3511         return;
3512     }
3513 #endif
3514
3515     strcpy(oldSlvgLog, log_path);
3516     strcat(oldSlvgLog, ".old");
3517     if (!logFile) {
3518         renamefile(log_path, oldSlvgLog);
3519         logFile = afs_fopen(log_path, "a");
3520
3521         if (!logFile) {         /* still nothing, use stdout */
3522             logFile = stdout;
3523             ShowLog = 0;
3524         }
3525 #ifndef AFS_NAMEI_ENV
3526         AFS_DEBUG_IOPS_LOG(logFile);
3527 #endif
3528     }
3529 }
3530
3531 #ifndef AFS_NT40_ENV
3532 void
3533 TimeStampLogFile(char * log_path)
3534 {
3535     char stampSlvgLog[AFSDIR_PATH_MAX];
3536     struct tm *lt;
3537     time_t now;
3538
3539     now = time(0);
3540     lt = localtime(&now);
3541     (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3542                        "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3543                        log_path, lt->tm_year + 1900,
3544                        lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3545                        lt->tm_sec);
3546
3547     /* try to link the logfile to a timestamped filename */
3548     /* if it fails, oh well, nothing we can do */
3549     link(log_path, stampSlvgLog);
3550 }
3551 #endif
3552
3553 void
3554 showlog(void)
3555 {
3556     char line[256];
3557
3558 #ifndef AFS_NT40_ENV
3559     if (useSyslog) {
3560         printf("Can't show log since using syslog.\n");
3561         fflush(stdout);
3562         return;
3563     }
3564 #endif
3565
3566     if (logFile) {
3567         rewind(logFile);
3568         fclose(logFile);
3569     }
3570
3571     logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3572
3573     if (!logFile)
3574         printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3575     else {
3576         rewind(logFile);
3577         while (fgets(line, sizeof(line), logFile))
3578             printf("%s", line);
3579         fflush(stdout);
3580     }
3581 }
3582
3583 void
3584 Log(const char *format, ...)
3585 {
3586     struct timeval now;
3587     char tmp[1024];
3588     va_list args;
3589
3590     va_start(args, format);
3591     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3592     va_end(args);
3593 #ifndef AFS_NT40_ENV
3594     if (useSyslog) {
3595         syslog(LOG_INFO, "%s", tmp);
3596     } else
3597 #endif
3598         if (logFile) {
3599             gettimeofday(&now, 0);
3600             fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3601             fflush(logFile);
3602         }
3603 }
3604
3605 void
3606 Abort(const char *format, ...)
3607 {
3608     va_list args;
3609     char tmp[1024];
3610
3611     va_start(args, format);
3612     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3613     va_end(args);
3614 #ifndef AFS_NT40_ENV
3615     if (useSyslog) {
3616         syslog(LOG_INFO, "%s", tmp);
3617     } else
3618 #endif
3619         if (logFile) {
3620             fprintf(logFile, "%s", tmp);
3621             fflush(logFile);
3622             if (ShowLog)
3623                 showlog();
3624         }
3625
3626     if (debug)
3627         abort();
3628     Exit(1);
3629 }
3630
3631 char *
3632 ToString(char *s)
3633 {
3634     register char *p;
3635     p = (char *)malloc(strlen(s) + 1);
3636     assert(p != NULL);
3637     strcpy(p, s);
3638     return p;
3639 }
3640
3641 /* Remove the FORCESALVAGE file */
3642 void
3643 RemoveTheForce(char *path)
3644 {
3645     char target[1024];
3646     struct afs_stat force; /* so we can use afs_stat to find it */
3647     strcpy(target,path);
3648     strcat(target,"/FORCESALVAGE");
3649     if (!Testing && ForceSalvage) {
3650         if (afs_stat(target,&force) == 0)  unlink(target);
3651     }
3652 }
3653
3654 #ifndef AFS_AIX32_ENV
3655 /*
3656  * UseTheForceLuke -    see if we can use the force
3657  */
3658 int
3659 UseTheForceLuke(char *path)
3660 {
3661     struct afs_stat force;
3662     char target[1024];
3663     strcpy(target,path);
3664     strcat(target,"/FORCESALVAGE");
3665
3666     return (afs_stat(target, &force) == 0);
3667 }
3668 #else
3669 /*
3670  * UseTheForceLuke -    see if we can use the force
3671  *
3672  * NOTE:
3673  *      The VRMIX fsck will not muck with the filesystem it is supposedly
3674  *      fixing and create a "FORCESALVAGE" file (by design).  Instead, we
3675  *      muck directly with the root inode, which is within the normal
3676  *      domain of fsck.
3677  *      ListViceInodes() has a side effect of setting ForceSalvage if
3678  *      it detects a need, based on root inode examination.
3679  */
3680 int
3681 UseTheForceLuke(char *path)
3682 {
3683
3684     return 0;                   /* sorry OB1    */
3685 }
3686 #endif
3687
3688 #ifdef AFS_NT40_ENV
3689 /* NT support routines */
3690
3691 static char execpathname[MAX_PATH];
3692 int
3693 nt_SalvagePartition(char *partName, int jobn)
3694 {
3695     int pid;
3696     int n;
3697     childJob_t job;
3698     if (!*execpathname) {
3699         n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3700         if (!n || n == 1023)
3701             return -1;
3702     }
3703     job.cj_magic = SALVAGER_MAGIC;
3704     job.cj_number = jobn;
3705     (void)strcpy(job.cj_part, partName);
3706     pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3707     return pid;
3708 }
3709
3710 int
3711 nt_SetupPartitionSalvage(void *datap, int len)
3712 {
3713     childJob_t *jobp = (childJob_t *) datap;
3714     char logname[AFSDIR_PATH_MAX];
3715
3716     if (len != sizeof(childJob_t))
3717         return -1;
3718     if (jobp->cj_magic != SALVAGER_MAGIC)
3719         return -1;
3720     myjob = *jobp;
3721
3722     /* Open logFile */
3723     (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3724                   myjob.cj_number);
3725     logFile = afs_fopen(logname, "w");
3726     if (!logFile)
3727         logFile = stdout;
3728
3729     return 0;
3730 }
3731
3732
3733 #endif /* AFS_NT40_ENV */