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