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