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