a0986b0ad871587aecd2b4fef4da4205e6acdf55
[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                 if (class != vLarge) {
2720                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2721                     vip->nAllocatedVnodes--;
2722                     memset(vnode, 0, sizeof(vnode));
2723                     IH_IWRITE(vnodeInfo[vSmall].handle,
2724                               vnodeIndexOffset(vcp, vnodeNumber),
2725                               (char *)&vnode, sizeof(vnode));
2726                     VolumeChanged = 1;
2727                 } else
2728                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2729             }
2730         }
2731     }
2732     STREAM_CLOSE(file);
2733     FDH_CLOSE(fdP);
2734 }
2735
2736 static char *
2737 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2738 {
2739     struct VnodeEssence *parentvp;
2740
2741     if (vnode == 1) {
2742         strcpy(path, ".");
2743         return path;
2744     }
2745     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2746         && GetDirName(vp->parent, parentvp, path)) {
2747         strcat(path, "/");
2748         strcat(path, vp->name);
2749         return path;
2750     }
2751     return 0;
2752 }
2753
2754 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2755  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2756  */
2757 static int
2758 IsVnodeOrphaned(VnodeId vnode)
2759 {
2760     struct VnodeEssence *vep;
2761
2762     if (vnode == 0)
2763         return (1);             /* Vnode zero does not exist */
2764     if (vnode == 1)
2765         return (0);             /* The root dir vnode is always claimed */
2766     vep = CheckVnodeNumber(vnode);      /* Get the vnode essence */
2767     if (!vep || !vep->claimed)
2768         return (1);             /* Vnode is not claimed - it is orphaned */
2769
2770     return (IsVnodeOrphaned(vep->parent));
2771 }
2772
2773 void
2774 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2775            IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2776            int *rootdirfound)
2777 {
2778     static struct DirSummary dir;
2779     static struct DirHandle dirHandle;
2780     struct VnodeEssence *parent;
2781     static char path[MAXPATHLEN];
2782     int dirok, code;
2783
2784     if (dirVnodeInfo->vnodes[i].salvaged)
2785         return;                 /* already salvaged */
2786
2787     dir.rwVid = rwVid;
2788     dirVnodeInfo->vnodes[i].salvaged = 1;
2789
2790     if (dirVnodeInfo->inodes[i] == 0)
2791         return;                 /* Not allocated to a directory */
2792
2793     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2794         if (dirVnodeInfo->vnodes[i].parent) {
2795             Log("Bad parent, vnode 1; %s...\n",
2796                 (Testing ? "skipping" : "salvaging"));
2797             dirVnodeInfo->vnodes[i].parent = 0;
2798             dirVnodeInfo->vnodes[i].changed = 1;
2799         }
2800     } else {
2801         parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2802         if (parent && parent->salvaged == 0)
2803             SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2804                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2805                        rootdir, rootdirfound);
2806     }
2807
2808     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2809     dir.unique = dirVnodeInfo->vnodes[i].unique;
2810     dir.copied = 0;
2811     dir.vname = name;
2812     dir.parent = dirVnodeInfo->vnodes[i].parent;
2813     dir.haveDot = dir.haveDotDot = 0;
2814     dir.ds_linkH = alinkH;
2815     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2816                         dirVnodeInfo->inodes[i]);
2817
2818     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2819     if (!dirok) {
2820         if (!RebuildDirs) {
2821             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2822                 (Testing ? "skipping" : "salvaging"));
2823         }
2824         if (!Testing) {
2825             CopyAndSalvage(&dir);
2826             dirok = 1;
2827         }
2828     }
2829     dirHandle = dir.dirHandle;
2830
2831     dir.name =
2832         GetDirName(bitNumberToVnodeNumber(i, vLarge),
2833                    &dirVnodeInfo->vnodes[i], path);
2834
2835     if (dirok) {
2836         /* If enumeration failed for random reasons, we will probably delete
2837          * too much stuff, so we guard against this instead.
2838          */
2839         assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2840     }
2841
2842     /* Delete the old directory if it was copied in order to salvage.
2843      * CopyOnWrite has written the new inode # to the disk, but we still
2844      * have the old one in our local structure here.  Thus, we idec the
2845      * local dude.
2846      */
2847     DFlush();
2848     if (dir.copied && !Testing) {
2849         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2850         assert(code == 0);
2851         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2852     }
2853
2854     /* Remember rootdir DirSummary _after_ it has been judged */
2855     if (dir.vnodeNumber == 1 && dir.unique == 1) {
2856         memcpy(rootdir, &dir, sizeof(struct DirSummary));
2857         *rootdirfound = 1;
2858     }
2859
2860     return;
2861 }
2862
2863 int
2864 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2865 {
2866     /* This routine, for now, will only be called for read-write volumes */
2867     int i, j, code;
2868     int BlocksInVolume = 0, FilesInVolume = 0;
2869     register VnodeClass class;
2870     struct DirSummary rootdir, oldrootdir;
2871     struct VnodeInfo *dirVnodeInfo;
2872     struct VnodeDiskObject vnode;
2873     VolumeDiskData volHeader;
2874     VolumeId vid;
2875     int orphaned, rootdirfound = 0;
2876     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
2877     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
2878     struct VnodeEssence *vep;
2879     afs_int32 v, pv;
2880     IHandle_t *h;
2881     afs_sfsize_t nBytes;
2882     AFSFid pa;
2883     VnodeId LFVnode, ThisVnode;
2884     Unique LFUnique, ThisUnique;
2885     char npath[128];
2886
2887     vid = rwIsp->volSummary->header.id;
2888     IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2889     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2890     assert(nBytes == sizeof(volHeader));
2891     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2892     assert(volHeader.destroyMe != DESTROY_ME);
2893     /* (should not have gotten this far with DESTROY_ME flag still set!) */
2894
2895     DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2896                        &maxunique);
2897     DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2898                        &maxunique);
2899
2900     dirVnodeInfo = &vnodeInfo[vLarge];
2901     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2902         SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2903                    &rootdirfound);
2904     }
2905 #ifdef AFS_NT40_ENV
2906     nt_sync(fileSysDevice);
2907 #else
2908     sync();                             /* This used to be done lower level, for every dir */
2909 #endif
2910     if (Showmode) {
2911         IH_RELEASE(h);
2912         return 0;
2913     }
2914
2915     /* Parse each vnode looking for orphaned vnodes and
2916      * connect them to the tree as orphaned (if requested).
2917      */
2918     oldrootdir = rootdir;
2919     for (class = 0; class < nVNODECLASSES; class++) {
2920         for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2921             vep = &(vnodeInfo[class].vnodes[v]);
2922             ThisVnode = bitNumberToVnodeNumber(v, class);
2923             ThisUnique = vep->unique;
2924
2925             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2926                 continue;       /* Ignore unused, claimed, and root vnodes */
2927
2928             /* This vnode is orphaned. If it is a directory vnode, then the '..'
2929              * entry in this vnode had incremented the parent link count (In
2930              * JudgeEntry()). We need to go to the parent and decrement that
2931              * link count. But if the parent's unique is zero, then the parent
2932              * link count was not incremented in JudgeEntry().
2933              */
2934             if (class == vLarge) {      /* directory vnode */
2935                 pv = vnodeIdToBitNumber(vep->parent);
2936                 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2937                     vnodeInfo[vLarge].vnodes[pv].count++;
2938             }
2939
2940             if (!rootdirfound)
2941                 continue;       /* If no rootdir, can't attach orphaned files */
2942
2943             /* Here we attach orphaned files and directories into the
2944              * root directory, LVVnode, making sure link counts stay correct.
2945              */
2946             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2947                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
2948                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
2949
2950                 /* Update this orphaned vnode's info. Its parent info and 
2951                  * link count (do for orphaned directories and files).
2952                  */
2953                 vep->parent = LFVnode;  /* Parent is the root dir */
2954                 vep->unique = LFUnique;
2955                 vep->changed = 1;
2956                 vep->claimed = 1;
2957                 vep->count--;   /* Inc link count (root dir will pt to it) */
2958
2959                 /* If this orphaned vnode is a directory, change '..'. 
2960                  * The name of the orphaned dir/file is unknown, so we
2961                  * build a unique name. No need to CopyOnWrite the directory
2962                  * since it is not connected to tree in BK or RO volume and
2963                  * won't be visible there.
2964                  */
2965                 if (class == vLarge) {
2966                     AFSFid pa;
2967                     DirHandle dh;
2968
2969                     /* Remove and recreate the ".." entry in this orphaned directory */
2970                     SetSalvageDirHandle(&dh, vid, fileSysDevice,
2971                                         vnodeInfo[class].inodes[v]);
2972                     pa.Vnode = LFVnode;
2973                     pa.Unique = LFUnique;
2974                     assert(Delete(&dh, "..") == 0);
2975                     assert(Create(&dh, "..", &pa) == 0);
2976
2977                     /* The original parent's link count was decremented above.
2978                      * Here we increment the new parent's link count.
2979                      */
2980                     pv = vnodeIdToBitNumber(LFVnode);
2981                     vnodeInfo[vLarge].vnodes[pv].count--;
2982
2983                 }
2984
2985                 /* Go to the root dir and add this entry. The link count of the
2986                  * root dir was incremented when ".." was created. Try 10 times.
2987                  */
2988                 for (j = 0; j < 10; j++) {
2989                     pa.Vnode = ThisVnode;
2990                     pa.Unique = ThisUnique;
2991
2992                     (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2993                                        ((class ==
2994                                          vLarge) ? "__ORPHANDIR__" :
2995                                         "__ORPHANFILE__"), ThisVnode,
2996                                        ThisUnique);
2997
2998                     CopyOnWrite(&rootdir);
2999                     code = Create(&rootdir.dirHandle, npath, &pa);
3000                     if (!code)
3001                         break;
3002
3003                     ThisUnique += 50;   /* Try creating a different file */
3004                 }
3005                 assert(code == 0);
3006                 Log("Attaching orphaned %s to volume's root dir as %s\n",
3007                     ((class == vLarge) ? "directory" : "file"), npath);
3008             }
3009         }                       /* for each vnode in the class */
3010     }                           /* for each class of vnode */
3011
3012     /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3013     DFlush();
3014     if (!oldrootdir.copied && rootdir.copied) {
3015         code =
3016             IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3017                    oldrootdir.rwVid);
3018         assert(code == 0);
3019         /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3020     }
3021
3022     DFlush();                   /* Flush the changes */
3023     if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3024         Log("Cannot attach orphaned files and directories: Root directory not found\n");
3025         orphans = ORPH_IGNORE;
3026     }
3027
3028     /* Write out all changed vnodes. Orphaned files and directories
3029      * will get removed here also (if requested).
3030      */
3031     for (class = 0; class < nVNODECLASSES; class++) {
3032         int nVnodes = vnodeInfo[class].nVnodes;
3033         struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3034         struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3035         FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3036         BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3037         for (i = 0; i < nVnodes; i++) {
3038             register struct VnodeEssence *vnp = &vnodes[i];
3039             VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3040
3041             /* If the vnode is good but is unclaimed (not listed in
3042              * any directory entries), then it is orphaned.
3043              */
3044             orphaned = -1;
3045             if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3046                 vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
3047                 vnp->changed = 1;
3048             }
3049
3050             if (vnp->changed || vnp->count) {
3051                 int oldCount;
3052                 int code;
3053                 nBytes =
3054                     IH_IREAD(vnodeInfo[class].handle,
3055                              vnodeIndexOffset(vcp, vnodeNumber),
3056                              (char *)&vnode, sizeof(vnode));
3057                 assert(nBytes == sizeof(vnode));
3058
3059                 vnode.parent = vnp->parent;
3060                 oldCount = vnode.linkCount;
3061                 vnode.linkCount = vnode.linkCount - vnp->count;
3062
3063                 if (orphaned == -1)
3064                     orphaned = IsVnodeOrphaned(vnodeNumber);
3065                 if (orphaned) {
3066                     if (!vnp->todelete) {
3067                         /* Orphans should have already been attached (if requested) */
3068                         assert(orphans != ORPH_ATTACH);
3069                         oblocks += vnp->blockCount;
3070                         ofiles++;
3071                     }
3072                     if (((orphans == ORPH_REMOVE) || vnp->todelete)
3073                         && !Testing) {
3074                         BlocksInVolume -= vnp->blockCount;
3075                         FilesInVolume--;
3076                         if (VNDISK_GET_INO(&vnode)) {
3077                             code =
3078                                 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3079                             assert(code == 0);
3080                         }
3081                         memset(&vnode, 0, sizeof(vnode));
3082                     }
3083                 } else if (vnp->count) {
3084                     if (!Showmode) {
3085                         Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3086                     }
3087                 } else {
3088                     vnode.modeBits = vnp->modeBits;
3089                 }
3090
3091                 vnode.dataVersion++;
3092                 if (!Testing) {
3093                     nBytes =
3094                         IH_IWRITE(vnodeInfo[class].handle,
3095                                   vnodeIndexOffset(vcp, vnodeNumber),
3096                                   (char *)&vnode, sizeof(vnode));
3097                     assert(nBytes == sizeof(vnode));
3098                 }
3099                 VolumeChanged = 1;
3100             }
3101         }
3102     }
3103     if (!Showmode && ofiles) {
3104         Log("%s %d orphaned files and directories (approx. %u KB)\n",
3105             (!Testing
3106              && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3107             oblocks);
3108     }
3109
3110     for (class = 0; class < nVNODECLASSES; class++) {
3111         register struct VnodeInfo *vip = &vnodeInfo[class];
3112         for (i = 0; i < vip->nVnodes; i++)
3113             if (vip->vnodes[i].name)
3114                 free(vip->vnodes[i].name);
3115         if (vip->vnodes)
3116             free(vip->vnodes);
3117         if (vip->inodes)
3118             free(vip->inodes);
3119     }
3120
3121     /* Set correct resource utilization statistics */
3122     volHeader.filecount = FilesInVolume;
3123     volHeader.diskused = BlocksInVolume;
3124
3125     /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3126     if (volHeader.uniquifier < (maxunique + 1)) {
3127         if (!Showmode)
3128             Log("Volume uniquifier is too low; fixed\n");
3129         /* Plus 2,000 in case there are workstations out there with
3130          * cached vnodes that have since been deleted
3131          */
3132         volHeader.uniquifier = (maxunique + 1 + 2000);
3133     }
3134
3135     /* Turn off the inUse bit; the volume's been salvaged! */
3136     volHeader.inUse = 0;        /* clear flag indicating inUse@last crash */
3137     volHeader.needsSalvaged = 0;        /* clear 'damaged' flag */
3138     volHeader.inService = 1;    /* allow service again */
3139     volHeader.needsCallback = (VolumeChanged != 0);
3140     volHeader.dontSalvage = DONT_SALVAGE;
3141     VolumeChanged = 0;
3142     if (!Testing) {
3143         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3144         assert(nBytes == sizeof(volHeader));
3145     }
3146     if (!Showmode) {
3147         Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3148             (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3149             FilesInVolume, BlocksInVolume);
3150     }
3151     IH_RELEASE(vnodeInfo[vSmall].handle);
3152     IH_RELEASE(vnodeInfo[vLarge].handle);
3153     IH_RELEASE(h);
3154     return 0;
3155 }
3156
3157 void
3158 ClearROInUseBit(struct VolumeSummary *summary)
3159 {
3160     IHandle_t *h = summary->volumeInfoHandle;
3161     afs_sfsize_t nBytes;
3162
3163     VolumeDiskData volHeader;
3164
3165     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3166     assert(nBytes == sizeof(volHeader));
3167     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3168     volHeader.inUse = 0;
3169     volHeader.needsSalvaged = 0;
3170     volHeader.inService = 1;
3171     volHeader.dontSalvage = DONT_SALVAGE;
3172     if (!Testing) {
3173         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3174         assert(nBytes == sizeof(volHeader));
3175     }
3176 }
3177
3178 /* MaybeZapVolume
3179  * Possible delete the volume.
3180  *
3181  * deleteMe - Always do so, only a partial volume.
3182  */
3183 void
3184 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3185                int check)
3186 {
3187     if (readOnly(isp) || deleteMe) {
3188         if (isp->volSummary && isp->volSummary->fileName) {
3189             if (deleteMe) {
3190                 if (!Showmode)
3191                     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);
3192                 if (!Showmode)
3193                     Log("It will be deleted on this server (you may find it elsewhere)\n");
3194             } else {
3195                 if (!Showmode)
3196                     Log("Volume %u needs to be salvaged.  Since it is read-only, however,\n", isp->volumeId);
3197                 if (!Showmode)
3198                     Log("it will be deleted instead.  It should be recloned.\n");
3199             }
3200             if (!Testing)
3201                 unlink(isp->volSummary->fileName);
3202         }
3203     } else if (!check) {
3204         Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3205             isp->volumeId);
3206         Abort("Salvage of volume %u aborted\n", isp->volumeId);
3207     }
3208 }
3209
3210
3211 void
3212 AskOffline(VolumeId volumeId, char * partition)
3213 {
3214     afs_int32 code, i;
3215
3216     for (i = 0; i < 3; i++) {
3217         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3218
3219         if (code == SYNC_OK) {
3220             break;
3221         } else if (code == SYNC_DENIED) {
3222 #ifdef DEMAND_ATTACH_ENABLE
3223             Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
3224 #else
3225             Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
3226 #endif
3227             Abort("Salvage aborted\n");
3228         } else if (code == SYNC_BAD_COMMAND) {
3229             Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3230                 FSYNC_VOL_OFF);
3231 #ifdef DEMAND_ATTACH_ENABLE
3232             Log("AskOffline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3233 #else
3234             Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3235 #endif
3236             Abort("Salvage aborted\n");
3237         } else if (i < 2) {
3238             /* try it again */
3239             Log("AskOffline:  request for fileserver to take volume offline failed; trying again...\n");
3240             FSYNC_clientFinis();
3241             FSYNC_clientInit();
3242         }
3243     }
3244     if (code != SYNC_OK) {
3245         Log("AskOffline:  request for fileserver to take volume offline failed; salvage aborting.\n");
3246         Abort("Salvage aborted\n");
3247     }
3248
3249 #ifdef AFS_DEMAND_ATTACH_FS
3250     /* set inUse = programType in the volume header. We do this in case
3251      * the fileserver restarts/crashes while we are salvaging.
3252      * Otherwise, the fileserver could attach the volume again on
3253      * startup while we are salvaging, which would be very bad, or
3254      * schedule another salvage while we are salvaging, which would be
3255      * annoying. */
3256     if (!Testing) {
3257         int fd;
3258         IHandle_t *h;
3259         char name[VMAXPATHLEN];
3260         struct VolumeHeader header;
3261         struct VolumeDiskHeader diskHeader;
3262         struct VolumeDiskData volHeader;
3263
3264         afs_snprintf(name, sizeof(name), "%s/" VFORMAT, fileSysPathName,
3265             afs_printable_uint32_lu(volumeId));
3266
3267         fd = afs_open(name, O_RDONLY);
3268         assert(fd >= 0);
3269         assert(read(fd, &diskHeader, sizeof(diskHeader)) == sizeof(diskHeader));
3270         assert(diskHeader.stamp.magic == VOLUMEHEADERMAGIC);
3271         close(fd);
3272
3273         DiskToVolumeHeader(&header, &diskHeader);
3274
3275         IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
3276         assert(IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
3277         assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3278
3279         volHeader.inUse = programType;
3280
3281         assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
3282
3283         IH_RELEASE(h);
3284     }
3285 #endif /* AFS_DEMAND_ATTACH_FS */
3286 }
3287
3288 void
3289 AskOnline(VolumeId volumeId, char *partition)
3290 {
3291     afs_int32 code, i;
3292
3293     for (i = 0; i < 3; i++) {
3294         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3295
3296         if (code == SYNC_OK) {
3297             break;
3298         } else if (code == SYNC_DENIED) {
3299             Log("AskOnline:  file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3300         } else if (code == SYNC_BAD_COMMAND) {
3301             Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
3302                 FSYNC_VOL_ON);
3303 #ifdef DEMAND_ATTACH_ENABLE
3304             Log("AskOnline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3305 #else
3306             Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3307 #endif
3308             break;
3309         } else if (i < 2) {
3310             /* try it again */
3311             Log("AskOnline:  request for fileserver to take volume offline failed; trying again...\n");
3312             FSYNC_clientFinis();
3313             FSYNC_clientInit();
3314         }
3315     }
3316 }
3317
3318 int
3319 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3320 {
3321     /* Volume parameter is passed in case iopen is upgraded in future to
3322      * require a volume Id to be passed
3323      */
3324     char buf[4096];
3325     IHandle_t *srcH, *destH;
3326     FdHandle_t *srcFdP, *destFdP;
3327     register int n = 0;
3328
3329     IH_INIT(srcH, device, rwvolume, inode1);
3330     srcFdP = IH_OPEN(srcH);
3331     assert(srcFdP != NULL);
3332     IH_INIT(destH, device, rwvolume, inode2);
3333     destFdP = IH_OPEN(destH);
3334     assert(n != -1);
3335     while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3336         assert(FDH_WRITE(destFdP, buf, n) == n);
3337     assert(n == 0);
3338     FDH_REALLYCLOSE(srcFdP);
3339     FDH_REALLYCLOSE(destFdP);
3340     IH_RELEASE(srcH);
3341     IH_RELEASE(destH);
3342     return 0;
3343 }
3344
3345 void
3346 PrintInodeList(void)
3347 {
3348     register struct ViceInodeInfo *ip;
3349     struct ViceInodeInfo *buf;
3350     struct afs_stat status;
3351     register int nInodes;
3352
3353     assert(afs_fstat(inodeFd, &status) == 0);
3354     buf = (struct ViceInodeInfo *)malloc(status.st_size);
3355     assert(buf != NULL);
3356     nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3357     assert(read(inodeFd, buf, status.st_size) == status.st_size);
3358     for (ip = buf; nInodes--; ip++) {
3359         Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3360             PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3361             (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3362             ip->u.param[2], ip->u.param[3]);
3363     }
3364     free(buf);
3365 }
3366
3367 void
3368 PrintInodeSummary(void)
3369 {
3370     int i;
3371     struct InodeSummary *isp;
3372
3373     for (i = 0; i < nVolumesInInodeFile; i++) {
3374         isp = &inodeSummary[i];
3375         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);
3376     }
3377 }
3378
3379 void
3380 PrintVolumeSummary(void)
3381 {
3382     int i;
3383     struct VolumeSummary *vsp;
3384
3385     for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3386         Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3387     }
3388 }
3389
3390 int
3391 Fork(void)
3392 {
3393     int f;
3394 #ifdef AFS_NT40_ENV
3395     f = 0;
3396     assert(0);                  /* Fork is never executed in the NT code path */
3397 #else
3398     f = fork();
3399     assert(f >= 0);
3400 #ifdef AFS_DEMAND_ATTACH_FS
3401     if ((f == 0) && (programType == salvageServer)) {
3402         /* we are a salvageserver child */
3403 #ifdef FSSYNC_BUILD_CLIENT
3404         VChildProcReconnectFS_r();
3405 #endif
3406 #ifdef SALVSYNC_BUILD_CLIENT
3407         VReconnectSALV_r();
3408 #endif
3409     }
3410 #endif /* AFS_DEMAND_ATTACH_FS */
3411 #endif /* !AFS_NT40_ENV */
3412     return f;
3413 }
3414
3415 void
3416 Exit(int code)
3417 {
3418     if (ShowLog)
3419         showlog();
3420
3421 #ifdef AFS_DEMAND_ATTACH_FS
3422     if (programType == salvageServer) {
3423 #ifdef SALVSYNC_BUILD_CLIENT
3424         VDisconnectSALV();
3425 #endif
3426 #ifdef FSSYNC_BUILD_CLIENT
3427         VDisconnectFS();
3428 #endif
3429     }
3430 #endif /* AFS_DEMAND_ATTACH_FS */
3431
3432 #ifdef AFS_NT40_ENV
3433     if (main_thread != pthread_self())
3434         pthread_exit((void *)code);
3435     else
3436         exit(code);
3437 #else
3438     exit(code);
3439 #endif
3440 }
3441
3442 int
3443 Wait(char *prog)
3444 {
3445     int status;
3446     int pid;
3447     pid = wait(&status);
3448     assert(pid != -1);
3449     if (WCOREDUMP(status))
3450         Log("\"%s\" core dumped!\n", prog);
3451     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3452         return -1;
3453     return pid;
3454 }
3455
3456 static char *
3457 TimeStamp(time_t clock, int precision)
3458 {
3459     struct tm *lt;
3460     static char timestamp[20];
3461     lt = localtime(&clock);
3462     if (precision)
3463         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
3464     else
3465         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3466     return timestamp;
3467 }
3468
3469 void
3470 CheckLogFile(char * log_path)
3471 {
3472     char oldSlvgLog[AFSDIR_PATH_MAX];
3473
3474 #ifndef AFS_NT40_ENV
3475     if (useSyslog) {
3476         ShowLog = 0;
3477         return;
3478     }
3479 #endif
3480
3481     strcpy(oldSlvgLog, log_path);
3482     strcat(oldSlvgLog, ".old");
3483     if (!logFile) {
3484         renamefile(log_path, oldSlvgLog);
3485         logFile = afs_fopen(log_path, "a");
3486
3487         if (!logFile) {         /* still nothing, use stdout */
3488             logFile = stdout;
3489             ShowLog = 0;
3490         }
3491 #ifndef AFS_NAMEI_ENV
3492         AFS_DEBUG_IOPS_LOG(logFile);
3493 #endif
3494     }
3495 }
3496
3497 #ifndef AFS_NT40_ENV
3498 void
3499 TimeStampLogFile(char * log_path)
3500 {
3501     char stampSlvgLog[AFSDIR_PATH_MAX];
3502     struct tm *lt;
3503     time_t now;
3504
3505     now = time(0);
3506     lt = localtime(&now);
3507     (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3508                        "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3509                        log_path, lt->tm_year + 1900,
3510                        lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3511                        lt->tm_sec);
3512
3513     /* try to link the logfile to a timestamped filename */
3514     /* if it fails, oh well, nothing we can do */
3515     link(log_path, stampSlvgLog);
3516 }
3517 #endif
3518
3519 void
3520 showlog(void)
3521 {
3522     char line[256];
3523
3524 #ifndef AFS_NT40_ENV
3525     if (useSyslog) {
3526         printf("Can't show log since using syslog.\n");
3527         fflush(stdout);
3528         return;
3529     }
3530 #endif
3531
3532     rewind(logFile);
3533     fclose(logFile);
3534
3535     logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3536
3537     if (!logFile)
3538         printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3539     else {
3540         rewind(logFile);
3541         while (fgets(line, sizeof(line), logFile))
3542             printf("%s", line);
3543         fflush(stdout);
3544     }
3545 }
3546
3547 void
3548 Log(const char *format, ...)
3549 {
3550     struct timeval now;
3551     char tmp[1024];
3552     va_list args;
3553
3554     va_start(args, format);
3555     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3556     va_end(args);
3557 #ifndef AFS_NT40_ENV
3558     if (useSyslog) {
3559         syslog(LOG_INFO, "%s", tmp);
3560     } else
3561 #endif
3562         if (logFile) {
3563             gettimeofday(&now, 0);
3564             fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3565             fflush(logFile);
3566         }
3567 }
3568
3569 void
3570 Abort(const char *format, ...)
3571 {
3572     va_list args;
3573     char tmp[1024];
3574
3575     va_start(args, format);
3576     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3577     va_end(args);
3578 #ifndef AFS_NT40_ENV
3579     if (useSyslog) {
3580         syslog(LOG_INFO, "%s", tmp);
3581     } else
3582 #endif
3583         if (logFile) {
3584             fprintf(logFile, "%s", tmp);
3585             fflush(logFile);
3586             if (ShowLog)
3587                 showlog();
3588         }
3589
3590     if (debug)
3591         abort();
3592     Exit(1);
3593 }
3594
3595 char *
3596 ToString(char *s)
3597 {
3598     register char *p;
3599     p = (char *)malloc(strlen(s) + 1);
3600     assert(p != NULL);
3601     strcpy(p, s);
3602     return p;
3603 }
3604
3605 /* Remove the FORCESALVAGE file */
3606 void
3607 RemoveTheForce(char *path)
3608 {
3609     char target[1024];
3610     struct afs_stat force; /* so we can use afs_stat to find it */
3611     strcpy(target,path);
3612     strcat(target,"/FORCESALVAGE");
3613     if (!Testing && ForceSalvage) {
3614         if (afs_stat(target,&force) == 0)  unlink(target);
3615     }
3616 }
3617
3618 #ifndef AFS_AIX32_ENV
3619 /*
3620  * UseTheForceLuke -    see if we can use the force
3621  */
3622 int
3623 UseTheForceLuke(char *path)
3624 {
3625     struct afs_stat force;
3626     char target[1024];
3627     strcpy(target,path);
3628     strcat(target,"/FORCESALVAGE");
3629
3630     return (afs_stat(target, &force) == 0);
3631 }
3632 #else
3633 /*
3634  * UseTheForceLuke -    see if we can use the force
3635  *
3636  * NOTE:
3637  *      The VRMIX fsck will not muck with the filesystem it is supposedly
3638  *      fixing and create a "FORCESALVAGE" file (by design).  Instead, we
3639  *      muck directly with the root inode, which is within the normal
3640  *      domain of fsck.
3641  *      ListViceInodes() has a side effect of setting ForceSalvage if
3642  *      it detects a need, based on root inode examination.
3643  */
3644 int
3645 UseTheForceLuke(char *path)
3646 {
3647
3648     return 0;                   /* sorry OB1    */
3649 }
3650 #endif
3651
3652 #ifdef AFS_NT40_ENV
3653 /* NT support routines */
3654
3655 static char execpathname[MAX_PATH];
3656 int
3657 nt_SalvagePartition(char *partName, int jobn)
3658 {
3659     int pid;
3660     int n;
3661     childJob_t job;
3662     if (!*execpathname) {
3663         n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3664         if (!n || n == 1023)
3665             return -1;
3666     }
3667     job.cj_magic = SALVAGER_MAGIC;
3668     job.cj_number = jobn;
3669     (void)strcpy(job.cj_part, partName);
3670     pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3671     return pid;
3672 }
3673
3674 int
3675 nt_SetupPartitionSalvage(void *datap, int len)
3676 {
3677     childJob_t *jobp = (childJob_t *) datap;
3678     char logname[AFSDIR_PATH_MAX];
3679
3680     if (len != sizeof(childJob_t))
3681         return -1;
3682     if (jobp->cj_magic != SALVAGER_MAGIC)
3683         return -1;
3684     myjob = *jobp;
3685
3686     /* Open logFile */
3687     (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3688                   myjob.cj_number);
3689     logFile = afs_fopen(logname, "w");
3690     if (!logFile)
3691         logFile = stdout;
3692
3693     return 0;
3694 }
3695
3696
3697 #endif /* AFS_NT40_ENV */