aix-largefile-cleanup-20060620
[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 int Testing = 0;                /* -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             CreateLinkTable(isp, ino);
1456         }
1457     }
1458     if (fdP)
1459         FDH_REALLYCLOSE(fdP);
1460 #else
1461     IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1462 #endif
1463
1464     /* Salvage in reverse order--read/write volume last; this way any
1465      * Inodes not referenced by the time we salvage the read/write volume
1466      * can be picked up by the read/write volume */
1467     /* ACTUALLY, that's not done right now--the inodes just vanish */
1468     for (i = nVols - 1; i >= salvageTo; i--) {
1469         int rw = (i == 0);
1470         struct InodeSummary *lisp = &isp[i];
1471 #ifdef AFS_NAMEI_ENV
1472         /* If only the RO is present on this partition, the link table
1473          * shows up as a RW volume special file. Need to make sure the
1474          * salvager doesn't try to salvage the non-existent RW.
1475          */
1476         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1477             /* If this only special inode is the link table, continue */
1478             if (inodes->u.special.type == VI_LINKTABLE) {
1479                 haveRWvolume = 0;
1480                 continue;
1481             }
1482         }
1483 #endif
1484         if (!Showmode)
1485             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1486                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1487         /* Check inodes twice.  The second time do things seriously.  This
1488          * way the whole RO volume can be deleted, below, if anything goes wrong */
1489         for (check = 1; check >= 0; check--) {
1490             int deleteMe;
1491             if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1492                 == -1) {
1493                 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1494                 if (rw && deleteMe) {
1495                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
1496                                          * volume won't be called */
1497                     break;
1498                 }
1499                 if (!rw)
1500                     break;
1501             }
1502             if (rw && check == 1)
1503                 continue;
1504             if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1505                 MaybeZapVolume(lisp, "Vnode index", 0, check);
1506                 break;
1507             }
1508         }
1509     }
1510
1511     /* Fix actual inode counts */
1512     if (!Showmode) {
1513         Log("totalInodes %d\n",totalInodes);
1514         for (ip = inodes; totalInodes; ip++, totalInodes--) {
1515             static int TraceBadLinkCounts = 0;
1516 #ifdef AFS_NAMEI_ENV
1517             if (VGLinkH->ih_ino == ip->inodeNumber) {
1518                 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1519                 VGLinkH_p1 = ip->u.param[0];
1520                 continue;       /* Deal with this last. */
1521             }
1522 #endif
1523             if (ip->linkCount != 0 && TraceBadLinkCounts) {
1524                 TraceBadLinkCounts--;   /* Limit reports, per volume */
1525                 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]);
1526             }
1527             while (ip->linkCount > 0) {
1528                 /* below used to assert, not break */
1529                 if (!Testing) {
1530                     if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1531                         Log("idec failed. inode %s errno %d\n",
1532                             PrintInode(NULL, ip->inodeNumber), errno);
1533                         break;
1534                     }
1535                 }
1536                 ip->linkCount--;
1537             }
1538             while (ip->linkCount < 0) {
1539                 /* these used to be asserts */
1540                 if (!Testing) {
1541                     if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1542                         Log("iinc failed. inode %s errno %d\n",
1543                             PrintInode(NULL, ip->inodeNumber), errno);
1544                         break;
1545                     }
1546                 }
1547                 ip->linkCount++;
1548             }
1549         }
1550 #ifdef AFS_NAMEI_ENV
1551         while (dec_VGLinkH > 0) {
1552             if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1553                 Log("idec failed on link table, errno = %d\n", errno);
1554             }
1555             dec_VGLinkH--;
1556         }
1557         while (dec_VGLinkH < 0) {
1558             if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1559                 Log("iinc failed on link table, errno = %d\n", errno);
1560             }
1561             dec_VGLinkH++;
1562         }
1563 #endif
1564     }
1565     free(inodes);
1566     /* Directory consistency checks on the rw volume */
1567     if (haveRWvolume)
1568         SalvageVolume(isp, VGLinkH);
1569     IH_RELEASE(VGLinkH);
1570
1571     if (canfork && !debug) {
1572         ShowLog = 0;
1573         Exit(0);
1574     }
1575 }
1576
1577 int
1578 QuickCheck(register struct InodeSummary *isp, int nVols)
1579 {
1580     /* Check headers BEFORE forking */
1581     register int i;
1582     IHandle_t *h;
1583
1584     for (i = 0; i < nVols; i++) {
1585         struct VolumeSummary *vs = isp[i].volSummary;
1586         VolumeDiskData volHeader;
1587         if (!vs) {
1588             /* Don't salvage just because phantom rw volume is there... */
1589             /* (If a read-only volume exists, read/write inodes must also exist) */
1590             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1591                 continue;
1592             return 0;
1593         }
1594         IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1595         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1596             == sizeof(volHeader)
1597             && volHeader.stamp.magic == VOLUMEINFOMAGIC
1598             && volHeader.dontSalvage == DONT_SALVAGE
1599             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1600             if (volHeader.inUse == 1) {
1601                 volHeader.inUse = 0;
1602                 volHeader.inService = 1;
1603                 if (!Testing) {
1604                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1605                         != sizeof(volHeader)) {
1606                         IH_RELEASE(h);
1607                         return 0;
1608                     }
1609                 }
1610             }
1611             IH_RELEASE(h);
1612         } else {
1613             IH_RELEASE(h);
1614             return 0;
1615         }
1616     }
1617     return 1;
1618 }
1619
1620
1621 /* SalvageVolumeHeaderFile
1622  *
1623  * Salvage the top level V*.vol header file. Make sure the special files
1624  * exist and that there are no duplicates.
1625  *
1626  * Calls SalvageHeader for each possible type of volume special file.
1627  */
1628
1629 int
1630 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1631                         register struct ViceInodeInfo *inodes, int RW,
1632                         int check, int *deleteMe)
1633 {
1634     int headerFd = 0;
1635     int i;
1636     register struct ViceInodeInfo *ip;
1637     int allinodesobsolete = 1;
1638     struct VolumeDiskHeader diskHeader;
1639
1640     if (deleteMe)
1641         *deleteMe = 0;
1642     memset(&tempHeader, 0, sizeof(tempHeader));
1643     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1644     tempHeader.stamp.version = VOLUMEHEADERVERSION;
1645     tempHeader.id = isp->volumeId;
1646     tempHeader.parent = isp->RWvolumeId;
1647     /* Check for duplicates (inodes are sorted by type field) */
1648     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1649         ip = &inodes[isp->index + i];
1650         if (ip->u.special.type == (ip + 1)->u.special.type) {
1651             if (!Showmode)
1652                 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1653             return -1;
1654         }
1655     }
1656     for (i = 0; i < isp->nSpecialInodes; i++) {
1657         ip = &inodes[isp->index + i];
1658         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1659             if (check) {
1660                 Log("Rubbish header inode\n");
1661                 return -1;
1662             }
1663             Log("Rubbish header inode; deleted\n");
1664         } else if (!stuff[ip->u.special.type - 1].obsolete) {
1665             *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1666             if (!check && ip->u.special.type != VI_LINKTABLE)
1667                 ip->linkCount--;        /* Keep the inode around */
1668             allinodesobsolete = 0;
1669         }
1670     }
1671
1672     if (allinodesobsolete) {
1673         if (deleteMe)
1674             *deleteMe = 1;
1675         return -1;
1676     }
1677
1678     if (!check)
1679         VGLinkH_cnt++;          /* one for every header. */
1680
1681     if (!RW && !check && isp->volSummary) {
1682         ClearROInUseBit(isp->volSummary);
1683         return 0;
1684     }
1685
1686     for (i = 0; i < MAXINODETYPE; i++) {
1687         if (stuff[i].inodeType == VI_LINKTABLE) {
1688             /* Gross hack: SalvageHeader does a bcmp on the volume header.
1689              * And we may have recreated the link table earlier, so set the
1690              * RW header as well.
1691              */
1692             if (VALID_INO(VGLinkH->ih_ino)) {
1693                 *stuff[i].inode = VGLinkH->ih_ino;
1694             }
1695             continue;
1696         }
1697         if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1698             return -1;
1699     }
1700
1701     if (isp->volSummary == NULL) {
1702         char name[64];
1703         (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1704         if (check) {
1705             Log("No header file for volume %u\n", isp->volumeId);
1706             return -1;
1707         }
1708         if (!Showmode)
1709             Log("No header file for volume %u; %screating %s/%s\n",
1710                 isp->volumeId, (Testing ? "it would have been " : ""),
1711                 fileSysPathName, name);
1712         headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
1713         assert(headerFd != -1);
1714         isp->volSummary = (struct VolumeSummary *)
1715             malloc(sizeof(struct VolumeSummary));
1716         isp->volSummary->fileName = ToString(name);
1717     } else {
1718         char name[64];
1719         /* hack: these two fields are obsolete... */
1720         isp->volSummary->header.volumeAcl = 0;
1721         isp->volSummary->header.volumeMountTable = 0;
1722
1723         if (memcmp
1724             (&isp->volSummary->header, &tempHeader,
1725              sizeof(struct VolumeHeader))) {
1726             /* We often remove the name before calling us, so we make a fake one up */
1727             if (isp->volSummary->fileName) {
1728                 strcpy(name, isp->volSummary->fileName);
1729             } else {
1730                 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1731                 isp->volSummary->fileName = ToString(name);
1732             }
1733
1734             Log("Header file %s is damaged or no longer valid%s\n", name,
1735                 (check ? "" : "; repairing"));
1736             if (check)
1737                 return -1;
1738
1739             headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
1740             assert(headerFd != -1);
1741         }
1742     }
1743     if (headerFd) {
1744         memcpy(&isp->volSummary->header, &tempHeader,
1745                sizeof(struct VolumeHeader));
1746         if (Testing) {
1747             if (!Showmode)
1748                 Log("It would have written a new header file for volume %u\n",
1749                     isp->volumeId);
1750         } else {
1751             VolumeHeaderToDisk(&diskHeader, &tempHeader);
1752             if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1753                 != sizeof(struct VolumeDiskHeader)) {
1754                 Log("Couldn't rewrite volume header file!\n");
1755                 close(headerFd);
1756                 return -1;
1757             }
1758         }
1759         close(headerFd);
1760     }
1761     IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1762             isp->volSummary->header.volumeInfo);
1763     return 0;
1764 }
1765
1766 int
1767 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1768               int *deleteMe)
1769 {
1770     union {
1771         VolumeDiskData volumeInfo;
1772         struct versionStamp fileHeader;
1773     } header;
1774     IHandle_t *specH;
1775     int recreate = 0;
1776     afs_int32 code;
1777     FdHandle_t *fdP;
1778
1779     if (sp->obsolete)
1780         return 0;
1781 #ifndef AFS_NAMEI_ENV
1782     if (sp->inodeType == VI_LINKTABLE)
1783         return 0;
1784 #endif
1785     if (*(sp->inode) == 0) {
1786         if (check) {
1787             Log("Missing inode in volume header (%s)\n", sp->description);
1788             return -1;
1789         }
1790         if (!Showmode)
1791             Log("Missing inode in volume header (%s); %s\n", sp->description,
1792                 (Testing ? "it would have recreated it" : "recreating"));
1793         if (!Testing) {
1794             *(sp->inode) =
1795                 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1796                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1797             if (!VALID_INO(*(sp->inode)))
1798                 Abort
1799                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1800                      sp->description, errno);
1801         }
1802         recreate = 1;
1803     }
1804
1805     IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1806     fdP = IH_OPEN(specH);
1807     if (OKToZap && (fdP == NULL) && BadError(errno)) {
1808         /* bail out early and destroy the volume */
1809         if (!Showmode)
1810             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1811         if (deleteMe)
1812             *deleteMe = 1;
1813         IH_RELEASE(specH);
1814         return -1;
1815     }
1816     if (fdP == NULL)
1817         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1818               sp->description, errno);
1819
1820     if (!recreate
1821         && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1822             || header.fileHeader.magic != sp->stamp.magic)) {
1823         if (check) {
1824             Log("Part of the header (%s) is corrupted\n", sp->description);
1825             FDH_REALLYCLOSE(fdP);
1826             IH_RELEASE(specH);
1827             return -1;
1828         }
1829         Log("Part of the header (%s) is corrupted; recreating\n",
1830             sp->description);
1831         recreate = 1;
1832     }
1833     if (sp->inodeType == VI_VOLINFO
1834         && header.volumeInfo.destroyMe == DESTROY_ME) {
1835         if (deleteMe)
1836             *deleteMe = 1;
1837         FDH_REALLYCLOSE(fdP);
1838         IH_RELEASE(specH);
1839         return -1;
1840     }
1841     if (recreate && !Testing) {
1842         if (check)
1843             Abort
1844                 ("Internal error: recreating volume header (%s) in check mode\n",
1845                  sp->description);
1846         code = FDH_TRUNC(fdP, 0);
1847         if (code == -1)
1848             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1849                   sp->description, errno);
1850
1851         /* The following code should be moved into vutil.c */
1852         if (sp->inodeType == VI_VOLINFO) {
1853             struct timeval tp;
1854             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1855             header.volumeInfo.stamp = sp->stamp;
1856             header.volumeInfo.id = isp->volumeId;
1857             header.volumeInfo.parentId = isp->RWvolumeId;
1858             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1859             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1860                 isp->volumeId, isp->volumeId);
1861             header.volumeInfo.inService = 0;
1862             header.volumeInfo.blessed = 0;
1863             /* The + 1000 is a hack in case there are any files out in venus caches */
1864             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1865             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
1866             header.volumeInfo.needsCallback = 0;
1867             gettimeofday(&tp, 0);
1868             header.volumeInfo.creationDate = tp.tv_sec;
1869             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1870                 Abort
1871                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1872                      sp->description, errno);
1873             }
1874             code =
1875                 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1876                           sizeof(header.volumeInfo));
1877             if (code != sizeof(header.volumeInfo)) {
1878                 if (code < 0)
1879                     Abort
1880                         ("Unable to write volume header file (%s) (errno = %d)\n",
1881                          sp->description, errno);
1882                 Abort("Unable to write entire volume header file (%s)\n",
1883                       sp->description);
1884             }
1885         } else {
1886             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1887                 Abort
1888                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1889                      sp->description, errno);
1890             }
1891             code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1892             if (code != sizeof(sp->stamp)) {
1893                 if (code < 0)
1894                     Abort
1895                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1896                          sp->description, errno);
1897                 Abort
1898                     ("Unable to write entire version stamp in volume header file (%s)\n",
1899                      sp->description);
1900             }
1901         }
1902     }
1903     FDH_REALLYCLOSE(fdP);
1904     IH_RELEASE(specH);
1905     if (sp->inodeType == VI_VOLINFO) {
1906         VolInfo = header.volumeInfo;
1907         if (check) {
1908             char update[25];
1909             if (VolInfo.updateDate) {
1910                 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1911                 if (!Showmode)
1912                     Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1913                         (Testing ? "it would have been " : ""), update);
1914             } else {
1915                 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1916                 if (!Showmode)
1917                     Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1918                         VolInfo.id, update);
1919             }
1920
1921         }
1922     }
1923
1924     return 0;
1925 }
1926
1927 int
1928 SalvageVnodes(register struct InodeSummary *rwIsp,
1929               register struct InodeSummary *thisIsp,
1930               register struct ViceInodeInfo *inodes, int check)
1931 {
1932     int ilarge, ismall, ioffset, RW, nInodes;
1933     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
1934     if (Showmode)
1935         return 0;
1936     RW = (rwIsp == thisIsp);
1937     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1938     ismall =
1939         SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1940                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1941     if (check && ismall == -1)
1942         return -1;
1943     ilarge =
1944         SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1945                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1946     return (ilarge == 0 && ismall == 0 ? 0 : -1);
1947 }
1948
1949 int
1950 SalvageIndex(Inode ino, VnodeClass class, int RW,
1951              register struct ViceInodeInfo *ip, int nInodes,
1952              struct VolumeSummary *volSummary, int check)
1953 {
1954     VolumeId volumeNumber;
1955     char buf[SIZEOF_LARGEDISKVNODE];
1956     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1957     int err = 0;
1958     StreamHandle_t *file;
1959     struct VnodeClassInfo *vcp;
1960     afs_sfsize_t size;
1961     afs_fsize_t vnodeLength;
1962     int vnodeIndex, nVnodes;
1963     afs_ino_str_t stmp1, stmp2;
1964     IHandle_t *handle;
1965     FdHandle_t *fdP;
1966
1967     volumeNumber = volSummary->header.id;
1968     IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
1969     fdP = IH_OPEN(handle);
1970     assert(fdP != NULL);
1971     file = FDH_FDOPEN(fdP, "r+");
1972     assert(file != NULL);
1973     vcp = &VnodeClassInfo[class];
1974     size = OS_SIZE(fdP->fd_fd);
1975     assert(size != -1);
1976     nVnodes = (size / vcp->diskSize) - 1;
1977     if (nVnodes > 0) {
1978         assert((nVnodes + 1) * vcp->diskSize == size);
1979         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
1980     } else {
1981         nVnodes = 0;
1982     }
1983     for (vnodeIndex = 0;
1984          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
1985          nVnodes--, vnodeIndex++) {
1986         if (vnode->type != vNull) {
1987             int vnodeChanged = 0;
1988             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
1989             /* Log programs that belong to root (potentially suid root);
1990              * don't bother for read-only or backup volumes */
1991 #ifdef  notdef                  /* This is done elsewhere */
1992             if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
1993                 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);
1994 #endif
1995             if (VNDISK_GET_INO(vnode) == 0) {
1996                 if (RW) {
1997                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
1998                     memset(vnode, 0, vcp->diskSize);
1999                     vnodeChanged = 1;
2000                 }
2001             } else {
2002                 if (vcp->magic != vnode->vnodeMagic) {
2003                     /* bad magic #, probably partially created vnode */
2004                     Log("Partially allocated vnode %d deleted.\n",
2005                         vnodeNumber);
2006                     memset(vnode, 0, vcp->diskSize);
2007                     vnodeChanged = 1;
2008                     goto vnodeDone;
2009                 }
2010                 /* ****** Should do a bit more salvage here:  e.g. make sure
2011                  * vnode type matches what it should be given the index */
2012                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2013 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2014  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2015  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2016  *                  }
2017  */
2018                     ip++;
2019                     nInodes--;
2020                 }
2021                 if (!RW) {
2022                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2023                         /* The following doesn't work, because the version number
2024                          * is not maintained correctly by the file server */
2025                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2026                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2027                          * break; */
2028                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2029                             break;
2030                         ip++;
2031                         nInodes--;
2032                     }
2033                 } else {
2034                     /* For RW volume, look for vnode with matching inode number;
2035                      * if no such match, take the first determined by our sort
2036                      * order */
2037                     register struct ViceInodeInfo *lip = ip;
2038                     register int lnInodes = nInodes;
2039                     while (lnInodes
2040                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2041                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2042                             ip = lip;
2043                             nInodes = lnInodes;
2044                             break;
2045                         }
2046                         lip++;
2047                         lnInodes--;
2048                     }
2049                 }
2050                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2051                     /* "Matching" inode */
2052                     if (RW) {
2053                         Unique vu, iu;
2054                         FileVersion vd, id;
2055                         vu = vnode->uniquifier;
2056                         iu = ip->u.vnode.vnodeUniquifier;
2057                         vd = vnode->dataVersion;
2058                         id = ip->u.vnode.inodeDataVersion;
2059                         /*
2060                          * Because of the possibility of the uniquifier overflows (> 4M)
2061                          * we compare them modulo the low 22-bits; we shouldn't worry
2062                          * about mismatching since they shouldn't to many old 
2063                          * uniquifiers of the same vnode...
2064                          */
2065                         if (IUnique(vu) != IUnique(iu)) {
2066                             if (!Showmode) {
2067                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2068                             }
2069
2070                             vnode->uniquifier = iu;
2071 #ifdef  AFS_3DISPARES
2072                             vnode->dataVersion = (id >= vd ?
2073                                                   /* 90% of 2.1M */
2074                                                   ((id - vd) >
2075                                                    1887437 ? vd : id) :
2076                                                   /* 90% of 2.1M */
2077                                                   ((vd - id) >
2078                                                    1887437 ? id : vd));
2079 #else
2080 #if defined(AFS_SGI_EXMAG)
2081                             vnode->dataVersion = (id >= vd ?
2082                                                   /* 90% of 16M */
2083                                                   ((id - vd) >
2084                                                    15099494 ? vd : id) :
2085                                                   /* 90% of 16M */
2086                                                   ((vd - id) >
2087                                                    15099494 ? id : vd));
2088 #else
2089                             vnode->dataVersion = (id > vd ? id : vd);
2090 #endif /* AFS_SGI_EXMAG */
2091 #endif /* AFS_3DISPARES */
2092                             vnodeChanged = 1;
2093                         } else {
2094                             /* don't bother checking for vd > id any more, since
2095                              * partial file transfers always result in this state,
2096                              * and you can't do much else anyway (you've already
2097                              * found the best data you can) */
2098 #ifdef  AFS_3DISPARES
2099                             if (!vnodeIsDirectory(vnodeNumber)
2100                                 && ((vd < id && (id - vd) < 1887437)
2101                                     || ((vd > id && (vd - id) > 1887437)))) {
2102 #else
2103 #if defined(AFS_SGI_EXMAG)
2104                             if (!vnodeIsDirectory(vnodeNumber)
2105                                 && ((vd < id && (id - vd) < 15099494)
2106                                     || ((vd > id && (vd - id) > 15099494)))) {
2107 #else
2108                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2109 #endif /* AFS_SGI_EXMAG */
2110 #endif
2111                                 if (!Showmode)
2112                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2113                                 vnode->dataVersion = id;
2114                                 vnodeChanged = 1;
2115                             }
2116                         }
2117                     }
2118                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2119                         if (check) {
2120                             if (!Showmode) {
2121                                 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);
2122                             }
2123                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2124                             err = -1;
2125                             goto zooks;
2126                         }
2127                         if (!Showmode) {
2128                             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);
2129                         }
2130                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2131                         vnodeChanged = 1;
2132                     }
2133                     VNDISK_GET_LEN(vnodeLength, vnode);
2134                     if (ip->byteCount != vnodeLength) {
2135                         if (check) {
2136                             if (!Showmode)
2137                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2138                             err = -1;
2139                             goto zooks;
2140                         }
2141                         if (!Showmode)
2142                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2143                         VNDISK_SET_LEN(vnode, ip->byteCount);
2144                         vnodeChanged = 1;
2145                     }
2146                     if (!check)
2147                         ip->linkCount--;        /* Keep the inode around */
2148                     ip++;
2149                     nInodes--;
2150                 } else {        /* no matching inode */
2151                     if (VNDISK_GET_INO(vnode) != 0
2152                         || vnode->type == vDirectory) {
2153                         /* No matching inode--get rid of the vnode */
2154                         if (check) {
2155                             if (VNDISK_GET_INO(vnode)) {
2156                                 if (!Showmode) {
2157                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2158                                 }
2159                             } else {
2160                                 if (!Showmode)
2161                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2162                             }
2163                             err = -1;
2164                             goto zooks;
2165                         }
2166                         if (VNDISK_GET_INO(vnode)) {
2167                             if (!Showmode) {
2168                                 time_t serverModifyTime = vnode->serverModifyTime;
2169                                 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));
2170                             }
2171                         } else {
2172                             if (!Showmode) {
2173                                 time_t serverModifyTime = vnode->serverModifyTime;
2174                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2175                             }
2176                         }
2177                         memset(vnode, 0, vcp->diskSize);
2178                         vnodeChanged = 1;
2179                     } else {
2180                         /* Should not reach here becuase we checked for 
2181                          * (inodeNumber == 0) above. And where we zero the vnode,
2182                          * we also goto vnodeDone.
2183                          */
2184                     }
2185                 }
2186                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2187                     ip++;
2188                     nInodes--;
2189                 }
2190             }                   /* VNDISK_GET_INO(vnode) != 0 */
2191           vnodeDone:
2192             assert(!(vnodeChanged && check));
2193             if (vnodeChanged && !Testing) {
2194                 assert(IH_IWRITE
2195                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2196                         (char *)vnode, vcp->diskSize)
2197                        == vcp->diskSize);
2198                 VolumeChanged = 1;      /* For break call back */
2199             }
2200         }
2201     }
2202   zooks:
2203     STREAM_CLOSE(file);
2204     FDH_CLOSE(fdP);
2205     IH_RELEASE(handle);
2206     return err;
2207 }
2208
2209 struct VnodeEssence *
2210 CheckVnodeNumber(VnodeId vnodeNumber)
2211 {
2212     VnodeClass class;
2213     struct VnodeInfo *vip;
2214     int offset;
2215
2216     class = vnodeIdToClass(vnodeNumber);
2217     vip = &vnodeInfo[class];
2218     offset = vnodeIdToBitNumber(vnodeNumber);
2219     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2220 }
2221
2222 void
2223 CopyOnWrite(register struct DirSummary *dir)
2224 {
2225     /* Copy the directory unconditionally if we are going to change it:
2226      * not just if was cloned.
2227      */
2228     struct VnodeDiskObject vnode;
2229     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2230     Inode oldinode, newinode;
2231     afs_sfsize_t code;
2232
2233     if (dir->copied || Testing)
2234         return;
2235     DFlush();                   /* Well justified paranoia... */
2236
2237     code =
2238         IH_IREAD(vnodeInfo[vLarge].handle,
2239                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2240                  sizeof(vnode));
2241     assert(code == sizeof(vnode));
2242     oldinode = VNDISK_GET_INO(&vnode);
2243     /* Increment the version number by a whole lot to avoid problems with
2244      * clients that were promised new version numbers--but the file server
2245      * crashed before the versions were written to disk.
2246      */
2247     newinode =
2248         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2249                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2250                   200);
2251     assert(VALID_INO(newinode));
2252     assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2253     vnode.cloned = 0;
2254     VNDISK_SET_INO(&vnode, newinode);
2255     code =
2256         IH_IWRITE(vnodeInfo[vLarge].handle,
2257                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2258                   sizeof(vnode));
2259     assert(code == sizeof(vnode));
2260
2261     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2262                         fileSysDevice, newinode);
2263     /* Don't delete the original inode right away, because the directory is
2264      * still being scanned.
2265      */
2266     dir->copied = 1;
2267 }
2268
2269 /*
2270  * This function should either successfully create a new dir, or give up 
2271  * and leave things the way they were.  In particular, if it fails to write 
2272  * the new dir properly, it should return w/o changing the reference to the 
2273  * old dir.
2274  */
2275 void
2276 CopyAndSalvage(register struct DirSummary *dir)
2277 {
2278     struct VnodeDiskObject vnode;
2279     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2280     Inode oldinode, newinode;
2281     DirHandle newdir;
2282     afs_int32 code;
2283     afs_sfsize_t lcode;
2284     afs_int32 parentUnique = 1;
2285     struct VnodeEssence *vnodeEssence;
2286
2287     if (Testing)
2288         return;
2289     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2290     lcode =
2291         IH_IREAD(vnodeInfo[vLarge].handle,
2292                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2293                  sizeof(vnode));
2294     assert(lcode == sizeof(vnode));
2295     oldinode = VNDISK_GET_INO(&vnode);
2296     /* Increment the version number by a whole lot to avoid problems with
2297      * clients that were promised new version numbers--but the file server
2298      * crashed before the versions were written to disk.
2299      */
2300     newinode =
2301         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2302                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2303                   200);
2304     assert(VALID_INO(newinode));
2305     SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2306
2307     /* Assign . and .. vnode numbers from dir and vnode.parent. 
2308      * The uniquifier for . is in the vnode.
2309      * The uniquifier for .. might be set to a bogus value of 1 and 
2310      * the salvager will later clean it up.
2311      */
2312     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2313         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2314     }
2315     code =
2316         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2317                    vnode.uniquifier,
2318                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2319                    parentUnique);
2320     if (code == 0)
2321         code = DFlush();
2322     if (code) {
2323         /* didn't really build the new directory properly, let's just give up. */
2324         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2325         Log("Directory salvage returned code %d, continuing.\n", code);
2326         if (code) {
2327             Log("also failed to decrement link count on new inode");
2328         }
2329         assert(1 == 2);
2330     }
2331     Log("Checking the results of the directory salvage...\n");
2332     if (!DirOK(&newdir)) {
2333         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2334         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2335         assert(code == 0);
2336         assert(1 == 2);
2337     }
2338     vnode.cloned = 0;
2339     VNDISK_SET_INO(&vnode, newinode);
2340     VNDISK_SET_LEN(&vnode, Length(&newdir));
2341     lcode =
2342         IH_IWRITE(vnodeInfo[vLarge].handle,
2343                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2344                   sizeof(vnode));
2345     assert(lcode == sizeof(vnode));
2346 #ifdef AFS_NT40_ENV
2347     nt_sync(fileSysDevice);
2348 #else
2349     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2350                                  * an open FD on the file itself to fsync.
2351                                  */
2352 #endif
2353     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2354     assert(code == 0);
2355     dir->dirHandle = newdir;
2356 }
2357
2358 void
2359 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2360            Unique unique)
2361 {
2362     struct VnodeEssence *vnodeEssence;
2363     afs_int32 dirOrphaned, todelete;
2364
2365     dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2366
2367     vnodeEssence = CheckVnodeNumber(vnodeNumber);
2368     if (vnodeEssence == NULL) {
2369         if (!Showmode) {
2370             Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2371         }
2372         if (!Testing) {
2373             CopyOnWrite(dir);
2374             assert(Delete(&dir->dirHandle, name) == 0);
2375         }
2376         return;
2377     }
2378 #ifdef AFS_AIX_ENV
2379 #ifndef AFS_NAMEI_ENV
2380     /* On AIX machines, don't allow entries to point to inode 0. That is a special 
2381      * mount inode for the partition. If this inode were deleted, it would crash
2382      * the machine.
2383      */
2384     if (vnodeEssence->InodeNumber == 0) {
2385         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"));
2386         if (!Testing) {
2387             CopyOnWrite(dir);
2388             assert(Delete(&dir->dirHandle, name) == 0);
2389         }
2390         return;
2391     }
2392 #endif
2393 #endif
2394
2395     if (!(vnodeNumber & 1) && !Showmode
2396         && !(vnodeEssence->count || vnodeEssence->unique
2397              || vnodeEssence->modeBits)) {
2398         Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2399             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2400             vnodeNumber, unique,
2401             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2402              ""));
2403         if (!unique) {
2404             if (!Testing) {
2405                 CopyOnWrite(dir);
2406                 assert(Delete(&dir->dirHandle, name) == 0);
2407             }
2408             return;
2409         }
2410     }
2411
2412     /* Check if the Uniquifiers match. If not, change the directory entry
2413      * so its unique matches the vnode unique. Delete if the unique is zero
2414      * or if the directory is orphaned.
2415      */
2416     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2417         if (!vnodeEssence->unique
2418             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2419             /* This is an orphaned directory. Don't delete the . or ..
2420              * entry. Otherwise, it will get created in the next 
2421              * salvage and deleted again here. So Just skip it.
2422              */
2423             return;
2424         }
2425
2426         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2427
2428         if (!Showmode) {
2429             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")));
2430         }
2431         if (!Testing) {
2432             ViceFid fid;
2433             fid.Vnode = vnodeNumber;
2434             fid.Unique = vnodeEssence->unique;
2435             CopyOnWrite(dir);
2436             assert(Delete(&dir->dirHandle, name) == 0);
2437             if (!todelete)
2438                 assert(Create(&dir->dirHandle, name, &fid) == 0);
2439         }
2440         if (todelete)
2441             return;             /* no need to continue */
2442     }
2443
2444     if (strcmp(name, ".") == 0) {
2445         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2446             ViceFid fid;
2447             if (!Showmode)
2448                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2449             if (!Testing) {
2450                 CopyOnWrite(dir);
2451                 assert(Delete(&dir->dirHandle, ".") == 0);
2452                 fid.Vnode = dir->vnodeNumber;
2453                 fid.Unique = dir->unique;
2454                 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2455             }
2456
2457             vnodeNumber = fid.Vnode;    /* Get the new Essence */
2458             unique = fid.Unique;
2459             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2460         }
2461         dir->haveDot = 1;
2462     } else if (strcmp(name, "..") == 0) {
2463         ViceFid pa;
2464         if (dir->parent) {
2465             struct VnodeEssence *dotdot;
2466             pa.Vnode = dir->parent;
2467             dotdot = CheckVnodeNumber(pa.Vnode);
2468             assert(dotdot != NULL);     /* XXX Should not be assert */
2469             pa.Unique = dotdot->unique;
2470         } else {
2471             pa.Vnode = dir->vnodeNumber;
2472             pa.Unique = dir->unique;
2473         }
2474         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2475             if (!Showmode)
2476                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2477             if (!Testing) {
2478                 CopyOnWrite(dir);
2479                 assert(Delete(&dir->dirHandle, "..") == 0);
2480                 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2481             }
2482
2483             vnodeNumber = pa.Vnode;     /* Get the new Essence */
2484             unique = pa.Unique;
2485             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2486         }
2487         dir->haveDotDot = 1;
2488     } else if (strncmp(name, ".__afs", 6) == 0) {
2489         if (!Showmode) {
2490             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);
2491         }
2492         if (!Testing) {
2493             CopyOnWrite(dir);
2494             assert(Delete(&dir->dirHandle, name) == 0);
2495         }
2496         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
2497         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
2498         return;
2499     } else {
2500         if (ShowSuid && (vnodeEssence->modeBits & 06000))
2501             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);
2502         if (ShowMounts && (vnodeEssence->type == vSymlink)
2503             && !(vnodeEssence->modeBits & 0111)) {
2504             int code, size;
2505             char buf[1024];
2506             IHandle_t *ihP;
2507             FdHandle_t *fdP;
2508
2509             IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2510                     vnodeEssence->InodeNumber);
2511             fdP = IH_OPEN(ihP);
2512             assert(fdP != NULL);
2513             size = FDH_SIZE(fdP);
2514             assert(size != -1);
2515             memset(buf, 0, 1024);
2516             if (size > 1024)
2517                 size = 1024;
2518             code = FDH_READ(fdP, buf, size);
2519             assert(code == size);
2520             Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2521                 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2522                 dir->name ? dir->name : "??", name, buf);
2523             FDH_REALLYCLOSE(fdP);
2524             IH_RELEASE(ihP);
2525         }
2526         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2527             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);
2528         if (vnodeIdToClass(vnodeNumber) == vLarge
2529             && vnodeEssence->name == NULL) {
2530             char *n;
2531             if ((n = (char *)malloc(strlen(name) + 1)))
2532                 strcpy(n, name);
2533             vnodeEssence->name = n;
2534         }
2535
2536         /* The directory entry points to the vnode. Check to see if the
2537          * vnode points back to the directory. If not, then let the 
2538          * directory claim it (else it might end up orphaned). Vnodes 
2539          * already claimed by another directory are deleted from this
2540          * directory: hardlinks to the same vnode are not allowed
2541          * from different directories.
2542          */
2543         if (vnodeEssence->parent != dir->vnodeNumber) {
2544             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2545                 /* Vnode does not point back to this directory.
2546                  * Orphaned dirs cannot claim a file (it may belong to
2547                  * another non-orphaned dir).
2548                  */
2549                 if (!Showmode) {
2550                     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);
2551                 }
2552                 vnodeEssence->parent = dir->vnodeNumber;
2553                 vnodeEssence->changed = 1;
2554             } else {
2555                 /* Vnode was claimed by another directory */
2556                 if (!Showmode) {
2557                     if (dirOrphaned) {
2558                         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 " : ""));
2559                     } else if (vnodeNumber == 1) {
2560                         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 " : ""));
2561                     } else {
2562                         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 " : ""));
2563                     }
2564                 }
2565                 if (!Testing) {
2566                     CopyOnWrite(dir);
2567                     assert(Delete(&dir->dirHandle, name) == 0);
2568                 }
2569                 return;
2570             }
2571         }
2572         /* This directory claims the vnode */
2573         vnodeEssence->claimed = 1;
2574     }
2575     vnodeEssence->count--;
2576 }
2577
2578 void
2579 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2580 {
2581     register struct VnodeInfo *vip = &vnodeInfo[class];
2582     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2583     char buf[SIZEOF_LARGEDISKVNODE];
2584     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2585     afs_sfsize_t size;
2586     StreamHandle_t *file;
2587     int vnodeIndex;
2588     int nVnodes;
2589     FdHandle_t *fdP;
2590
2591     IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2592     fdP = IH_OPEN(vip->handle);
2593     assert(fdP != NULL);
2594     file = FDH_FDOPEN(fdP, "r+");
2595     assert(file != NULL);
2596     size = OS_SIZE(fdP->fd_fd);
2597     assert(size != -1);
2598     vip->nVnodes = (size / vcp->diskSize) - 1;
2599     if (vip->nVnodes > 0) {
2600         assert((vip->nVnodes + 1) * vcp->diskSize == size);
2601         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2602         assert((vip->vnodes = (struct VnodeEssence *)
2603                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2604         if (class == vLarge) {
2605             assert((vip->inodes = (Inode *)
2606                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2607         } else {
2608             vip->inodes = NULL;
2609         }
2610     } else {
2611         vip->nVnodes = 0;
2612         vip->vnodes = NULL;
2613         vip->inodes = NULL;
2614     }
2615     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2616     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2617          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2618          nVnodes--, vnodeIndex++) {
2619         if (vnode->type != vNull) {
2620             register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2621             afs_fsize_t vnodeLength;
2622             vip->nAllocatedVnodes++;
2623             vep->count = vnode->linkCount;
2624             VNDISK_GET_LEN(vnodeLength, vnode);
2625             vep->blockCount = nBlocks(vnodeLength);
2626             vip->volumeBlockCount += vep->blockCount;
2627             vep->parent = vnode->parent;
2628             vep->unique = vnode->uniquifier;
2629             if (*maxu < vnode->uniquifier)
2630                 *maxu = vnode->uniquifier;
2631             vep->modeBits = vnode->modeBits;
2632             vep->InodeNumber = VNDISK_GET_INO(vnode);
2633             vep->type = vnode->type;
2634             vep->author = vnode->author;
2635             vep->owner = vnode->owner;
2636             vep->group = vnode->group;
2637             if (vnode->type == vDirectory) {
2638                 assert(class == vLarge);
2639                 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2640             }
2641         }
2642     }
2643     STREAM_CLOSE(file);
2644     FDH_CLOSE(fdP);
2645 }
2646
2647 static char *
2648 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2649 {
2650     struct VnodeEssence *parentvp;
2651
2652     if (vnode == 1) {
2653         strcpy(path, ".");
2654         return path;
2655     }
2656     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2657         && GetDirName(vp->parent, parentvp, path)) {
2658         strcat(path, "/");
2659         strcat(path, vp->name);
2660         return path;
2661     }
2662     return 0;
2663 }
2664
2665 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2666  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2667  */
2668 static int
2669 IsVnodeOrphaned(VnodeId vnode)
2670 {
2671     struct VnodeEssence *vep;
2672
2673     if (vnode == 0)
2674         return (1);             /* Vnode zero does not exist */
2675     if (vnode == 1)
2676         return (0);             /* The root dir vnode is always claimed */
2677     vep = CheckVnodeNumber(vnode);      /* Get the vnode essence */
2678     if (!vep || !vep->claimed)
2679         return (1);             /* Vnode is not claimed - it is orphaned */
2680
2681     return (IsVnodeOrphaned(vep->parent));
2682 }
2683
2684 void
2685 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2686            IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2687            int *rootdirfound)
2688 {
2689     static struct DirSummary dir;
2690     static struct DirHandle dirHandle;
2691     struct VnodeEssence *parent;
2692     static char path[MAXPATHLEN];
2693     int dirok, code;
2694
2695     if (dirVnodeInfo->vnodes[i].salvaged)
2696         return;                 /* already salvaged */
2697
2698     dir.rwVid = rwVid;
2699     dirVnodeInfo->vnodes[i].salvaged = 1;
2700
2701     if (dirVnodeInfo->inodes[i] == 0)
2702         return;                 /* Not allocated to a directory */
2703
2704     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2705         if (dirVnodeInfo->vnodes[i].parent) {
2706             Log("Bad parent, vnode 1; %s...\n",
2707                 (Testing ? "skipping" : "salvaging"));
2708             dirVnodeInfo->vnodes[i].parent = 0;
2709             dirVnodeInfo->vnodes[i].changed = 1;
2710         }
2711     } else {
2712         parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2713         if (parent && parent->salvaged == 0)
2714             SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2715                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2716                        rootdir, rootdirfound);
2717     }
2718
2719     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2720     dir.unique = dirVnodeInfo->vnodes[i].unique;
2721     dir.copied = 0;
2722     dir.vname = name;
2723     dir.parent = dirVnodeInfo->vnodes[i].parent;
2724     dir.haveDot = dir.haveDotDot = 0;
2725     dir.ds_linkH = alinkH;
2726     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2727                         dirVnodeInfo->inodes[i]);
2728
2729     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2730     if (!dirok) {
2731         if (!RebuildDirs) {
2732             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2733                 (Testing ? "skipping" : "salvaging"));
2734         }
2735         if (!Testing) {
2736             CopyAndSalvage(&dir);
2737             dirok = 1;
2738         }
2739     }
2740     dirHandle = dir.dirHandle;
2741
2742     dir.name =
2743         GetDirName(bitNumberToVnodeNumber(i, vLarge),
2744                    &dirVnodeInfo->vnodes[i], path);
2745
2746     if (dirok) {
2747         /* If enumeration failed for random reasons, we will probably delete
2748          * too much stuff, so we guard against this instead.
2749          */
2750         assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2751     }
2752
2753     /* Delete the old directory if it was copied in order to salvage.
2754      * CopyOnWrite has written the new inode # to the disk, but we still
2755      * have the old one in our local structure here.  Thus, we idec the
2756      * local dude.
2757      */
2758     DFlush();
2759     if (dir.copied && !Testing) {
2760         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2761         assert(code == 0);
2762         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2763     }
2764
2765     /* Remember rootdir DirSummary _after_ it has been judged */
2766     if (dir.vnodeNumber == 1 && dir.unique == 1) {
2767         memcpy(rootdir, &dir, sizeof(struct DirSummary));
2768         *rootdirfound = 1;
2769     }
2770
2771     return;
2772 }
2773
2774 int
2775 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2776 {
2777     /* This routine, for now, will only be called for read-write volumes */
2778     int i, j, code;
2779     int BlocksInVolume = 0, FilesInVolume = 0;
2780     register VnodeClass class;
2781     struct DirSummary rootdir, oldrootdir;
2782     struct VnodeInfo *dirVnodeInfo;
2783     struct VnodeDiskObject vnode;
2784     VolumeDiskData volHeader;
2785     VolumeId vid;
2786     int orphaned, rootdirfound = 0;
2787     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
2788     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
2789     struct VnodeEssence *vep;
2790     afs_int32 v, pv;
2791     IHandle_t *h;
2792     afs_sfsize_t nBytes;
2793     ViceFid pa;
2794     VnodeId LFVnode, ThisVnode;
2795     Unique LFUnique, ThisUnique;
2796     char npath[128];
2797
2798     vid = rwIsp->volSummary->header.id;
2799     IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2800     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2801     assert(nBytes == sizeof(volHeader));
2802     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2803     assert(volHeader.destroyMe != DESTROY_ME);
2804     /* (should not have gotten this far with DESTROY_ME flag still set!) */
2805
2806     DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2807                        &maxunique);
2808     DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2809                        &maxunique);
2810
2811     dirVnodeInfo = &vnodeInfo[vLarge];
2812     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2813         SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2814                    &rootdirfound);
2815     }
2816     if (Showmode) {
2817         IH_RELEASE(h);
2818         return 0;
2819     }
2820
2821     /* Parse each vnode looking for orphaned vnodes and
2822      * connect them to the tree as orphaned (if requested).
2823      */
2824     oldrootdir = rootdir;
2825     for (class = 0; class < nVNODECLASSES; class++) {
2826         for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2827             vep = &(vnodeInfo[class].vnodes[v]);
2828             ThisVnode = bitNumberToVnodeNumber(v, class);
2829             ThisUnique = vep->unique;
2830
2831             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2832                 continue;       /* Ignore unused, claimed, and root vnodes */
2833
2834             /* This vnode is orphaned. If it is a directory vnode, then the '..'
2835              * entry in this vnode had incremented the parent link count (In
2836              * JudgeEntry()). We need to go to the parent and decrement that
2837              * link count. But if the parent's unique is zero, then the parent
2838              * link count was not incremented in JudgeEntry().
2839              */
2840             if (class == vLarge) {      /* directory vnode */
2841                 pv = vnodeIdToBitNumber(vep->parent);
2842                 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2843                     vnodeInfo[vLarge].vnodes[pv].count++;
2844             }
2845
2846             if (!rootdirfound)
2847                 continue;       /* If no rootdir, can't attach orphaned files */
2848
2849             /* Here we attach orphaned files and directories into the
2850              * root directory, LVVnode, making sure link counts stay correct.
2851              */
2852             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2853                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
2854                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
2855
2856                 /* Update this orphaned vnode's info. Its parent info and 
2857                  * link count (do for orphaned directories and files).
2858                  */
2859                 vep->parent = LFVnode;  /* Parent is the root dir */
2860                 vep->unique = LFUnique;
2861                 vep->changed = 1;
2862                 vep->claimed = 1;
2863                 vep->count--;   /* Inc link count (root dir will pt to it) */
2864
2865                 /* If this orphaned vnode is a directory, change '..'. 
2866                  * The name of the orphaned dir/file is unknown, so we
2867                  * build a unique name. No need to CopyOnWrite the directory
2868                  * since it is not connected to tree in BK or RO volume and
2869                  * won't be visible there.
2870                  */
2871                 if (class == vLarge) {
2872                     ViceFid pa;
2873                     DirHandle dh;
2874
2875                     /* Remove and recreate the ".." entry in this orphaned directory */
2876                     SetSalvageDirHandle(&dh, vid, fileSysDevice,
2877                                         vnodeInfo[class].inodes[v]);
2878                     pa.Vnode = LFVnode;
2879                     pa.Unique = LFUnique;
2880                     assert(Delete(&dh, "..") == 0);
2881                     assert(Create(&dh, "..", &pa) == 0);
2882
2883                     /* The original parent's link count was decremented above.
2884                      * Here we increment the new parent's link count.
2885                      */
2886                     pv = vnodeIdToBitNumber(LFVnode);
2887                     vnodeInfo[vLarge].vnodes[pv].count--;
2888
2889                 }
2890
2891                 /* Go to the root dir and add this entry. The link count of the
2892                  * root dir was incremented when ".." was created. Try 10 times.
2893                  */
2894                 for (j = 0; j < 10; j++) {
2895                     pa.Vnode = ThisVnode;
2896                     pa.Unique = ThisUnique;
2897
2898                     (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2899                                        ((class ==
2900                                          vLarge) ? "__ORPHANDIR__" :
2901                                         "__ORPHANFILE__"), ThisVnode,
2902                                        ThisUnique);
2903
2904                     CopyOnWrite(&rootdir);
2905                     code = Create(&rootdir.dirHandle, npath, &pa);
2906                     if (!code)
2907                         break;
2908
2909                     ThisUnique += 50;   /* Try creating a different file */
2910                 }
2911                 assert(code == 0);
2912                 Log("Attaching orphaned %s to volume's root dir as %s\n",
2913                     ((class == vLarge) ? "directory" : "file"), npath);
2914             }
2915         }                       /* for each vnode in the class */
2916     }                           /* for each class of vnode */
2917
2918     /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2919     DFlush();
2920     if (!oldrootdir.copied && rootdir.copied) {
2921         code =
2922             IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2923                    oldrootdir.rwVid);
2924         assert(code == 0);
2925         /* dirVnodeInfo->inodes[?] is not updated with new inode number */
2926     }
2927
2928     DFlush();                   /* Flush the changes */
2929     if (!rootdirfound && (orphans == ORPH_ATTACH)) {
2930         Log("Cannot attach orphaned files and directories: Root directory not found\n");
2931         orphans = ORPH_IGNORE;
2932     }
2933
2934     /* Write out all changed vnodes. Orphaned files and directories
2935      * will get removed here also (if requested).
2936      */
2937     for (class = 0; class < nVNODECLASSES; class++) {
2938         int nVnodes = vnodeInfo[class].nVnodes;
2939         struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2940         struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
2941         FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
2942         BlocksInVolume += vnodeInfo[class].volumeBlockCount;
2943         for (i = 0; i < nVnodes; i++) {
2944             register struct VnodeEssence *vnp = &vnodes[i];
2945             VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
2946
2947             /* If the vnode is good but is unclaimed (not listed in
2948              * any directory entries), then it is orphaned.
2949              */
2950             orphaned = -1;
2951             if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
2952                 vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
2953                 vnp->changed = 1;
2954             }
2955
2956             if (vnp->changed || vnp->count) {
2957                 int oldCount;
2958                 int code;
2959                 nBytes =
2960                     IH_IREAD(vnodeInfo[class].handle,
2961                              vnodeIndexOffset(vcp, vnodeNumber),
2962                              (char *)&vnode, sizeof(vnode));
2963                 assert(nBytes == sizeof(vnode));
2964
2965                 vnode.parent = vnp->parent;
2966                 oldCount = vnode.linkCount;
2967                 vnode.linkCount = vnode.linkCount - vnp->count;
2968
2969                 if (orphaned == -1)
2970                     orphaned = IsVnodeOrphaned(vnodeNumber);
2971                 if (orphaned) {
2972                     if (!vnp->todelete) {
2973                         /* Orphans should have already been attached (if requested) */
2974                         assert(orphans != ORPH_ATTACH);
2975                         oblocks += vnp->blockCount;
2976                         ofiles++;
2977                     }
2978                     if (((orphans == ORPH_REMOVE) || vnp->todelete)
2979                         && !Testing) {
2980                         BlocksInVolume -= vnp->blockCount;
2981                         FilesInVolume--;
2982                         if (VNDISK_GET_INO(&vnode)) {
2983                             code =
2984                                 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
2985                             assert(code == 0);
2986                         }
2987                         memset(&vnode, 0, sizeof(vnode));
2988                     }
2989                 } else if (vnp->count) {
2990                     if (!Showmode) {
2991                         Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
2992                     }
2993                 }
2994
2995                 vnode.dataVersion++;
2996                 if (!Testing) {
2997                     nBytes =
2998                         IH_IWRITE(vnodeInfo[class].handle,
2999                                   vnodeIndexOffset(vcp, vnodeNumber),
3000                                   (char *)&vnode, sizeof(vnode));
3001                     assert(nBytes == sizeof(vnode));
3002                 }
3003                 VolumeChanged = 1;
3004             }
3005         }
3006     }
3007     if (!Showmode && ofiles) {
3008         Log("%s %d orphaned files and directories (approx. %u KB)\n",
3009             (!Testing
3010              && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3011             oblocks);
3012     }
3013
3014     for (class = 0; class < nVNODECLASSES; class++) {
3015         register struct VnodeInfo *vip = &vnodeInfo[class];
3016         for (i = 0; i < vip->nVnodes; i++)
3017             if (vip->vnodes[i].name)
3018                 free(vip->vnodes[i].name);
3019         if (vip->vnodes)
3020             free(vip->vnodes);
3021         if (vip->inodes)
3022             free(vip->inodes);
3023     }
3024
3025     /* Set correct resource utilization statistics */
3026     volHeader.filecount = FilesInVolume;
3027     volHeader.diskused = BlocksInVolume;
3028
3029     /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3030     if (volHeader.uniquifier < (maxunique + 1)) {
3031         if (!Showmode)
3032             Log("Volume uniquifier is too low; fixed\n");
3033         /* Plus 2,000 in case there are workstations out there with
3034          * cached vnodes that have since been deleted
3035          */
3036         volHeader.uniquifier = (maxunique + 1 + 2000);
3037     }
3038
3039     /* Turn off the inUse bit; the volume's been salvaged! */
3040     volHeader.inUse = 0;        /* clear flag indicating inUse@last crash */
3041     volHeader.needsSalvaged = 0;        /* clear 'damaged' flag */
3042     volHeader.inService = 1;    /* allow service again */
3043     volHeader.needsCallback = (VolumeChanged != 0);
3044     volHeader.dontSalvage = DONT_SALVAGE;
3045     VolumeChanged = 0;
3046     if (!Testing) {
3047         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3048         assert(nBytes == sizeof(volHeader));
3049     }
3050     if (!Showmode) {
3051         Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3052             (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3053             FilesInVolume, BlocksInVolume);
3054     }
3055     IH_RELEASE(vnodeInfo[vSmall].handle);
3056     IH_RELEASE(vnodeInfo[vLarge].handle);
3057     IH_RELEASE(h);
3058     return 0;
3059 }
3060
3061 void
3062 ClearROInUseBit(struct VolumeSummary *summary)
3063 {
3064     IHandle_t *h = summary->volumeInfoHandle;
3065     afs_sfsize_t nBytes;
3066
3067     VolumeDiskData volHeader;
3068
3069     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3070     assert(nBytes == sizeof(volHeader));
3071     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3072     volHeader.inUse = 0;
3073     volHeader.needsSalvaged = 0;
3074     volHeader.inService = 1;
3075     volHeader.dontSalvage = DONT_SALVAGE;
3076     if (!Testing) {
3077         nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3078         assert(nBytes == sizeof(volHeader));
3079     }
3080 }
3081
3082 /* MaybeZapVolume
3083  * Possible delete the volume.
3084  *
3085  * deleteMe - Always do so, only a partial volume.
3086  */
3087 void
3088 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3089                int check)
3090 {
3091     if (readOnly(isp) || deleteMe) {
3092         if (isp->volSummary && isp->volSummary->fileName) {
3093             if (deleteMe) {
3094                 if (!Showmode)
3095                     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);
3096                 if (!Showmode)
3097                     Log("It will be deleted on this server (you may find it elsewhere)\n");
3098             } else {
3099                 if (!Showmode)
3100                     Log("Volume %u needs to be salvaged.  Since it is read-only, however,\n", isp->volumeId);
3101                 if (!Showmode)
3102                     Log("it will be deleted instead.  It should be recloned.\n");
3103             }
3104             if (!Testing)
3105                 unlink(isp->volSummary->fileName);
3106         }
3107     } else if (!check) {
3108         Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3109             isp->volumeId);
3110         Abort("Salvage of volume %u aborted\n", isp->volumeId);
3111     }
3112 }
3113
3114
3115 void
3116 AskOffline(VolumeId volumeId)
3117 {
3118     afs_int32 code, i;
3119
3120     for (i = 0; i < 3; i++) {
3121         code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3122
3123         if (code == SYNC_OK) {
3124             break;
3125         } else if (code == SYNC_DENIED) {
3126 #ifdef DEMAND_ATTACH_ENABLE
3127             Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
3128 #else
3129             Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
3130 #endif
3131             Abort("Salvage aborted\n");
3132         } else if (code == SYNC_BAD_COMMAND) {
3133             Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3134                 FSYNC_VOL_OFF);
3135 #ifdef DEMAND_ATTACH_ENABLE
3136             Log("AskOffline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3137 #else
3138             Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3139 #endif
3140             Abort("Salvage aborted\n");
3141         } else if (i < 2) {
3142             /* try it again */
3143             Log("AskOffline:  request for fileserver to take volume offline failed; trying again...\n");
3144             FSYNC_clientFinis();
3145             FSYNC_clientInit();
3146         }
3147     }
3148     if (code != SYNC_OK) {
3149         Log("AskOffline:  request for fileserver to take volume offline failed; salvage aborting.\n");
3150         Abort("Salvage aborted\n");
3151     }
3152 }
3153
3154 void
3155 AskOnline(VolumeId volumeId, char *partition)
3156 {
3157     afs_int32 code, i;
3158
3159     for (i = 0; i < 3; i++) {
3160         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3161
3162         if (code == SYNC_OK) {
3163             break;
3164         } else if (code == SYNC_DENIED) {
3165             Log("AskOnline:  file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3166         } else if (code == SYNC_BAD_COMMAND) {
3167             Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
3168                 FSYNC_VOL_ON);
3169 #ifdef DEMAND_ATTACH_ENABLE
3170             Log("AskOnline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3171 #else
3172             Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3173 #endif
3174             break;
3175         } else if (i < 2) {
3176             /* try it again */
3177             Log("AskOnline:  request for fileserver to take volume offline failed; trying again...\n");
3178             FSYNC_clientFinis();
3179             FSYNC_clientInit();
3180         }
3181     }
3182 }
3183
3184 int
3185 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3186 {
3187     /* Volume parameter is passed in case iopen is upgraded in future to
3188      * require a volume Id to be passed
3189      */
3190     char buf[4096];
3191     IHandle_t *srcH, *destH;
3192     FdHandle_t *srcFdP, *destFdP;
3193     register int n = 0;
3194
3195     IH_INIT(srcH, device, rwvolume, inode1);
3196     srcFdP = IH_OPEN(srcH);
3197     assert(srcFdP != NULL);
3198     IH_INIT(destH, device, rwvolume, inode2);
3199     destFdP = IH_OPEN(destH);
3200     assert(n != -1);
3201     while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3202         assert(FDH_WRITE(destFdP, buf, n) == n);
3203     assert(n == 0);
3204     FDH_REALLYCLOSE(srcFdP);
3205     FDH_REALLYCLOSE(destFdP);
3206     IH_RELEASE(srcH);
3207     IH_RELEASE(destH);
3208     return 0;
3209 }
3210
3211 void
3212 PrintInodeList(void)
3213 {
3214     register struct ViceInodeInfo *ip;
3215     struct ViceInodeInfo *buf;
3216     struct afs_stat status;
3217     register nInodes;
3218
3219     assert(afs_fstat(inodeFd, &status) == 0);
3220     buf = (struct ViceInodeInfo *)malloc(status.st_size);
3221     assert(buf != NULL);
3222     nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3223     assert(read(inodeFd, buf, status.st_size) == status.st_size);
3224     for (ip = buf; nInodes--; ip++) {
3225         Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3226             PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3227             (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3228             ip->u.param[2], ip->u.param[3]);
3229     }
3230     free(buf);
3231 }
3232
3233 void
3234 PrintInodeSummary(void)
3235 {
3236     int i;
3237     struct InodeSummary *isp;
3238
3239     for (i = 0; i < nVolumesInInodeFile; i++) {
3240         isp = &inodeSummary[i];
3241         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);
3242     }
3243 }
3244
3245 void
3246 PrintVolumeSummary(void)
3247 {
3248     int i;
3249     struct VolumeSummary *vsp;
3250
3251     for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3252         Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3253     }
3254 }
3255
3256 int
3257 Fork(void)
3258 {
3259     int f;
3260 #ifdef AFS_NT40_ENV
3261     f = 0;
3262     assert(0);                  /* Fork is never executed in the NT code path */
3263 #else
3264     f = fork();
3265     assert(f >= 0);
3266 #endif
3267     return f;
3268 }
3269
3270 void
3271 Exit(code)
3272      int code;
3273 {
3274     if (ShowLog)
3275         showlog();
3276 #ifdef AFS_NT40_ENV
3277     if (main_thread != pthread_self())
3278         pthread_exit((void *)code);
3279     else
3280         exit(code);
3281 #else
3282     exit(code);
3283 #endif
3284 }
3285
3286 int
3287 Wait(char *prog)
3288 {
3289     int status;
3290     int pid;
3291     pid = wait(&status);
3292     assert(pid != -1);
3293     if (WCOREDUMP(status))
3294         Log("\"%s\" core dumped!\n", prog);
3295     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3296         return -1;
3297     return pid;
3298 }
3299
3300 static char *
3301 TimeStamp(time_t clock, int precision)
3302 {
3303     struct tm *lt;
3304     static char timestamp[20];
3305     lt = localtime(&clock);
3306     if (precision)
3307         (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3308     else
3309         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3310     return timestamp;
3311 }
3312
3313 void
3314 CheckLogFile(char * log_path)
3315 {
3316     char oldSlvgLog[AFSDIR_PATH_MAX];
3317
3318 #ifndef AFS_NT40_ENV
3319     if (useSyslog) {
3320         ShowLog = 0;
3321         return;
3322     }
3323 #endif
3324
3325     strcpy(oldSlvgLog, log_path);
3326     strcat(oldSlvgLog, ".old");
3327     if (!logFile) {
3328         renamefile(log_path, oldSlvgLog);
3329         logFile = afs_fopen(log_path, "a");
3330
3331         if (!logFile) {         /* still nothing, use stdout */
3332             logFile = stdout;
3333             ShowLog = 0;
3334         }
3335 #ifndef AFS_NAMEI_ENV
3336         AFS_DEBUG_IOPS_LOG(logFile);
3337 #endif
3338     }
3339 }
3340
3341 #ifndef AFS_NT40_ENV
3342 void
3343 TimeStampLogFile(char * log_path)
3344 {
3345     char stampSlvgLog[AFSDIR_PATH_MAX];
3346     struct tm *lt;
3347     time_t now;
3348
3349     now = time(0);
3350     lt = localtime(&now);
3351     (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3352                        "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3353                        log_path, lt->tm_year + 1900,
3354                        lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3355                        lt->tm_sec);
3356
3357     /* try to link the logfile to a timestamped filename */
3358     /* if it fails, oh well, nothing we can do */
3359     link(log_path, stampSlvgLog);
3360 }
3361 #endif
3362
3363 void
3364 showlog(void)
3365 {
3366     char line[256];
3367
3368 #ifndef AFS_NT40_ENV
3369     if (useSyslog) {
3370         printf("Can't show log since using syslog.\n");
3371         fflush(stdout);
3372         return;
3373     }
3374 #endif
3375
3376     rewind(logFile);
3377     fclose(logFile);
3378
3379     logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3380
3381     if (!logFile)
3382         printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3383     else {
3384         rewind(logFile);
3385         while (fgets(line, sizeof(line), logFile))
3386             printf("%s", line);
3387         fflush(stdout);
3388     }
3389 }
3390
3391 void
3392 Log(const char *format, ...)
3393 {
3394     struct timeval now;
3395     char tmp[1024];
3396     va_list args;
3397
3398     va_start(args, format);
3399     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3400     va_end(args);
3401 #ifndef AFS_NT40_ENV
3402     if (useSyslog) {
3403         syslog(LOG_INFO, "%s", tmp);
3404     } else
3405 #endif
3406     {
3407         gettimeofday(&now, 0);
3408         fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3409         fflush(logFile);
3410     }
3411 }
3412
3413 void
3414 Abort(const char *format, ...)
3415 {
3416     va_list args;
3417     char tmp[1024];
3418
3419     va_start(args, format);
3420     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3421     va_end(args);
3422 #ifndef AFS_NT40_ENV
3423     if (useSyslog) {
3424         syslog(LOG_INFO, "%s", tmp);
3425     } else
3426 #endif
3427     {
3428         fprintf(logFile, "%s", tmp);
3429         fflush(logFile);
3430         if (ShowLog)
3431             showlog();
3432     }
3433
3434     if (debug)
3435         abort();
3436     Exit(1);
3437 }
3438
3439 char *
3440 ToString(char *s)
3441 {
3442     register char *p;
3443     p = (char *)malloc(strlen(s) + 1);
3444     assert(p != NULL);
3445     strcpy(p, s);
3446     return p;
3447
3448 }
3449
3450 /* Remove the FORCESALVAGE file */
3451 void
3452 RemoveTheForce(char *path)
3453 {
3454     if (!Testing && ForceSalvage) {
3455         if (chdir(path) == 0)
3456             unlink("FORCESALVAGE");
3457     }
3458 }
3459
3460 #ifndef AFS_AIX32_ENV
3461 /*
3462  * UseTheForceLuke -    see if we can use the force
3463  */
3464 int
3465 UseTheForceLuke(char *path)
3466 {
3467     struct afs_stat force;
3468
3469     assert(chdir(path) != -1);
3470
3471     return (afs_stat("FORCESALVAGE", &force) == 0);
3472 }
3473 #else
3474 /*
3475  * UseTheForceLuke -    see if we can use the force
3476  *
3477  * NOTE:
3478  *      The VRMIX fsck will not muck with the filesystem it is supposedly
3479  *      fixing and create a "FORCESALVAGE" file (by design).  Instead, we
3480  *      muck directly with the root inode, which is within the normal
3481  *      domain of fsck.
3482  *      ListViceInodes() has a side effect of setting ForceSalvage if
3483  *      it detects a need, based on root inode examination.
3484  */
3485 int
3486 UseTheForceLuke(char *path)
3487 {
3488
3489     return 0;                   /* sorry OB1    */
3490 }
3491 #endif
3492
3493 #ifdef AFS_NT40_ENV
3494 /* NT support routines */
3495
3496 static char execpathname[MAX_PATH];
3497 int
3498 nt_SalvagePartition(char *partName, int jobn)
3499 {
3500     int pid;
3501     int n;
3502     childJob_t job;
3503     if (!*execpathname) {
3504         n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3505         if (!n || n == 1023)
3506             return -1;
3507     }
3508     job.cj_magic = SALVAGER_MAGIC;
3509     job.cj_number = jobn;
3510     (void)strcpy(job.cj_part, partName);
3511     pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3512     return pid;
3513 }
3514
3515 int
3516 nt_SetupPartitionSalvage(void *datap, int len)
3517 {
3518     childJob_t *jobp = (childJob_t *) datap;
3519     char logname[AFSDIR_PATH_MAX];
3520
3521     if (len != sizeof(childJob_t))
3522         return -1;
3523     if (jobp->cj_magic != SALVAGER_MAGIC)
3524         return -1;
3525     myjob = *jobp;
3526
3527     /* Open logFile */
3528     (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3529                   myjob.cj_number);
3530     logFile = afs_fopen(logname, "w");
3531     if (!logFile)
3532         logFile = stdout;
3533
3534     return 0;
3535 }
3536
3537
3538 #endif /* AFS_NT40_ENV */