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