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