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