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