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