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