salvager: Fix in-memory invalid linktable counts
[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 only the RO is present on this partition, the link table
2010          * shows up as a RW volume special file. Need to make sure the
2011          * salvager doesn't try to salvage the non-existent RW.
2012          */
2013         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
2014             /* If this only special inode is the link table, continue */
2015             if (inodes->u.special.type == VI_LINKTABLE) {
2016                 haveRWvolume = 0;
2017                 continue;
2018             }
2019         }
2020 #endif
2021         if (!Showmode)
2022             Log("%s VOLUME %" AFS_VOLID_FMT "%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
2023                 afs_printable_VolumeId_lu(lisp->volumeId), (Testing ? "(READONLY mode)" : ""));
2024         /* Check inodes twice.  The second time do things seriously.  This
2025          * way the whole RO volume can be deleted, below, if anything goes wrong */
2026         for (check = 1; check >= 0; check--) {
2027             int deleteMe;
2028             if (SalvageVolumeHeaderFile(salvinfo, lisp, allInodes, rw, check, &deleteMe)
2029                 == -1) {
2030                 MaybeZapVolume(salvinfo, lisp, "Volume header", deleteMe, check);
2031                 if (rw && deleteMe) {
2032                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
2033                                          * volume won't be called */
2034                     break;
2035                 }
2036                 if (!rw)
2037                     break;
2038             }
2039             if (rw && check == 1)
2040                 continue;
2041             if (SalvageVnodes(salvinfo, isp, lisp, allInodes, check) == -1) {
2042                 MaybeZapVolume(salvinfo, lisp, "Vnode index", 0, check);
2043                 break;
2044             }
2045         }
2046     }
2047
2048     /* Fix actual inode counts */
2049     if (!Showmode) {
2050         afs_ino_str_t stmp;
2051         Log("totalInodes %d\n",totalInodes);
2052         for (ip = inodes; totalInodes; ip++, totalInodes--) {
2053             static int TraceBadLinkCounts = 0;
2054 #ifdef AFS_NAMEI_ENV
2055             if (salvinfo->VGLinkH->ih_ino == ip->inodeNumber) {
2056                 dec_VGLinkH = ip->linkCount - salvinfo->VGLinkH_cnt;
2057                 VGLinkH_p1 = ip->u.param[0];
2058                 continue;       /* Deal with this last. */
2059             }
2060 #endif
2061             if (ip->linkCount != 0 && TraceBadLinkCounts) {
2062                 TraceBadLinkCounts--;   /* Limit reports, per volume */
2063                 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 */
2064             }
2065             while (ip->linkCount > 0) {
2066                 /* below used to assert, not break */
2067                 if (!Testing) {
2068                     if (IH_DEC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2069                         Log("idec failed. inode %s errno %d\n",
2070                             PrintInode(stmp, ip->inodeNumber), errno);
2071                         break;
2072                     }
2073                 }
2074                 ip->linkCount--;
2075             }
2076             while (ip->linkCount < 0) {
2077                 /* these used to be asserts */
2078                 if (!Testing) {
2079                     if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2080                         Log("iinc failed. inode %s errno %d\n",
2081                             PrintInode(stmp, ip->inodeNumber), errno);
2082                         break;
2083                     }
2084                 }
2085                 ip->linkCount++;
2086             }
2087         }
2088 #ifdef AFS_NAMEI_ENV
2089         while (dec_VGLinkH > 0) {
2090             if (IH_DEC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2091                 Log("idec failed on link table, errno = %d\n", errno);
2092             }
2093             dec_VGLinkH--;
2094         }
2095         while (dec_VGLinkH < 0) {
2096             if (IH_INC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2097                 Log("iinc failed on link table, errno = %d\n", errno);
2098             }
2099             dec_VGLinkH++;
2100         }
2101 #endif
2102     }
2103     free(inodes);
2104     /* Directory consistency checks on the rw volume */
2105     if (haveRWvolume)
2106         SalvageVolume(salvinfo, isp, salvinfo->VGLinkH);
2107     IH_RELEASE(salvinfo->VGLinkH);
2108
2109     if (canfork && !debug) {
2110         ShowLog = 0;
2111         Exit(0);
2112     }
2113 }
2114
2115 int
2116 QuickCheck(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
2117 {
2118     /* Check headers BEFORE forking */
2119     int i;
2120     IHandle_t *h;
2121
2122     for (i = 0; i < nVols; i++) {
2123         struct VolumeSummary *vs = isp[i].volSummary;
2124         VolumeDiskData volHeader;
2125         if (!vs) {
2126             /* Don't salvage just because phantom rw volume is there... */
2127             /* (If a read-only volume exists, read/write inodes must also exist) */
2128             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2129                 continue;
2130             return 0;
2131         }
2132         IH_INIT(h, salvinfo->fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2133         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2134             == sizeof(volHeader)
2135             && volHeader.stamp.magic == VOLUMEINFOMAGIC
2136             && volHeader.dontSalvage == DONT_SALVAGE
2137             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2138             if (volHeader.inUse != 0) {
2139                 volHeader.inUse = 0;
2140                 volHeader.inService = 1;
2141                 if (!Testing) {
2142                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2143                         != sizeof(volHeader)) {
2144                         IH_RELEASE(h);
2145                         return 0;
2146                     }
2147                 }
2148             }
2149             IH_RELEASE(h);
2150         } else {
2151             IH_RELEASE(h);
2152             return 0;
2153         }
2154     }
2155     return 1;
2156 }
2157
2158
2159 /* SalvageVolumeHeaderFile
2160  *
2161  * Salvage the top level V*.vol header file. Make sure the special files
2162  * exist and that there are no duplicates.
2163  *
2164  * Calls SalvageHeader for each possible type of volume special file.
2165  */
2166
2167 int
2168 SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
2169                         struct ViceInodeInfo *inodes, int RW,
2170                         int check, int *deleteMe)
2171 {
2172     int i;
2173     struct ViceInodeInfo *ip;
2174     int allinodesobsolete = 1;
2175     struct VolumeDiskHeader diskHeader;
2176     afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
2177     int *skip;
2178     struct VolumeHeader tempHeader;
2179     struct afs_inode_info stuff[MAXINODETYPE];
2180
2181     /* keeps track of special inodes that are probably 'good'; they are
2182      * referenced in the vol header, and are included in the given inodes
2183      * array */
2184     struct {
2185         int valid;
2186         Inode inode;
2187     } goodspecial[MAXINODETYPE];
2188
2189     if (deleteMe)
2190         *deleteMe = 0;
2191
2192     memset(goodspecial, 0, sizeof(goodspecial));
2193
2194     skip = calloc(isp->nSpecialInodes, sizeof(*skip));
2195     if (skip == NULL) {
2196         Log("cannot allocate memory for inode skip array when salvaging "
2197             "volume %lu; not performing duplicate special inode recovery\n",
2198             afs_printable_uint32_lu(isp->volumeId));
2199         /* still try to perform the salvage; the skip array only does anything
2200          * if we detect duplicate special inodes */
2201     }
2202
2203     init_inode_info(&tempHeader, stuff);
2204
2205     /*
2206      * First, look at the special inodes and see if any are referenced by
2207      * the existing volume header. If we find duplicate special inodes, we
2208      * can use this information to use the referenced inode (it's more
2209      * likely to be the 'good' one), and throw away the duplicates.
2210      */
2211     if (isp->volSummary && skip) {
2212         /* use tempHeader, so we can use the stuff[] array to easily index
2213          * into the isp->volSummary special inodes */
2214         memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2215
2216         for (i = 0; i < isp->nSpecialInodes; i++) {
2217             ip = &inodes[isp->index + i];
2218             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2219                 /* will get taken care of in a later loop */
2220                 continue;
2221             }
2222             if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2223                 goodspecial[ip->u.special.type-1].valid = 1;
2224                 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2225             }
2226         }
2227     }
2228
2229     memset(&tempHeader, 0, sizeof(tempHeader));
2230     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2231     tempHeader.stamp.version = VOLUMEHEADERVERSION;
2232     tempHeader.id = isp->volumeId;
2233     tempHeader.parent = isp->RWvolumeId;
2234
2235     /* Check for duplicates (inodes are sorted by type field) */
2236     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2237         ip = &inodes[isp->index + i];
2238         if (ip->u.special.type == (ip + 1)->u.special.type) {
2239             afs_ino_str_t stmp1, stmp2;
2240
2241             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2242                 /* Will be caught in the loop below */
2243                 continue;
2244             }
2245             if (!Showmode) {
2246                 Log("Duplicate special %d inodes for volume %" AFS_VOLID_FMT " found (%s, %s);\n",
2247                     ip->u.special.type, afs_printable_VolumeId_lu(isp->volumeId),
2248                     PrintInode(stmp1, ip->inodeNumber),
2249                     PrintInode(stmp2, (ip+1)->inodeNumber));
2250             }
2251             if (skip && goodspecial[ip->u.special.type-1].valid) {
2252                 Inode gi = goodspecial[ip->u.special.type-1].inode;
2253
2254                 if (!Showmode) {
2255                     Log("using special inode referenced by vol header (%s)\n",
2256                         PrintInode(stmp1, gi));
2257                 }
2258
2259                 /* the volume header references some special inode of
2260                  * this type in the inodes array; are we it? */
2261                 if (ip->inodeNumber != gi) {
2262                     skip[i] = 1;
2263                 } else if ((ip+1)->inodeNumber != gi) {
2264                     /* in case this is the last iteration; we need to
2265                      * make sure we check ip+1, too */
2266                     skip[i+1] = 1;
2267                 }
2268             } else {
2269                 if (!Showmode)
2270                     Log("cannot determine which is correct; salvage of volume %" AFS_VOLID_FMT " aborted\n", afs_printable_VolumeId_lu(isp->volumeId));
2271                 if (skip) {
2272                     free(skip);
2273                 }
2274                 return -1;
2275             }
2276         }
2277     }
2278     for (i = 0; i < isp->nSpecialInodes; i++) {
2279         afs_ino_str_t stmp;
2280         ip = &inodes[isp->index + i];
2281         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2282             if (check) {
2283                 Log("Rubbish header inode %s of type %d\n",
2284                     PrintInode(stmp, ip->inodeNumber),
2285                     ip->u.special.type);
2286                 if (skip) {
2287                     free(skip);
2288                 }
2289                 return -1;
2290             }
2291             Log("Rubbish header inode %s of type %d; deleted\n",
2292                 PrintInode(stmp, ip->inodeNumber),
2293                 ip->u.special.type);
2294         } else if (!stuff[ip->u.special.type - 1].obsolete) {
2295             if (skip && skip[i]) {
2296                 if (orphans == ORPH_REMOVE) {
2297                     Log("Removing orphan special inode %s of type %d\n",
2298                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2299                     continue;
2300                 } else {
2301                     Log("Ignoring orphan special inode %s of type %d\n",
2302                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2303                     /* fall through to the ip->linkCount--; line below */
2304                 }
2305             } else {
2306                 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2307                 allinodesobsolete = 0;
2308             }
2309             if (!check && ip->u.special.type != VI_LINKTABLE)
2310                 ip->linkCount--;        /* Keep the inode around */
2311         }
2312     }
2313     if (skip) {
2314         free(skip);
2315     }
2316     skip = NULL;
2317
2318     if (allinodesobsolete) {
2319         if (deleteMe)
2320             *deleteMe = 1;
2321         return -1;
2322     }
2323
2324     if (!check)
2325         salvinfo->VGLinkH_cnt++;                /* one for every header. */
2326
2327     if (!RW && !check && isp->volSummary) {
2328         ClearROInUseBit(isp->volSummary);
2329         return 0;
2330     }
2331
2332     for (i = 0; i < MAXINODETYPE; i++) {
2333         if (stuff[i].inodeType == VI_LINKTABLE) {
2334             /* Gross hack: SalvageHeader does a bcmp on the volume header.
2335              * And we may have recreated the link table earlier, so set the
2336              * RW header as well. The header magic was already checked.
2337              */
2338             if (VALID_INO(salvinfo->VGLinkH->ih_ino)) {
2339                 *stuff[i].inode = salvinfo->VGLinkH->ih_ino;
2340             }
2341             continue;
2342         }
2343         if (SalvageHeader(salvinfo, &stuff[i], isp, check, deleteMe) == -1 && check)
2344             return -1;
2345     }
2346
2347     if (isp->volSummary == NULL) {
2348         char path[64];
2349         char headerName[64];
2350         snprintf(headerName, sizeof headerName, VFORMAT,
2351                  afs_printable_VolumeId_lu(isp->volumeId));
2352         snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
2353                  salvinfo->fileSysPath, headerName);
2354         if (check) {
2355             Log("No header file for volume %" AFS_VOLID_FMT "\n", afs_printable_VolumeId_lu(isp->volumeId));
2356             return -1;
2357         }
2358         if (!Showmode)
2359             Log("No header file for volume %" AFS_VOLID_FMT "; %screating %s\n",
2360                 afs_printable_VolumeId_lu(isp->volumeId), (Testing ? "it would have been " : ""),
2361                 path);
2362         isp->volSummary = calloc(1, sizeof(struct VolumeSummary));
2363
2364         writefunc = VCreateVolumeDiskHeader;
2365     } else {
2366         char path[64];
2367         char headerName[64];
2368         /* hack: these two fields are obsolete... */
2369         isp->volSummary->header.volumeAcl = 0;
2370         isp->volSummary->header.volumeMountTable = 0;
2371
2372         if (memcmp
2373             (&isp->volSummary->header, &tempHeader,
2374              sizeof(struct VolumeHeader))) {
2375             VolumeExternalName_r(isp->volumeId, headerName, sizeof(headerName));
2376             snprintf(path, sizeof path, "%s" OS_DIRSEP "%s",
2377                      salvinfo->fileSysPath, headerName);
2378
2379             Log("Header file %s is damaged or no longer valid%s\n", path,
2380                 (check ? "" : "; repairing"));
2381             if (check)
2382                 return -1;
2383
2384             writefunc = VWriteVolumeDiskHeader;
2385         }
2386     }
2387     if (writefunc) {
2388         memcpy(&isp->volSummary->header, &tempHeader,
2389                sizeof(struct VolumeHeader));
2390         if (Testing) {
2391             if (!Showmode)
2392                 Log("It would have written a new header file for volume %" AFS_VOLID_FMT "\n",
2393                     afs_printable_VolumeId_lu(isp->volumeId));
2394         } else {
2395             afs_int32 code;
2396             VolumeHeaderToDisk(&diskHeader, &tempHeader);
2397             code = (*writefunc)(&diskHeader, salvinfo->fileSysPartition);
2398             if (code) {
2399                 Log("Error %ld writing volume header file for volume %" AFS_VOLID_FMT "\n",
2400                     afs_printable_int32_ld(code),
2401                     afs_printable_VolumeId_lu(diskHeader.id));
2402                 return -1;
2403             }
2404         }
2405     }
2406     IH_INIT(isp->volSummary->volumeInfoHandle, salvinfo->fileSysDevice, isp->RWvolumeId,
2407             isp->volSummary->header.volumeInfo);
2408     return 0;
2409 }
2410
2411 int
2412 SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
2413               struct InodeSummary *isp, int check, int *deleteMe)
2414 {
2415     union {
2416         VolumeDiskData volumeInfo;
2417         struct versionStamp fileHeader;
2418     } header;
2419     IHandle_t *specH;
2420     int recreate = 0;
2421     ssize_t nBytes;
2422     FdHandle_t *fdP;
2423
2424     if (sp->obsolete)
2425         return 0;
2426 #ifndef AFS_NAMEI_ENV
2427     if (sp->inodeType == VI_LINKTABLE)
2428         return 0; /* header magic was already checked */
2429 #endif
2430     if (*(sp->inode) == 0) {
2431         if (check) {
2432             Log("Missing inode in volume header (%s)\n", sp->description);
2433             return -1;
2434         }
2435         if (!Showmode)
2436             Log("Missing inode in volume header (%s); %s\n", sp->description,
2437                 (Testing ? "it would have recreated it" : "recreating"));
2438         if (!Testing) {
2439             *(sp->inode) =
2440                 IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
2441                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2442             if (!VALID_INO(*(sp->inode)))
2443                 Abort
2444                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2445                      sp->description, errno);
2446         }
2447         recreate = 1;
2448     }
2449
2450     IH_INIT(specH, salvinfo->fileSysDevice, isp->RWvolumeId, *(sp->inode));
2451     fdP = IH_OPEN(specH);
2452     if (OKToZap && (fdP == NULL) && BadError(errno)) {
2453         /* bail out early and destroy the volume */
2454         if (!Showmode)
2455             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2456         if (deleteMe)
2457             *deleteMe = 1;
2458         IH_RELEASE(specH);
2459         return -1;
2460     }
2461     if (fdP == NULL)
2462         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2463               sp->description, errno);
2464
2465     if (!recreate
2466         && (FDH_PREAD(fdP, (char *)&header, sp->size, 0) != sp->size
2467             || header.fileHeader.magic != sp->stamp.magic)) {
2468         if (check) {
2469             Log("Part of the header (%s) is corrupted\n", sp->description);
2470             FDH_REALLYCLOSE(fdP);
2471             IH_RELEASE(specH);
2472             return -1;
2473         }
2474         Log("Part of the header (%s) is corrupted; recreating\n",
2475             sp->description);
2476         recreate = 1;
2477         /* header can be garbage; make sure we don't read garbage data from
2478          * it below */
2479         memset(&header, 0, sizeof(header));
2480     }
2481 #ifdef AFS_NAMEI_ENV
2482     if (namei_FixSpecialOGM(fdP, check)) {
2483         Log("Error with namei header OGM data (%s)\n", sp->description);
2484         FDH_REALLYCLOSE(fdP);
2485         IH_RELEASE(specH);
2486         return -1;
2487     }
2488 #endif
2489     if (sp->inodeType == VI_VOLINFO
2490         && header.volumeInfo.destroyMe == DESTROY_ME) {
2491         if (deleteMe)
2492             *deleteMe = 1;
2493         FDH_REALLYCLOSE(fdP);
2494         IH_RELEASE(specH);
2495         return -1;
2496     }
2497     if (recreate && !Testing) {
2498         if (check)
2499             Abort
2500                 ("Internal error: recreating volume header (%s) in check mode\n",
2501                  sp->description);
2502         nBytes = FDH_TRUNC(fdP, 0);
2503         if (nBytes == -1)
2504             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2505                   sp->description, errno);
2506
2507         /* The following code should be moved into vutil.c */
2508         if (sp->inodeType == VI_VOLINFO) {
2509             struct timeval tp;
2510             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2511             header.volumeInfo.stamp = sp->stamp;
2512             header.volumeInfo.id = isp->volumeId;
2513             header.volumeInfo.parentId = isp->RWvolumeId;
2514             sprintf(header.volumeInfo.name, "bogus.%" AFS_VOLID_FMT, afs_printable_VolumeId_lu(isp->volumeId));
2515             Log("Warning: the name of volume %" AFS_VOLID_FMT " is now \"bogus.%" AFS_VOLID_FMT "\"\n",
2516                 afs_printable_VolumeId_lu(isp->volumeId), afs_printable_VolumeId_lu(isp->volumeId));
2517             header.volumeInfo.inService = 0;
2518             header.volumeInfo.blessed = 0;
2519             /* The + 1000 is a hack in case there are any files out in venus caches */
2520             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2521             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
2522             header.volumeInfo.needsCallback = 0;
2523             gettimeofday(&tp, NULL);
2524             header.volumeInfo.creationDate = tp.tv_sec;
2525             nBytes =
2526                 FDH_PWRITE(fdP, (char *)&header.volumeInfo,
2527                            sizeof(header.volumeInfo), 0);
2528             if (nBytes != sizeof(header.volumeInfo)) {
2529                 if (nBytes < 0)
2530                     Abort
2531                         ("Unable to write volume header file (%s) (errno = %d)\n",
2532                          sp->description, errno);
2533                 Abort("Unable to write entire volume header file (%s)\n",
2534                       sp->description);
2535             }
2536         } else {
2537             nBytes = FDH_PWRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp), 0);
2538             if (nBytes != sizeof(sp->stamp)) {
2539                 if (nBytes < 0)
2540                     Abort
2541                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2542                          sp->description, errno);
2543                 Abort
2544                     ("Unable to write entire version stamp in volume header file (%s)\n",
2545                      sp->description);
2546             }
2547         }
2548     }
2549     FDH_REALLYCLOSE(fdP);
2550     IH_RELEASE(specH);
2551     if (sp->inodeType == VI_VOLINFO) {
2552         salvinfo->VolInfo = header.volumeInfo;
2553         if (check) {
2554             char update[25];
2555
2556             if (salvinfo->VolInfo.updateDate) {
2557                 strcpy(update, TimeStamp(salvinfo->VolInfo.updateDate, 0));
2558                 if (!Showmode)
2559                     Log("%s (%" AFS_VOLID_FMT ") %supdated %s\n", salvinfo->VolInfo.name,
2560                         afs_printable_VolumeId_lu(salvinfo->VolInfo.id),
2561                         (Testing ? "it would have been " : ""), update);
2562             } else {
2563                 strcpy(update, TimeStamp(salvinfo->VolInfo.creationDate, 0));
2564                 if (!Showmode)
2565                     Log("%s (%" AFS_VOLID_FMT ") not updated (created %s)\n",
2566                         salvinfo->VolInfo.name, afs_printable_VolumeId_lu(salvinfo->VolInfo.id), update);
2567             }
2568
2569         }
2570     }
2571
2572     return 0;
2573 }
2574
2575 int
2576 SalvageVnodes(struct SalvInfo *salvinfo,
2577               struct InodeSummary *rwIsp,
2578               struct InodeSummary *thisIsp,
2579               struct ViceInodeInfo *inodes, int check)
2580 {
2581     int ilarge, ismall, ioffset, RW, nInodes;
2582     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
2583     if (Showmode)
2584         return 0;
2585     RW = (rwIsp == thisIsp);
2586     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2587     ismall =
2588         SalvageIndex(salvinfo, thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2589                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2590     if (check && ismall == -1)
2591         return -1;
2592     ilarge =
2593         SalvageIndex(salvinfo, thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2594                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2595     return (ilarge == 0 && ismall == 0 ? 0 : -1);
2596 }
2597
2598 int
2599 SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
2600              struct ViceInodeInfo *ip, int nInodes,
2601              struct VolumeSummary *volSummary, int check)
2602 {
2603     char buf[SIZEOF_LARGEDISKVNODE];
2604     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2605     int err = 0;
2606     StreamHandle_t *file;
2607     struct VnodeClassInfo *vcp;
2608     afs_sfsize_t size;
2609     afs_sfsize_t nVnodes;
2610     afs_fsize_t vnodeLength;
2611     int vnodeIndex;
2612     afs_ino_str_t stmp1, stmp2;
2613     IHandle_t *handle;
2614     FdHandle_t *fdP;
2615
2616     IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
2617     fdP = IH_OPEN(handle);
2618     opr_Assert(fdP != NULL);
2619     file = FDH_FDOPEN(fdP, "r+");
2620     opr_Assert(file != NULL);
2621     vcp = &VnodeClassInfo[class];
2622     size = OS_SIZE(fdP->fd_fd);
2623     opr_Assert(size != -1);
2624     nVnodes = (size / vcp->diskSize) - 1;
2625     if (nVnodes > 0) {
2626         opr_Assert((nVnodes + 1) * vcp->diskSize == size);
2627         opr_Verify(STREAM_ASEEK(file, vcp->diskSize) == 0);
2628     } else {
2629         nVnodes = 0;
2630     }
2631     for (vnodeIndex = 0;
2632          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2633          nVnodes--, vnodeIndex++) {
2634         if (vnode->type != vNull) {
2635             int vnodeChanged = 0;
2636             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2637             if (VNDISK_GET_INO(vnode) == 0) {
2638                 if (RW) {
2639                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2640                     memset(vnode, 0, vcp->diskSize);
2641                     vnodeChanged = 1;
2642                 }
2643             } else {
2644                 if (vcp->magic != vnode->vnodeMagic) {
2645                     /* bad magic #, probably partially created vnode */
2646                     if (check) {
2647                        Log("Partially allocated vnode %d: bad magic (is %lx should be %lx)\n",
2648                            vnodeNumber, afs_printable_uint32_lu(vnode->vnodeMagic),
2649                            afs_printable_uint32_lu(vcp->magic));
2650                        memset(vnode, 0, vcp->diskSize);
2651                        err = -1;
2652                        goto zooks;
2653                     }
2654                     Log("Partially allocated vnode %d deleted.\n",
2655                         vnodeNumber);
2656                     memset(vnode, 0, vcp->diskSize);
2657                     vnodeChanged = 1;
2658                     goto vnodeDone;
2659                 }
2660                 /* ****** Should do a bit more salvage here:  e.g. make sure
2661                  * vnode type matches what it should be given the index */
2662                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2663 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2664  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2665  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2666  *                  }
2667  */
2668                     ip++;
2669                     nInodes--;
2670                 }
2671                 if (!RW) {
2672                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2673                         /* The following doesn't work, because the version number
2674                          * is not maintained correctly by the file server */
2675                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2676                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2677                          * break; */
2678                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2679                             break;
2680                         ip++;
2681                         nInodes--;
2682                     }
2683                 } else {
2684                     /* For RW volume, look for vnode with matching inode number;
2685                      * if no such match, take the first determined by our sort
2686                      * order */
2687                     struct ViceInodeInfo *lip = ip;
2688                     int lnInodes = nInodes;
2689                     while (lnInodes
2690                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2691                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2692                             ip = lip;
2693                             nInodes = lnInodes;
2694                             break;
2695                         }
2696                         lip++;
2697                         lnInodes--;
2698                     }
2699                 }
2700                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2701                     /* "Matching" inode */
2702                     if (RW) {
2703                         Unique vu, iu;
2704                         FileVersion vd, id;
2705                         vu = vnode->uniquifier;
2706                         iu = ip->u.vnode.vnodeUniquifier;
2707                         vd = vnode->dataVersion;
2708                         id = ip->u.vnode.inodeDataVersion;
2709                         /*
2710                          * Because of the possibility of the uniquifier overflows (> 4M)
2711                          * we compare them modulo the low 22-bits; we shouldn't worry
2712                          * about mismatching since they shouldn't to many old
2713                          * uniquifiers of the same vnode...
2714                          */
2715                         if (IUnique(vu) != IUnique(iu)) {
2716                             if (!Showmode) {
2717                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2718                             }
2719
2720                             vnode->uniquifier = iu;
2721 #ifdef  AFS_3DISPARES
2722                             vnode->dataVersion = (id >= vd ?
2723                                                   /* 90% of 2.1M */
2724                                                   ((id - vd) >
2725                                                    1887437 ? vd : id) :
2726                                                   /* 90% of 2.1M */
2727                                                   ((vd - id) >
2728                                                    1887437 ? id : vd));
2729 #else
2730 #if defined(AFS_SGI_EXMAG)
2731                             vnode->dataVersion = (id >= vd ?
2732                                                   /* 90% of 16M */
2733                                                   ((id - vd) >
2734                                                    15099494 ? vd : id) :
2735                                                   /* 90% of 16M */
2736                                                   ((vd - id) >
2737                                                    15099494 ? id : vd));
2738 #else
2739                             vnode->dataVersion = (id > vd ? id : vd);
2740 #endif /* AFS_SGI_EXMAG */
2741 #endif /* AFS_3DISPARES */
2742                             vnodeChanged = 1;
2743                         } else {
2744                             /* don't bother checking for vd > id any more, since
2745                              * partial file transfers always result in this state,
2746                              * and you can't do much else anyway (you've already
2747                              * found the best data you can) */
2748 #ifdef  AFS_3DISPARES
2749                             if (!vnodeIsDirectory(vnodeNumber)
2750                                 && ((vd < id && (id - vd) < 1887437)
2751                                     || ((vd > id && (vd - id) > 1887437)))) {
2752 #else
2753 #if defined(AFS_SGI_EXMAG)
2754                             if (!vnodeIsDirectory(vnodeNumber)
2755                                 && ((vd < id && (id - vd) < 15099494)
2756                                     || ((vd > id && (vd - id) > 15099494)))) {
2757 #else
2758                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2759 #endif /* AFS_SGI_EXMAG */
2760 #endif
2761                                 if (!Showmode)
2762                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2763                                 vnode->dataVersion = id;
2764                                 vnodeChanged = 1;
2765                             }
2766                         }
2767                     }
2768                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2769                         if (check) {
2770                             if (!Showmode) {
2771                                 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);
2772                             }
2773                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2774                             err = -1;
2775                             goto zooks;
2776                         }
2777                         if (!Showmode) {
2778                             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);
2779                         }
2780                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2781                         vnodeChanged = 1;
2782                     }
2783                     VNDISK_GET_LEN(vnodeLength, vnode);
2784                     if (ip->byteCount != vnodeLength) {
2785                         if (check) {
2786                             if (!Showmode)
2787                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2788                             err = -1;
2789                             goto zooks;
2790                         }
2791                         if (!Showmode)
2792                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2793                         VNDISK_SET_LEN(vnode, ip->byteCount);
2794                         vnodeChanged = 1;
2795                     }
2796                     if (!check)
2797                         ip->linkCount--;        /* Keep the inode around */
2798                     ip++;
2799                     nInodes--;
2800                 } else {        /* no matching inode */
2801                     afs_ino_str_t stmp;
2802                     if (VNDISK_GET_INO(vnode) != 0
2803                         || vnode->type == vDirectory) {
2804                         /* No matching inode--get rid of the vnode */
2805                         if (check) {
2806                             if (VNDISK_GET_INO(vnode)) {
2807                                 if (!Showmode) {
2808                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)));
2809                                 }
2810                             } else {
2811                                 if (!Showmode)
2812                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2813                             }
2814                             err = -1;
2815                             goto zooks;
2816                         }
2817                         if (VNDISK_GET_INO(vnode)) {
2818                             if (!Showmode) {
2819                                 time_t serverModifyTime = vnode->serverModifyTime;
2820                                 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));
2821                             }
2822                         } else {
2823                             if (!Showmode) {
2824                                 time_t serverModifyTime = vnode->serverModifyTime;
2825                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2826                             }
2827                         }
2828                         memset(vnode, 0, vcp->diskSize);
2829                         vnodeChanged = 1;
2830                     } else {
2831                         /* Should not reach here becuase we checked for
2832                          * (inodeNumber == 0) above. And where we zero the vnode,
2833                          * we also goto vnodeDone.
2834                          */
2835                     }
2836                 }
2837                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2838                     ip++;
2839                     nInodes--;
2840                 }
2841             }                   /* VNDISK_GET_INO(vnode) != 0 */
2842           vnodeDone:
2843             opr_Assert(!(vnodeChanged && check));
2844             if (vnodeChanged && !Testing) {
2845                 opr_Verify(IH_IWRITE(handle,
2846                                      vnodeIndexOffset(vcp, vnodeNumber),
2847                                      (char *)vnode, vcp->diskSize)
2848                                 == vcp->diskSize);
2849                 salvinfo->VolumeChanged = 1;    /* For break call back */
2850             }
2851         }
2852     }
2853   zooks:
2854     STREAM_CLOSE(file);
2855     FDH_CLOSE(fdP);
2856     IH_RELEASE(handle);
2857     return err;
2858 }
2859
2860 struct VnodeEssence *
2861 CheckVnodeNumber(struct SalvInfo *salvinfo, VnodeId vnodeNumber)
2862 {
2863     VnodeClass class;
2864     struct VnodeInfo *vip;
2865     int offset;
2866
2867     class = vnodeIdToClass(vnodeNumber);
2868     vip = &salvinfo->vnodeInfo[class];
2869     offset = vnodeIdToBitNumber(vnodeNumber);
2870     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2871 }
2872
2873 void
2874 CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
2875 {
2876     /* Copy the directory unconditionally if we are going to change it:
2877      * not just if was cloned.
2878      */
2879     struct VnodeDiskObject vnode;
2880     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2881     Inode oldinode, newinode;
2882     afs_sfsize_t code;
2883
2884     if (dir->copied || Testing)
2885         return;
2886     DFlush();                   /* Well justified paranoia... */
2887
2888     code =
2889         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2890                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2891                  sizeof(vnode));
2892     opr_Assert(code == sizeof(vnode));
2893     oldinode = VNDISK_GET_INO(&vnode);
2894     /* Increment the version number by a whole lot to avoid problems with
2895      * clients that were promised new version numbers--but the file server
2896      * crashed before the versions were written to disk.
2897      */
2898     newinode =
2899         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2900                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2901                   200);
2902     opr_Assert(VALID_INO(newinode));
2903     opr_Verify(CopyInode(salvinfo->fileSysDevice, oldinode, newinode,
2904                          dir->rwVid) == 0);
2905     vnode.cloned = 0;
2906     VNDISK_SET_INO(&vnode, newinode);
2907     code =
2908         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
2909                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2910                   sizeof(vnode));
2911     opr_Assert(code == sizeof(vnode));
2912
2913     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2914                         salvinfo->fileSysDevice, newinode,
2915                         &salvinfo->VolumeChanged);
2916     /* Don't delete the original inode right away, because the directory is
2917      * still being scanned.
2918      */
2919     dir->copied = 1;
2920 }
2921
2922 /*
2923  * This function should either successfully create a new dir, or give up
2924  * and leave things the way they were.  In particular, if it fails to write
2925  * the new dir properly, it should return w/o changing the reference to the
2926  * old dir.
2927  */
2928 void
2929 CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
2930 {
2931     struct VnodeDiskObject vnode;
2932     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2933     Inode oldinode, newinode;
2934     DirHandle newdir;
2935     FdHandle_t *fdP;
2936     afs_int32 code;
2937     afs_sfsize_t lcode;
2938     afs_int32 parentUnique = 1;
2939     struct VnodeEssence *vnodeEssence;
2940     afs_fsize_t length;
2941
2942     if (Testing)
2943         return;
2944     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2945     lcode =
2946         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2947                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2948                  sizeof(vnode));
2949     opr_Assert(lcode == sizeof(vnode));
2950     oldinode = VNDISK_GET_INO(&vnode);
2951     /* Increment the version number by a whole lot to avoid problems with
2952      * clients that were promised new version numbers--but the file server
2953      * crashed before the versions were written to disk.
2954      */
2955     newinode =
2956         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2957                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2958                   200);
2959     opr_Assert(VALID_INO(newinode));
2960     SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
2961                         &salvinfo->VolumeChanged);
2962
2963     /* Assign . and .. vnode numbers from dir and vnode.parent.
2964      * The uniquifier for . is in the vnode.
2965      * The uniquifier for .. might be set to a bogus value of 1 and
2966      * the salvager will later clean it up.
2967      */
2968     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(salvinfo, vnode.parent))) {
2969         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2970     }
2971     code =
2972         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2973                    vnode.uniquifier,
2974                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2975                    parentUnique);
2976     if (code == 0)
2977         code = DFlush();
2978     if (code) {
2979         /* didn't really build the new directory properly, let's just give up. */
2980         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2981         Log("Directory salvage returned code %d, continuing.\n", code);
2982         if (code) {
2983             Log("also failed to decrement link count on new inode");
2984         }
2985         opr_Assert(0);
2986     }
2987     Log("Checking the results of the directory salvage...\n");
2988     if (!DirOK(&newdir)) {
2989         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2990         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2991         opr_Assert(code == 0);
2992         opr_Assert(0);
2993     }
2994     vnode.cloned = 0;
2995     VNDISK_SET_INO(&vnode, newinode);
2996     length = afs_dir_Length(&newdir);
2997     VNDISK_SET_LEN(&vnode, length);
2998     lcode =
2999         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
3000                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
3001                   sizeof(vnode));
3002     opr_Assert(lcode == sizeof(vnode));
3003     IH_CONDSYNC(salvinfo->vnodeInfo[vLarge].handle);
3004
3005     /* make sure old directory file is really closed */
3006     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
3007     FDH_REALLYCLOSE(fdP);
3008
3009     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
3010     opr_Assert(code == 0);
3011     dir->dirHandle = newdir;
3012 }
3013
3014 /**
3015  * arguments for JudgeEntry.
3016  */
3017 struct judgeEntry_params {
3018     struct DirSummary *dir;    /**< directory we're examining entries in */
3019     struct SalvInfo *salvinfo; /**< SalvInfo for the current salvage job */
3020 };
3021
3022 int
3023 JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
3024            afs_int32 unique)
3025 {
3026     struct judgeEntry_params *params = arock;
3027     struct DirSummary *dir = params->dir;
3028     struct SalvInfo *salvinfo = params->salvinfo;
3029     struct VnodeEssence *vnodeEssence;
3030     afs_int32 dirOrphaned, todelete;
3031
3032     dirOrphaned = IsVnodeOrphaned(salvinfo, dir->vnodeNumber);
3033
3034     vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3035     if (vnodeEssence == NULL) {
3036         if (!Showmode) {
3037             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);
3038         }
3039         if (!Testing) {
3040             CopyOnWrite(salvinfo, dir);
3041             opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
3042         }
3043         return 0;
3044     }
3045 #ifdef AFS_AIX_ENV
3046 #ifndef AFS_NAMEI_ENV
3047     /* On AIX machines, don't allow entries to point to inode 0. That is a special
3048      * mount inode for the partition. If this inode were deleted, it would crash
3049      * the machine.
3050      */
3051     if (vnodeEssence->InodeNumber == 0) {
3052         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"));
3053         if (!Testing) {
3054             CopyOnWrite(salvinfo, dir);
3055             opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
3056         }
3057         return 0;
3058     }
3059 #endif
3060 #endif
3061
3062     if (!(vnodeNumber & 1) && !Showmode
3063         && !(vnodeEssence->count || vnodeEssence->unique
3064              || vnodeEssence->modeBits)) {
3065         Log("dir vnode %u: invalid entry: %s" OS_DIRSEP "%s (vnode %u, unique %u)%s\n",
3066             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
3067             vnodeNumber, unique,
3068             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
3069              ""));
3070         if (!unique) {
3071             if (!Testing) {
3072                 CopyOnWrite(salvinfo, dir);
3073                 opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
3074             }
3075             return 0;
3076         }
3077     }
3078
3079     /* Check if the Uniquifiers match. If not, change the directory entry
3080      * so its unique matches the vnode unique. Delete if the unique is zero
3081      * or if the directory is orphaned.
3082      */
3083     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
3084         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
3085
3086         if (todelete
3087             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
3088                 if (dirOrphaned) {
3089                     /* This is an orphaned directory. Don't delete the . or ..
3090                      * entry. Otherwise, it will get created in the next
3091                      * salvage and deleted again here. So Just skip it.
3092                      * */
3093                     return 0;
3094                 }
3095                 /* (vnodeEssence->unique == 0 && ('.' || '..'));
3096                  * Entries arriving here should be deleted, but the directory
3097                  * is not orphaned. Therefore, the entry must be pointing at
3098                  * the wrong vnode.  Skip the 'else' clause and fall through;
3099                  * the code below will repair the entry so it correctly points
3100                  * at the vnode of the current directory (if '.') or the parent
3101                  * directory (if '..'). */
3102         } else {
3103             if (!Showmode) {
3104                 Log("dir vnode %u: %s" OS_DIRSEP "%s (vnode %u): unique changed from %u to %u %s\n",
3105                     dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique,
3106                     vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
3107             }
3108             if (!Testing) {
3109                 AFSFid fid;
3110                 fid.Vnode = vnodeNumber;
3111                 fid.Unique = vnodeEssence->unique;
3112                 CopyOnWrite(salvinfo, dir);
3113                 opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
3114                 if (!todelete)
3115                     opr_Verify(afs_dir_Create(&dir->dirHandle, name, &fid) == 0);
3116             }
3117             if (todelete)
3118                 return 0;               /* no need to continue */
3119         }
3120     }
3121
3122     if (strcmp(name, ".") == 0) {
3123         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
3124             if (!Showmode)
3125                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3126             if (!Testing) {
3127                 AFSFid fid;
3128                 CopyOnWrite(salvinfo, dir);
3129                 opr_Verify(afs_dir_Delete(&dir->dirHandle, ".") == 0);
3130                 fid.Vnode = dir->vnodeNumber;
3131                 fid.Unique = dir->unique;
3132                 opr_Verify(afs_dir_Create(&dir->dirHandle, ".", &fid) == 0);
3133                 vnodeNumber = fid.Vnode;        /* Get the new Essence */
3134                 unique = fid.Unique;
3135                 vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3136             }
3137         }
3138         dir->haveDot = 1;
3139     } else if (strcmp(name, "..") == 0) {
3140         AFSFid pa;
3141         if (dir->parent) {
3142             struct VnodeEssence *dotdot;
3143             pa.Vnode = dir->parent;
3144             dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
3145             opr_Assert(dotdot != NULL); /* XXX Should not be assert */
3146             pa.Unique = dotdot->unique;
3147         } else {
3148             pa.Vnode = dir->vnodeNumber;
3149             pa.Unique = dir->unique;
3150         }
3151         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
3152             if (!Showmode)
3153                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3154             if (!Testing) {
3155                 CopyOnWrite(salvinfo, dir);
3156                 opr_Verify(afs_dir_Delete(&dir->dirHandle, "..") == 0);
3157                 opr_Verify(afs_dir_Create(&dir->dirHandle, "..", &pa) == 0);
3158             }
3159
3160             vnodeNumber = pa.Vnode;     /* Get the new Essence */
3161             unique = pa.Unique;
3162             vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3163         }
3164         dir->haveDotDot = 1;
3165     } else if (strncmp(name, ".__afs", 6) == 0) {
3166         if (!Showmode) {
3167             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);
3168         }
3169         if (!Testing) {
3170             CopyOnWrite(salvinfo, dir);
3171             opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
3172         }
3173         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
3174         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
3175         return 0;
3176     } else {
3177         if (ShowSuid && (vnodeEssence->modeBits & 06000))
3178             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);
3179         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
3180             && !(vnodeEssence->modeBits & 0111)) {
3181             afs_sfsize_t nBytes;
3182             afs_sfsize_t size;
3183             char buf[1025];
3184             IHandle_t *ihP;
3185             FdHandle_t *fdP;
3186
3187             IH_INIT(ihP, salvinfo->fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3188                     vnodeEssence->InodeNumber);
3189             fdP = IH_OPEN(ihP);
3190             if (fdP == NULL) {
3191                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
3192                 IH_RELEASE(ihP);
3193                 return 0;
3194             }
3195             size = FDH_SIZE(fdP);
3196             if (size < 0) {
3197                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, (int)size, vnodeNumber);
3198                 FDH_REALLYCLOSE(fdP);
3199                 IH_RELEASE(ihP);
3200                 return 0;
3201             }
3202
3203             if (size > 1024)
3204                 size = 1024;
3205             nBytes = FDH_PREAD(fdP, buf, size, 0);
3206             if (nBytes == size) {
3207                 buf[size] = '\0';
3208                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
3209                     Log("Volume %" AFS_VOLID_FMT " (%s) mount point %s" OS_DIRSEP "%s to '%s' invalid, %s to symbolic link\n",
3210                         afs_printable_VolumeId_lu(dir->dirHandle.dirh_handle->ih_vid), dir->vname, dir->name ? dir->name : "??", name, buf,
3211                         Testing ? "would convert" : "converted");
3212                     vnodeEssence->modeBits |= 0111;
3213                     vnodeEssence->changed = 1;
3214                 } else if (ShowMounts) 
3215                     Log("In volume %" AFS_VOLID_FMT " (%s) found mountpoint %s" OS_DIRSEP "%s to '%s'\n",
3216                         afs_printable_VolumeId_lu(dir->dirHandle.dirh_handle->ih_vid),
3217                         dir->vname, dir->name ? dir->name : "??", name, buf);
3218             } else {
3219                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3220                     dir->vname, vnodeNumber, (int)size, (int)nBytes);
3221             }
3222             FDH_REALLYCLOSE(fdP);
3223             IH_RELEASE(ihP);
3224         }
3225         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3226             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);
3227         if (vnodeIdToClass(vnodeNumber) == vLarge
3228             && vnodeEssence->name == NULL) {
3229             vnodeEssence->name = strdup(name);
3230         }
3231
3232         /* The directory entry points to the vnode. Check to see if the
3233          * vnode points back to the directory. If not, then let the
3234          * directory claim it (else it might end up orphaned). Vnodes
3235          * already claimed by another directory are deleted from this
3236          * directory: hardlinks to the same vnode are not allowed
3237          * from different directories.
3238          */
3239         if (vnodeEssence->parent != dir->vnodeNumber) {
3240             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3241                 /* Vnode does not point back to this directory.
3242                  * Orphaned dirs cannot claim a file (it may belong to
3243                  * another non-orphaned dir).
3244                  */
3245                 if (!Showmode) {
3246                     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);
3247                 }
3248                 vnodeEssence->parent = dir->vnodeNumber;
3249                 vnodeEssence->changed = 1;
3250             } else {
3251                 /* Vnode was claimed by another directory */
3252                 if (!Showmode) {
3253                     if (dirOrphaned) {
3254                         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 " : ""));
3255                     } else if (vnodeNumber == 1) {
3256                         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 " : ""));
3257                     } else {
3258                         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 " : ""));
3259                     }
3260                 }
3261                 if (!Testing) {
3262                     CopyOnWrite(salvinfo, dir);
3263                     opr_Verify(afs_dir_Delete(&dir->dirHandle, name) == 0);
3264                 }
3265                 return 0;
3266             }
3267         }
3268         /* This directory claims the vnode */
3269         vnodeEssence->claimed = 1;
3270     }
3271     vnodeEssence->count--;
3272     return 0;
3273 }
3274
3275 void
3276 DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
3277                    VnodeClass class, Inode ino, Unique * maxu)
3278 {
3279     struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
3280     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3281     char buf[SIZEOF_LARGEDISKVNODE];
3282     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3283     afs_sfsize_t size;
3284     StreamHandle_t *file;
3285     int vnodeIndex;
3286     int nVnodes;
3287     FdHandle_t *fdP;
3288
3289     IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
3290     fdP = IH_OPEN(vip->handle);
3291     opr_Assert(fdP != NULL);
3292     file = FDH_FDOPEN(fdP, "r+");
3293     opr_Assert(file != NULL);
3294     size = OS_SIZE(fdP->fd_fd);
3295     opr_Assert(size != -1);
3296     vip->nVnodes = (size / vcp->diskSize) - 1;
3297     if (vip->nVnodes > 0) {
3298         opr_Assert((vip->nVnodes + 1) * vcp->diskSize == size);
3299         opr_Verify(STREAM_ASEEK(file, vcp->diskSize) == 0);
3300         opr_Verify((vip->vnodes = calloc(vip->nVnodes,
3301                                          sizeof(struct VnodeEssence)))
3302                         != NULL);
3303         if (class == vLarge) {
3304             opr_Verify((vip->inodes = calloc(vip->nVnodes, sizeof(Inode)))
3305                             != NULL);
3306         } else {
3307             vip->inodes = NULL;
3308         }
3309     } else {
3310         vip->nVnodes = 0;
3311         vip->vnodes = NULL;
3312         vip->inodes = NULL;
3313     }
3314     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3315     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3316          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3317          nVnodes--, vnodeIndex++) {
3318         if (vnode->type != vNull) {
3319             struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3320             afs_fsize_t vnodeLength;
3321             vip->nAllocatedVnodes++;
3322             vep->count = vnode->linkCount;
3323             VNDISK_GET_LEN(vnodeLength, vnode);
3324             vep->blockCount = nBlocks(vnodeLength);
3325             vip->volumeBlockCount += vep->blockCount;
3326             vep->parent = vnode->parent;
3327             vep->unique = vnode->uniquifier;
3328             if (*maxu < vnode->uniquifier)
3329                 *maxu = vnode->uniquifier;
3330             vep->modeBits = vnode->modeBits;
3331             vep->InodeNumber = VNDISK_GET_INO(vnode);
3332             vep->type = vnode->type;
3333             vep->author = vnode->author;
3334             vep->owner = vnode->owner;
3335             vep->group = vnode->group;
3336             if (vnode->type == vDirectory) {
3337                 if (class != vLarge) {
3338                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3339                     vip->nAllocatedVnodes--;
3340                     memset(vnode, 0, sizeof(*vnode));
3341                     IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3342                               vnodeIndexOffset(vcp, vnodeNumber),
3343                               (char *)&vnode, sizeof(vnode));
3344                     salvinfo->VolumeChanged = 1;
3345                 } else
3346                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3347             }
3348         }
3349     }
3350     STREAM_CLOSE(file);
3351     FDH_CLOSE(fdP);
3352 }
3353
3354 static char *
3355 GetDirName(struct SalvInfo *salvinfo, VnodeId vnode, struct VnodeEssence *vp,
3356            char *path)
3357 {
3358     struct VnodeEssence *parentvp;
3359
3360     if (vnode == 1) {
3361         strcpy(path, ".");
3362         return path;
3363     }
3364     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(salvinfo, vp->parent))
3365         && GetDirName(salvinfo, vp->parent, parentvp, path)) {
3366         strcat(path, OS_DIRSEP);
3367         strcat(path, vp->name);
3368         return path;
3369     }
3370     return 0;
3371 }
3372
3373 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3374  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3375  */
3376 static int
3377 IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode)
3378 {
3379     struct VnodeEssence *vep;
3380
3381     if (vnode == 0)
3382         return (1);             /* Vnode zero does not exist */
3383     if (vnode == 1)
3384         return (0);             /* The root dir vnode is always claimed */
3385     vep = CheckVnodeNumber(salvinfo, vnode);    /* Get the vnode essence */
3386     if (!vep || !vep->claimed)
3387         return (1);             /* Vnode is not claimed - it is orphaned */
3388
3389     return (IsVnodeOrphaned(salvinfo, vep->parent));
3390 }
3391
3392 void
3393 SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
3394            struct VnodeInfo *dirVnodeInfo, IHandle_t * alinkH, int i,
3395            struct DirSummary *rootdir, int *rootdirfound)
3396 {
3397     static struct DirSummary dir;
3398     static struct DirHandle dirHandle;
3399     struct VnodeEssence *parent;
3400     static char path[MAXPATHLEN];
3401     int dirok, code;
3402
3403     if (dirVnodeInfo->vnodes[i].salvaged)
3404         return;                 /* already salvaged */
3405
3406     dir.rwVid = rwVid;
3407     dirVnodeInfo->vnodes[i].salvaged = 1;
3408
3409     if (dirVnodeInfo->inodes[i] == 0)
3410         return;                 /* Not allocated to a directory */
3411
3412     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3413         if (dirVnodeInfo->vnodes[i].parent) {
3414             Log("Bad parent, vnode 1; %s...\n",
3415                 (Testing ? "skipping" : "salvaging"));
3416             dirVnodeInfo->vnodes[i].parent = 0;
3417             dirVnodeInfo->vnodes[i].changed = 1;
3418         }
3419     } else {
3420         parent = CheckVnodeNumber(salvinfo, dirVnodeInfo->vnodes[i].parent);
3421         if (parent && parent->salvaged == 0)
3422             SalvageDir(salvinfo, name, rwVid, dirVnodeInfo, alinkH,
3423                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3424                        rootdir, rootdirfound);
3425     }
3426
3427     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3428     dir.unique = dirVnodeInfo->vnodes[i].unique;
3429     dir.copied = 0;
3430     dir.vname = name;
3431     dir.parent = dirVnodeInfo->vnodes[i].parent;
3432     dir.haveDot = dir.haveDotDot = 0;
3433     dir.ds_linkH = alinkH;
3434     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, salvinfo->fileSysDevice,
3435                         dirVnodeInfo->inodes[i], &salvinfo->VolumeChanged);
3436
3437     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3438     if (!dirok) {
3439         if (!RebuildDirs) {
3440             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3441                 (Testing ? "skipping" : "salvaging"));
3442         }
3443         if (!Testing) {
3444             CopyAndSalvage(salvinfo, &dir);
3445             dirok = 1;
3446             dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3447         }
3448     }
3449     dirHandle = dir.dirHandle;
3450
3451     dir.name =
3452         GetDirName(salvinfo, bitNumberToVnodeNumber(i, vLarge),
3453                    &dirVnodeInfo->vnodes[i], path);
3454
3455     if (dirok) {
3456         /* If enumeration failed for random reasons, we will probably delete
3457          * too much stuff, so we guard against this instead.
3458          */
3459         struct judgeEntry_params judge_params;
3460         judge_params.salvinfo = salvinfo;
3461         judge_params.dir = &dir;
3462
3463         opr_Verify(afs_dir_EnumerateDir(&dirHandle, JudgeEntry,
3464                                         &judge_params) == 0);
3465     }
3466
3467     /* Delete the old directory if it was copied in order to salvage.
3468      * CopyOnWrite has written the new inode # to the disk, but we still
3469      * have the old one in our local structure here.  Thus, we idec the
3470      * local dude.
3471      */
3472     DFlush();
3473     if (dir.copied && !Testing) {
3474         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3475         opr_Assert(code == 0);
3476         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3477     }
3478
3479     /* Remember rootdir DirSummary _after_ it has been judged */
3480     if (dir.vnodeNumber == 1 && dir.unique == 1) {
3481         memcpy(rootdir, &dir, sizeof(struct DirSummary));
3482         *rootdirfound = 1;
3483     }
3484
3485     return;
3486 }
3487
3488 /**
3489  * Get a new FID that can be used to create a new file.
3490  *
3491  * @param[in] volHeader vol header for the volume
3492  * @param[in] class     what type of vnode we'll be creating (vLarge or vSmall)
3493  * @param[out] afid     the FID that we can use (only Vnode and Unique are set)
3494  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3495  *                          updated to the new max unique if we create a new
3496  *                          vnode
3497  */
3498 static void
3499 GetNewFID(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3500           VnodeClass class, AFSFid *afid, Unique *maxunique)
3501 {
3502     int i;
3503     for (i = 0; i < salvinfo->vnodeInfo[class].nVnodes; i++) {
3504         if (salvinfo->vnodeInfo[class].vnodes[i].type == vNull) {
3505             break;
3506         }
3507     }
3508     if (i == salvinfo->vnodeInfo[class].nVnodes) {
3509         /* no free vnodes; make a new one */
3510         salvinfo->vnodeInfo[class].nVnodes++;
3511         salvinfo->vnodeInfo[class].vnodes =
3512             realloc(salvinfo->vnodeInfo[class].vnodes,
3513                     sizeof(struct VnodeEssence) * (i+1));
3514
3515         salvinfo->vnodeInfo[class].vnodes[i].type = vNull;
3516     }
3517
3518     afid->Vnode = bitNumberToVnodeNumber(i, class);
3519
3520     if (volHeader->uniquifier < (*maxunique + 1)) {
3521         /* header uniq is bad; it will get bumped by 2000 later */
3522         afid->Unique = *maxunique + 1 + 2000;
3523         (*maxunique)++;
3524     } else {
3525         /* header uniq seems okay; just use that */
3526         afid->Unique = *maxunique = volHeader->uniquifier++;
3527     }
3528 }
3529
3530 /**
3531  * Create a vnode for a README file explaining not to use a recreated-root vol.
3532  *
3533  * @param[in] volHeader vol header for the volume
3534  * @param[in] alinkH    ihandle for i/o for the volume
3535  * @param[in] vid       volume id
3536  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3537  *                          updated to the new max unique if we create a new
3538  *                          vnode
3539  * @param[out] afid     FID for the new readme vnode
3540  * @param[out] ainode   the inode for the new readme file
3541  *
3542  * @return operation status
3543  *  @retval 0 success
3544  *  @retval -1 error
3545  */
3546 static int
3547 CreateReadme(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3548              IHandle_t *alinkH, VolumeId vid, Unique *maxunique, AFSFid *afid,
3549              Inode *ainode)
3550 {
3551     Inode readmeinode;
3552     struct VnodeDiskObject *rvnode = NULL;
3553     afs_sfsize_t bytes;
3554     IHandle_t *readmeH = NULL;
3555     struct VnodeEssence *vep;
3556     afs_fsize_t length;
3557     time_t now = time(NULL);
3558
3559     /* Try to make the note brief, but informative. Only administrators should
3560      * be able to read this file at first, so we can hopefully assume they
3561      * know what AFS is, what a volume is, etc. */
3562     char readme[] =
3563 "This volume has been salvaged, but has lost its original root directory.\n"
3564 "The root directory that exists now has been recreated from orphan files\n"
3565 "from the rest of the volume. This recreated root directory may interfere\n"
3566 "with old cached data on clients, and there is no way the salvager can\n"
3567 "reasonably prevent that. So, it is recommended that you do not continue to\n"
3568 "use this volume, but only copy the salvaged data to a new volume.\n"
3569 "Continuing to use this volume as it exists now may cause some clients to\n"
3570 "behave oddly when accessing this volume.\n"
3571 "\n\t -- Your friendly neighborhood OpenAFS salvager\n";
3572     /* ^ the person reading this probably just lost some data, so they could
3573      * use some cheering up. */
3574
3575     /* -1 for the trailing NUL */
3576     length = sizeof(readme) - 1;
3577
3578     GetNewFID(salvinfo, volHeader, vSmall, afid, maxunique);
3579
3580     vep = &salvinfo->vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
3581
3582     /* create the inode and write the contents */
3583     readmeinode = IH_CREATE(alinkH, salvinfo->fileSysDevice,
3584                             salvinfo->fileSysPath, 0, vid,
3585                             afid->Vnode, afid->Unique, 1);
3586     if (!VALID_INO(readmeinode)) {
3587         Log("CreateReadme: readme IH_CREATE failed\n");
3588         goto error;
3589     }
3590
3591     IH_INIT(readmeH, salvinfo->fileSysDevice, vid, readmeinode);
3592     bytes = IH_IWRITE(readmeH, 0, readme, length);
3593     IH_RELEASE(readmeH);
3594
3595     if (bytes != length) {
3596         Log("CreateReadme: IWRITE failed (%d/%d)\n", (int)bytes,
3597             (int)sizeof(readme));
3598         goto error;
3599     }
3600
3601     /* create the vnode and write it out */
3602     rvnode = calloc(1, SIZEOF_SMALLDISKVNODE);
3603     if (!rvnode) {
3604         Log("CreateRootDir: error alloc'ing memory\n");
3605         goto error;
3606     }
3607
3608     rvnode->type = vFile;
3609     rvnode->cloned = 0;
3610     rvnode->modeBits = 0777;
3611     rvnode->linkCount = 1;
3612     VNDISK_SET_LEN(rvnode, length);
3613     rvnode->uniquifier = afid->Unique;
3614     rvnode->dataVersion = 1;
3615     VNDISK_SET_INO(rvnode, readmeinode);
3616     rvnode->unixModifyTime = rvnode->serverModifyTime = now;
3617     rvnode->author = 0;
3618     rvnode->owner = 0;
3619     rvnode->parent = 1;
3620     rvnode->group = 0;
3621     rvnode->vnodeMagic = VnodeClassInfo[vSmall].magic;
3622
3623     bytes = IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3624                       vnodeIndexOffset(&VnodeClassInfo[vSmall], afid->Vnode),
3625                       (char*)rvnode, SIZEOF_SMALLDISKVNODE);
3626
3627     if (bytes != SIZEOF_SMALLDISKVNODE) {
3628         Log("CreateReadme: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3629             (int)SIZEOF_SMALLDISKVNODE);
3630         goto error;
3631     }
3632
3633     /* update VnodeEssence for new readme vnode */
3634     salvinfo->vnodeInfo[vSmall].nAllocatedVnodes++;
3635     vep->count = 0;
3636     vep->blockCount = nBlocks(length);
3637     salvinfo->vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
3638     vep->parent = rvnode->parent;
3639     vep->unique = rvnode->uniquifier;
3640     vep->modeBits = rvnode->modeBits;
3641     vep->InodeNumber = VNDISK_GET_INO(rvnode);
3642     vep->type = rvnode->type;
3643     vep->author = rvnode->author;
3644     vep->owner = rvnode->owner;
3645     vep->group = rvnode->group;
3646
3647     free(rvnode);
3648     rvnode = NULL;
3649
3650     vep->claimed = 1;
3651     vep->changed = 0;
3652     vep->salvaged = 1;
3653     vep->todelete = 0;
3654
3655     *ainode = readmeinode;
3656
3657     return 0;
3658
3659  error:
3660     if (IH_DEC(alinkH, readmeinode, vid)) {
3661         Log("CreateReadme (recovery): IH_DEC failed\n");
3662     }
3663
3664     if (rvnode) {
3665         free(rvnode);
3666         rvnode = NULL;
3667     }
3668
3669     return -1;
3670 }
3671
3672 /**
3673  * create a root dir for a volume that lacks one.
3674  *
3675  * @param[in] volHeader vol header for the volume
3676  * @param[in] alinkH    ihandle for disk access for this volume group
3677  * @param[in] vid       volume id we're dealing with
3678  * @param[out] rootdir  populated with info about the new root dir
3679  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3680  *                          updated to the new max unique if we create a new
3681  *                          vnode
3682  *
3683  * @return operation status
3684  *  @retval 0  success
3685  *  @retval -1 error
3686  */
3687 static int
3688 CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3689               IHandle_t *alinkH, VolumeId vid, struct DirSummary *rootdir,
3690               Unique *maxunique)
3691 {
3692     FileVersion dv;
3693     int decroot = 0, decreadme = 0;
3694     AFSFid did, readmeid;
3695     afs_fsize_t length;
3696     Inode rootinode;
3697     struct VnodeDiskObject *rootvnode = NULL;
3698     struct acl_accessList *ACL;
3699     Inode *ip;
3700     afs_sfsize_t bytes;
3701     struct VnodeEssence *vep;
3702     Inode readmeinode = 0;
3703     time_t now = time(NULL);
3704
3705     if (!salvinfo->vnodeInfo[vLarge].vnodes && !salvinfo->vnodeInfo[vSmall].vnodes) {
3706         Log("Not creating new root dir; volume appears to lack any vnodes\n");
3707         goto error;
3708     }
3709
3710     if (!salvinfo->vnodeInfo[vLarge].vnodes) {
3711         /* We don't have any large vnodes in the volume; allocate room
3712          * for one so we can recreate the root dir */
3713         salvinfo->vnodeInfo[vLarge].nVnodes = 1;
3714         salvinfo->vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
3715         salvinfo->vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
3716
3717         opr_Assert(salvinfo->vnodeInfo[vLarge].vnodes);
3718         opr_Assert(salvinfo->vnodeInfo[vLarge].inodes);
3719     }
3720
3721     vep = &salvinfo->vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
3722     ip = &salvinfo->vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
3723     if (vep->type != vNull) {
3724         Log("Not creating new root dir; existing vnode 1 is non-null\n");
3725         goto error;
3726     }
3727
3728     if (CreateReadme(salvinfo, volHeader, alinkH, vid, maxunique, &readmeid,
3729                      &readmeinode) != 0) {
3730         goto error;
3731     }
3732     decreadme = 1;
3733
3734     /* set the DV to a very high number, so it is unlikely that we collide
3735      * with a cached DV */
3736     dv = 1 << 30;
3737
3738     rootinode = IH_CREATE(alinkH, salvinfo->fileSysDevice, salvinfo->fileSysPath,
3739                           0, vid, 1, 1, dv);
3740     if (!VALID_INO(rootinode)) {
3741         Log("CreateRootDir: IH_CREATE failed\n");
3742         goto error;
3743     }
3744     decroot = 1;
3745
3746     SetSalvageDirHandle(&rootdir->dirHandle, vid, salvinfo->fileSysDevice,
3747                         rootinode, &salvinfo->VolumeChanged);
3748     did.Volume = vid;
3749     did.Vnode = 1;
3750     did.Unique = 1;
3751     if (afs_dir_MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
3752         Log("CreateRootDir: MakeDir failed\n");
3753         goto error;
3754     }
3755     if (afs_dir_Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
3756         Log("CreateRootDir: Create failed\n");
3757         goto error;
3758     }
3759     DFlush();
3760     length = afs_dir_Length(&rootdir->dirHandle);
3761     DZap(&rootdir->dirHandle);
3762
3763     /* create the new root dir vnode */
3764     rootvnode = calloc(1, SIZEOF_LARGEDISKVNODE);
3765     if (!rootvnode) {
3766         Log("CreateRootDir: malloc failed\n");
3767         goto error;
3768     }
3769
3770     /* only give 'rl' permissions to 'system:administrators'. We do this to
3771      * try to catch the attention of an administrator, that they should not
3772      * be writing to this directory or continue to use it. */
3773     ACL = VVnodeDiskACL(rootvnode);
3774     ACL->size = sizeof(struct acl_accessList);
3775     ACL->version = ACL_ACLVERSION;
3776     ACL->total = 1;
3777     ACL->positive = 1;
3778     ACL->negative = 0;
3779     ACL->entries[0].id = -204; /* system:administrators */
3780     ACL->entries[0].rights = PRSFS_READ | PRSFS_LOOKUP;
3781
3782     rootvnode->type = vDirectory;
3783     rootvnode->cloned = 0;
3784     rootvnode->modeBits = 0777;
3785     rootvnode->linkCount = 2;
3786     VNDISK_SET_LEN(rootvnode, length);
3787     rootvnode->uniquifier = 1;
3788     rootvnode->dataVersion = dv;
3789     VNDISK_SET_INO(rootvnode, rootinode);
3790     rootvnode->unixModifyTime = rootvnode->serverModifyTime = now;
3791     rootvnode->author = 0;
3792     rootvnode->owner = 0;
3793     rootvnode->parent = 0;
3794     rootvnode->group = 0;
3795     rootvnode->vnodeMagic = VnodeClassInfo[vLarge].magic;
3796
3797     /* write it out to disk */
3798     bytes = IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
3799               vnodeIndexOffset(&VnodeClassInfo[vLarge], 1),
3800               (char*)rootvnode, SIZEOF_LARGEDISKVNODE);
3801
3802     if (bytes != SIZEOF_LARGEDISKVNODE) {
3803         /* just cast to int and don't worry about printing real 64-bit ints;
3804          * a large disk vnode isn't anywhere near the 32-bit limit */
3805         Log("CreateRootDir: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3806             (int)SIZEOF_LARGEDISKVNODE);
3807         goto error;
3808     }
3809
3810     /* update VnodeEssence for the new root vnode */
3811     salvinfo->vnodeInfo[vLarge].nAllocatedVnodes++;
3812     vep->count = 0;
3813     vep->blockCount = nBlocks(length);
3814     salvinfo->vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
3815     vep->parent = rootvnode->parent;
3816     vep->unique = rootvnode->uniquifier;
3817     vep->modeBits = rootvnode->modeBits;
3818     vep->InodeNumber = VNDISK_GET_INO(rootvnode);
3819     vep->type = rootvnode->type;
3820     vep->author = rootvnode->author;
3821     vep->owner = rootvnode->owner;
3822     vep->group = rootvnode->group;
3823
3824     free(rootvnode);
3825     rootvnode = NULL;
3826
3827     vep->claimed = 0;
3828     vep->changed = 0;
3829     vep->salvaged = 1;
3830     vep->todelete = 0;
3831
3832     /* update DirSummary for the new root vnode */
3833     rootdir->vnodeNumber = 1;
3834     rootdir->unique = 1;
3835     rootdir->haveDot = 1;
3836     rootdir->haveDotDot = 1;
3837     rootdir->rwVid = vid;
3838     rootdir->copied = 0;
3839     rootdir->parent = 0;
3840     rootdir->name = strdup(".");
3841     rootdir->vname = volHeader->name;
3842     rootdir->ds_linkH = alinkH;
3843
3844     *ip = rootinode;
3845
3846     return 0;
3847
3848  error:
3849     if (decroot && IH_DEC(alinkH, rootinode, vid)) {
3850         Log("CreateRootDir (recovery): IH_DEC (root) failed\n");
3851     }
3852     if (decreadme && IH_DEC(alinkH, readmeinode, vid)) {
3853         Log("CreateRootDir (recovery): IH_DEC (readme) failed\n");
3854     }
3855     if (rootvnode) {
3856         free(rootvnode);
3857         rootvnode = NULL;
3858     }
3859     return -1;
3860 }
3861
3862 /**
3863  * salvage a volume group.
3864  *
3865  * @param[in] salvinfo information for the curent salvage job
3866  * @param[in] rwIsp    inode summary for rw volume
3867  * @param[in] alinkH   link table inode handle
3868  *
3869  * @return operation status
3870  *   @retval 0 success
3871  */
3872 int
3873 SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t * alinkH)
3874 {
3875     /* This routine, for now, will only be called for read-write volumes */
3876     int i, j, code;
3877     int BlocksInVolume = 0, FilesInVolume = 0;
3878     VnodeClass class;
3879     struct DirSummary rootdir, oldrootdir;
3880     struct VnodeInfo *dirVnodeInfo;
3881     struct VnodeDiskObject vnode;
3882     VolumeDiskData volHeader;
3883     VolumeId vid;
3884     int orphaned, rootdirfound = 0;
3885     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
3886     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
3887     struct VnodeEssence *vep;
3888     afs_int32 v, pv;
3889     IHandle_t *h;
3890     afs_sfsize_t nBytes;
3891     AFSFid pa;
3892     VnodeId LFVnode, ThisVnode;
3893     Unique LFUnique, ThisUnique;
3894     char npath[128];
3895     int newrootdir = 0;
3896
3897     vid = rwIsp->volSummary->header.id;
3898     IH_INIT(h, salvinfo->fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3899     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3900     opr_Assert(nBytes == sizeof(volHeader));
3901     opr_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3902     opr_Assert(volHeader.destroyMe != DESTROY_ME);
3903     /* (should not have gotten this far with DESTROY_ME flag still set!) */
3904
3905     DistilVnodeEssence(salvinfo, vid, vLarge,
3906                        rwIsp->volSummary->header.largeVnodeIndex, &maxunique);
3907     DistilVnodeEssence(salvinfo, vid, vSmall,
3908                        rwIsp->volSummary->header.smallVnodeIndex, &maxunique);
3909
3910     dirVnodeInfo = &salvinfo->vnodeInfo[vLarge];
3911     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3912         SalvageDir(salvinfo, volHeader.name, vid, dirVnodeInfo, alinkH, i,
3913                    &rootdir, &rootdirfound);
3914     }
3915 #ifdef AFS_NT40_ENV
3916     nt_sync(salvinfo->fileSysDevice);
3917 #else
3918     sync();                             /* This used to be done lower level, for every dir */
3919 #endif
3920     if (Showmode) {
3921         IH_RELEASE(h);
3922         return 0;
3923     }
3924
3925     if (!rootdirfound && (orphans == ORPH_ATTACH) && !Testing) {
3926
3927         Log("Cannot find root directory for volume %lu; attempting to create "
3928             "a new one\n", afs_printable_uint32_lu(vid));
3929
3930         code = CreateRootDir(salvinfo, &volHeader, alinkH, vid, &rootdir,
3931                              &maxunique);
3932         if (code == 0) {
3933             rootdirfound = 1;
3934             newrootdir = 1;
3935             salvinfo->VolumeChanged = 1;
3936         }
3937     }
3938
3939     /* Parse each vnode looking for orphaned vnodes and
3940      * connect them to the tree as orphaned (if requested).
3941      */
3942     oldrootdir = rootdir;
3943     for (class = 0; class < nVNODECLASSES; class++) {
3944         for (v = 0; v < salvinfo->vnodeInfo[class].nVnodes; v++) {
3945             vep = &(salvinfo->vnodeInfo[class].vnodes[v]);
3946             ThisVnode = bitNumberToVnodeNumber(v, class);
3947             ThisUnique = vep->unique;
3948
3949             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3950                 continue;       /* Ignore unused, claimed, and root vnodes */
3951
3952             /* This vnode is orphaned. If it is a directory vnode, then the '..'
3953              * entry in this vnode had incremented the parent link count (In
3954              * JudgeEntry()). We need to go to the parent and decrement that
3955              * link count. But if the parent's unique is zero, then the parent
3956              * link count was not incremented in JudgeEntry().
3957              */
3958             if (class == vLarge) {      /* directory vnode */
3959                 pv = vnodeIdToBitNumber(vep->parent);
3960                 if (salvinfo->vnodeInfo[vLarge].vnodes[pv].unique != 0) {
3961                     if (vep->parent == 1 && newrootdir) {
3962                         /* this vnode's parent was the volume root, and
3963                          * we just created the volume root. So, the parent
3964                          * dir didn't exist during JudgeEntry, so the link
3965                          * count was not inc'd there, so don't dec it here.
3966                          */
3967
3968                          /* noop */
3969
3970                     } else {
3971                         salvinfo->vnodeInfo[vLarge].vnodes[pv].count++;
3972                     }
3973                 }
3974             }
3975
3976             if (!rootdirfound)
3977                 continue;       /* If no rootdir, can't attach orphaned files */
3978
3979             /* Here we attach orphaned files and directories into the
3980              * root directory, LVVnode, making sure link counts stay correct.
3981              */
3982             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3983                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
3984                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
3985
3986                 /* Update this orphaned vnode's info. Its parent info and
3987                  * link count (do for orphaned directories and files).
3988                  */
3989                 vep->parent = LFVnode;  /* Parent is the root dir */
3990                 vep->unique = LFUnique;
3991                 vep->changed = 1;
3992                 vep->claimed = 1;
3993                 vep->count--;   /* Inc link count (root dir will pt to it) */
3994
3995                 /* If this orphaned vnode is a directory, change '..'.
3996                  * The name of the orphaned dir/file is unknown, so we
3997                  * build a unique name. No need to CopyOnWrite the directory
3998                  * since it is not connected to tree in BK or RO volume and
3999                  * won't be visible there.
4000                  */
4001                 if (class == vLarge) {
4002                     AFSFid pa;
4003                     DirHandle dh;
4004
4005                     /* Remove and recreate the ".." entry in this orphaned directory */
4006                     SetSalvageDirHandle(&dh, vid, salvinfo->fileSysDevice,
4007                                         salvinfo->vnodeInfo[class].inodes[v],
4008                                         &salvinfo->VolumeChanged);
4009                     pa.Vnode = LFVnode;
4010                     pa.Unique = LFUnique;
4011                     opr_Verify(afs_dir_Delete(&dh, "..") == 0);
4012                     opr_Verify(afs_dir_Create(&dh, "..", &pa) == 0);
4013
4014                     /* The original parent's link count was decremented above.
4015                      * Here we increment the new parent's link count.
4016                      */
4017                     pv = vnodeIdToBitNumber(LFVnode);
4018                     salvinfo->vnodeInfo[vLarge].vnodes[pv].count--;
4019
4020                 }
4021
4022                 /* Go to the root dir and add this entry. The link count of the
4023                  * root dir was incremented when ".." was created. Try 10 times.
4024                  */
4025                 for (j = 0; j < 10; j++) {
4026                     pa.Vnode = ThisVnode;
4027                     pa.Unique = ThisUnique;
4028
4029                     snprintf(npath, sizeof npath, "%s.%u.%u",
4030                              ((class == vLarge) ? "__ORPHANDIR__"
4031                                                 : "__ORPHANFILE__"),
4032                              ThisVnode, ThisUnique);
4033
4034                     CopyOnWrite(salvinfo, &rootdir);
4035                     code = afs_dir_Create(&rootdir.dirHandle, npath, &pa);
4036                     if (!code)
4037                         break;
4038
4039                     ThisUnique += 50;   /* Try creating a different file */
4040                 }
4041                 opr_Assert(code == 0);
4042                 Log("Attaching orphaned %s to volume's root dir as %s\n",
4043                     ((class == vLarge) ? "directory" : "file"), npath);
4044             }
4045         }                       /* for each vnode in the class */
4046     }                           /* for each class of vnode */
4047
4048     /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
4049     DFlush();
4050     if (rootdirfound && !oldrootdir.copied && rootdir.copied) {
4051         code =
4052             IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
4053                    oldrootdir.rwVid);
4054         opr_Assert(code == 0);
4055         /* dirVnodeInfo->inodes[?] is not updated with new inode number */
4056     }
4057
4058     DFlush();                   /* Flush the changes */
4059     if (!rootdirfound && (orphans == ORPH_ATTACH)) {
4060         Log("Cannot attach orphaned files and directories: Root directory not found\n");
4061         orphans = ORPH_IGNORE;
4062     }
4063
4064     /* Write out all changed vnodes. Orphaned files and directories
4065      * will get removed here also (if requested).
4066      */
4067     for (class = 0; class < nVNODECLASSES; class++) {
4068         afs_sfsize_t nVnodes = salvinfo->vnodeInfo[class].nVnodes;
4069         struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
4070         struct VnodeEssence *vnodes = salvinfo->vnodeInfo[class].vnodes;
4071         FilesInVolume += salvinfo->vnodeInfo[class].nAllocatedVnodes;
4072         BlocksInVolume += salvinfo->vnodeInfo[class].volumeBlockCount;
4073         for (i = 0; i < nVnodes; i++) {
4074             struct VnodeEssence *vnp = &vnodes[i];
4075             VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
4076
4077             /* If the vnode is good but is unclaimed (not listed in
4078              * any directory entries), then it is orphaned.
4079              */
4080             orphaned = -1;
4081             if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber))) {
4082                 vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
4083                 vnp->changed = 1;
4084             }
4085
4086             if (vnp->changed || vnp->count) {
4087                 int oldCount;
4088                 nBytes =
4089                     IH_IREAD(salvinfo->vnodeInfo[class].handle,
4090                              vnodeIndexOffset(vcp, vnodeNumber),
4091                              (char *)&vnode, sizeof(vnode));
4092                 opr_Assert(nBytes == sizeof(vnode));
4093
4094                 vnode.parent = vnp->parent;
4095                 oldCount = vnode.linkCount;
4096                 vnode.linkCount = vnode.linkCount - vnp->count;
4097
4098                 if (orphaned == -1)
4099                     orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber);
4100                 if (orphaned) {
4101                     if (!vnp->todelete) {
4102                         /* Orphans should have already been attached (if requested) */
4103                         opr_Assert(orphans != ORPH_ATTACH);
4104                         oblocks += vnp->blockCount;
4105                         ofiles++;
4106                     }
4107                     if (((orphans == ORPH_REMOVE) || vnp->todelete)
4108                         && !Testing) {
4109                         BlocksInVolume -= vnp->blockCount;
4110                         FilesInVolume--;
4111                         if (VNDISK_GET_INO(&vnode)) {
4112                             code =
4113                                 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
4114                             opr_Assert(code == 0);
4115                         }
4116                         memset(&vnode, 0, sizeof(vnode));
4117                     }
4118                 } else if (vnp->count) {
4119                     if (!Showmode) {
4120                         Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
4121                     }
4122                 } else {
4123                     vnode.modeBits = vnp->modeBits;
4124                 }
4125
4126                 vnode.dataVersion++;
4127                 if (!Testing) {
4128                     nBytes =
4129                         IH_IWRITE(salvinfo->vnodeInfo[class].handle,
4130                                   vnodeIndexOffset(vcp, vnodeNumber),
4131                                   (char *)&vnode, sizeof(vnode));
4132                     opr_Assert(nBytes == sizeof(vnode));
4133                 }
4134                 salvinfo->VolumeChanged = 1;
4135             }
4136         }
4137     }
4138     if (!Showmode && ofiles) {
4139         Log("%s %d orphaned files and directories (approx. %u KB)\n",
4140             (!Testing
4141              && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
4142             oblocks);
4143     }
4144
4145     for (class = 0; class < nVNODECLASSES; class++) {
4146         struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
4147         for (i = 0; i < vip->nVnodes; i++)
4148             if (vip->vnodes[i].name)
4149                 free(vip->vnodes[i].name);
4150         if (vip->vnodes)
4151             free(vip->vnodes);
4152         if (vip->inodes)
4153             free(vip->inodes);
4154     }
4155
4156     /* Set correct resource utilization statistics */
4157     volHeader.filecount = FilesInVolume;
4158     volHeader.diskused = BlocksInVolume;
4159
4160     /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
4161     if (volHeader.uniquifier < (maxunique + 1)) {
4162         if (!Showmode)
4163             Log("Volume uniquifier %u is too low (max uniq %u); fixed\n", volHeader.uniquifier, maxunique);
4164         /* Plus 2,000 in case there are workstations out there with
4165          * cached vnodes that have since been deleted
4166          */
4167         volHeader.uniquifier = (maxunique + 1 + 2000);
4168     }
4169
4170     if (newrootdir) {
4171         Log("*** WARNING: Root directory recreated, but volume is fragile! "
4172             "Only use this salvaged volume to copy data to another volume; "
4173             "do not continue to use this volume (%lu) as-is.\n",
4174             afs_printable_uint32_lu(vid));
4175     }
4176
4177     if (!Testing && salvinfo->VolumeChanged) {
4178 #ifdef FSSYNC_BUILD_CLIENT
4179         if (salvinfo->useFSYNC) {
4180             afs_int32 fsync_code;
4181
4182             fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
4183             if (fsync_code) {
4184                 Log("Error trying to tell the fileserver to break callbacks for "
4185                     "changed volume %lu; error code %ld\n",
4186                     afs_printable_uint32_lu(vid),
4187                     afs_printable_int32_ld(fsync_code));
4188             } else {
4189                 salvinfo->VolumeChanged = 0;
4190             }
4191         }
4192 #endif /* FSSYNC_BUILD_CLIENT */
4193
4194 #ifdef AFS_DEMAND_ATTACH_FS
4195         if (!salvinfo->useFSYNC) {
4196             /* A volume's contents have changed, but the fileserver will not
4197              * break callbacks on the volume until it tries to load the vol
4198              * header. So, to reduce the amount of time a client could have
4199              * stale data, remove fsstate.dat, so the fileserver will init
4200              * callback state with all clients. This is a very coarse hammer,
4201              * and in the future we should just record which volumes have
4202              * changed. */
4203             code = unlink(AFSDIR_SERVER_FSSTATE_FILEPATH);
4204             if (code && errno != ENOENT) {
4205                 Log("Error %d when trying to unlink FS state file %s\n", errno,
4206                     AFSDIR_SERVER_FSSTATE_FILEPATH);
4207             }
4208         }
4209 #endif
4210     }
4211
4212     /* Turn off the inUse bit; the volume's been salvaged! */
4213     volHeader.inUse = 0;        /* clear flag indicating inUse@last crash */
4214     volHeader.needsSalvaged = 0;        /* clear 'damaged' flag */
4215     volHeader.inService = 1;    /* allow service again */
4216     volHeader.needsCallback = (salvinfo->VolumeChanged != 0);
4217     volHeader.dontSalvage = DONT_SALVAGE;
4218     salvinfo->VolumeChanged = 0;
4219     if (!Testing) {
4220         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4221         opr_Assert(nBytes == sizeof(volHeader));
4222     }
4223     if (!Showmode) {
4224         Log("%sSalvaged %s (%" AFS_VOLID_FMT "): %d files, %d blocks\n",
4225             (Testing ? "It would have " : ""), volHeader.name, afs_printable_VolumeId_lu(volHeader.id),
4226             FilesInVolume, BlocksInVolume);
4227     }
4228
4229     IH_RELEASE(salvinfo->vnodeInfo[vSmall].handle);
4230     IH_RELEASE(salvinfo->vnodeInfo[vLarge].handle);
4231     IH_RELEASE(h);
4232     return 0;
4233 }
4234
4235 void
4236 ClearROInUseBit(struct VolumeSummary *summary)
4237 {
4238     IHandle_t *h = summary->volumeInfoHandle;
4239     afs_sfsize_t nBytes;
4240
4241     VolumeDiskData volHeader;
4242
4243     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
4244     opr_Assert(nBytes == sizeof(volHeader));
4245     opr_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
4246     volHeader.inUse = 0;
4247     volHeader.needsSalvaged = 0;
4248     volHeader.inService = 1;
4249     volHeader.dontSalvage = DONT_SALVAGE;
4250     if (!Testing) {
4251         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4252         opr_Assert(nBytes == sizeof(volHeader));
4253     }
4254 }
4255
4256 /* MaybeZapVolume
4257  * Possible delete the volume.
4258  *
4259  * deleteMe - Always do so, only a partial volume.
4260  */
4261 void
4262 MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
4263                char *message, int deleteMe, int check)
4264 {
4265     if (readOnly(isp) || deleteMe) {
4266         if (isp->volSummary && !isp->volSummary->deleted) {
4267             if (deleteMe) {
4268                 if (!Showmode)
4269                     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));
4270                 if (!Showmode)
4271                     Log("It will be deleted on this server (you may find it elsewhere)\n");
4272             } else {
4273                 if (!Showmode)
4274                     Log("Volume %" AFS_VOLID_FMT " needs to be salvaged.  Since it is read-only, however,\n", afs_printable_VolumeId_lu(isp->volumeId));
4275                 if (!Showmode)
4276                     Log("it will be deleted instead.  It should be recloned.\n");
4277             }
4278             if (!Testing) {
4279                 afs_int32 code;
4280                 char path[64];
4281                 char filename[VMAXPATHLEN];
4282                 VolumeExternalName_r(isp->volumeId, filename, sizeof(filename));
4283                 sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
4284
4285                 code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, isp->volumeId, isp->RWvolumeId);
4286                 if (code) {
4287                     Log("Error %ld destroying volume disk header for volume %" AFS_VOLID_FMT "\n",
4288                         afs_printable_int32_ld(code),
4289                         afs_printable_VolumeId_lu(isp->volumeId));
4290                 }
4291
4292                 /* make sure we actually delete the header file; ENOENT
4293                  * is fine, since VDestroyVolumeDiskHeader probably already
4294                  * unlinked it */
4295                 if (unlink(path) && errno != ENOENT) {
4296                     Log("Unable to unlink %s (errno = %d)\n", path, errno);
4297                 }
4298                 if (salvinfo->useFSYNC) {
4299                     AskDelete(salvinfo, isp->volumeId);
4300                 }
4301                 isp->volSummary->deleted = 1;
4302             }
4303         }
4304     } else if (!check) {
4305         Log("%s salvage was unsuccessful: read-write volume %" AFS_VOLID_FMT "\n", message,
4306             afs_printable_VolumeId_lu(isp->volumeId));
4307         Abort("Salvage of volume %" AFS_VOLID_FMT " aborted\n", afs_printable_VolumeId_lu(isp->volumeId));
4308     }
4309 }
4310
4311 #ifdef AFS_DEMAND_ATTACH_FS
4312 /**
4313  * Locks a volume on disk for salvaging.
4314  *
4315  * @param[in] volumeId   volume ID to lock
4316  *
4317  * @return operation status
4318  *  @retval 0  success
4319  *  @retval -1 volume lock raced with a fileserver restart; all volumes must
4320  *             checked out and locked again
4321  *
4322  * @note DAFS only
4323  */
4324 static int
4325 LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId)
4326 {
4327     afs_int32 code;
4328     int locktype;
4329
4330     /* should always be WRITE_LOCK, but keep the lock-type logic all
4331      * in one place, in VVolLockType. Params will be ignored, but
4332      * try to provide what we're logically doing. */
4333     locktype = VVolLockType(V_VOLUPD, 1);
4334
4335     code = VLockVolumeByIdNB(volumeId, salvinfo->fileSysPartition, locktype);
4336     if (code) {
4337         if (code == EBUSY) {
4338             Abort("Someone else appears to be using volume %lu; Aborted\n",
4339                   afs_printable_uint32_lu(volumeId));
4340         }
4341         Abort("Error %ld trying to lock volume %lu; Aborted\n",
4342               afs_printable_int32_ld(code),
4343               afs_printable_uint32_lu(volumeId));
4344     }
4345
4346     code = FSYNC_VerifyCheckout(volumeId, salvinfo->fileSysPartition->name, FSYNC_VOL_OFF, FSYNC_SALVAGE);
4347     if (code == SYNC_DENIED) {
4348         /* need to retry checking out volumes */
4349         return -1;
4350     }
4351     if (code != SYNC_OK) {
4352         Abort("FSYNC_VerifyCheckout failed for volume %lu with code %ld\n",
4353               afs_printable_uint32_lu(volumeId), afs_printable_int32_ld(code));
4354     }
4355
4356     /* set inUse = programType in the volume header to ensure that nobody
4357      * tries to use this volume again without salvaging, if we somehow crash
4358      * or otherwise exit before finishing the salvage.
4359      */
4360     if (!Testing) {
4361        IHandle_t *h;
4362        struct VolumeHeader header;
4363        struct VolumeDiskHeader diskHeader;
4364        struct VolumeDiskData volHeader;
4365
4366        code = VReadVolumeDiskHeader(volumeId, salvinfo->fileSysPartition, &diskHeader);
4367        if (code) {
4368            return 0;
4369        }
4370
4371        DiskToVolumeHeader(&header, &diskHeader);
4372
4373        IH_INIT(h, salvinfo->fileSysDevice, header.parent, header.volumeInfo);
4374        if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
4375            volHeader.stamp.magic != VOLUMEINFOMAGIC) {
4376
4377            IH_RELEASE(h);
4378            return 0;
4379        }
4380
4381        volHeader.inUse = programType;
4382
4383        /* If we can't re-write the header, bail out and error. We don't
4384         * assert when reading the header, since it's possible the
4385         * header isn't really there (when there's no data associated
4386         * with the volume; we just delete the vol header file in that
4387         * case). But if it's there enough that we can read it, but
4388         * somehow we cannot write to it to signify we're salvaging it,
4389         * we've got a big problem and we cannot continue. */
4390        opr_Verify(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
4391                        == sizeof(volHeader));
4392
4393        IH_RELEASE(h);
4394     }
4395
4396     return 0;
4397 }
4398 #endif /* AFS_DEMAND_ATTACH_FS */
4399
4400 static void
4401 AskError(struct SalvInfo *salvinfo, VolumeId volumeId)
4402 {
4403 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
4404     afs_int32 code;
4405     code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4406                        FSYNC_VOL_FORCE_ERROR, FSYNC_WHATEVER, NULL);
4407     if (code != SYNC_OK) {
4408         Log("AskError: failed to force volume %lu into error state; "
4409             "SYNC error code %ld (%s)\n", (long unsigned)volumeId,
4410             (long)code, SYNC_res2string(code));
4411     }
4412 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
4413 }
4414
4415 void
4416 AskOffline(struct SalvInfo *salvinfo, VolumeId volumeId)
4417 {
4418     afs_int32 code, i;
4419     SYNC_response res;
4420
4421     memset(&res, 0, sizeof(res));
4422
4423     for (i = 0; i < 3; i++) {
4424         code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4425                            FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
4426
4427         if (code == SYNC_OK) {
4428             break;
4429         } else if (code == SYNC_DENIED) {
4430             if (AskDAFS())
4431                 Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
4432             else
4433                 Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
4434             Abort("Salvage aborted\n");
4435         } else if (code == SYNC_BAD_COMMAND) {
4436             Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
4437                 FSYNC_VOL_OFF);
4438             if (AskDAFS()) {
4439 #ifdef AFS_DEMAND_ATTACH_FS
4440                 Log("AskOffline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
4441 #else
4442                 Log("AskOffline:  fileserver is DAFS but we are not.\n");
4443 #endif
4444             } else {
4445 #ifdef AFS_DEMAND_ATTACH_FS
4446                 Log("AskOffline:  fileserver is not DAFS but we are.\n");
4447 #else
4448                 Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
4449 #endif
4450             }
4451             Abort("Salvage aborted\n");
4452         } else if (i < 2) {
4453             /* try it again */
4454             Log("AskOffline:  request for fileserver to take volume offline failed; trying again...\n");
4455             FSYNC_clientFinis();
4456             FSYNC_clientInit();
4457         }
4458     }
4459     if (code != SYNC_OK) {
4460         Log("AskOffline:  request for fileserver to take volume offline failed; salvage aborting.\n");
4461         Abort("Salvage aborted\n");
4462     }
4463 }
4464
4465 /* don't want to pass around state; remember it here */
4466 static int isDAFS = -1;
4467 int
4468 AskDAFS(void)
4469 {
4470     SYNC_response res;
4471     afs_int32 code = 1, i;
4472
4473     /* we don't care if we race. the answer shouldn't change */
4474     if (isDAFS != -1)
4475         return isDAFS;
4476
4477     memset(&res, 0, sizeof(res));
4478
4479     for (i = 0; code && i < 3; i++) {
4480         code = FSYNC_VolOp(0, NULL, FSYNC_VOL_LISTVOLUMES, FSYNC_SALVAGE, &res);
4481         if (code) {
4482             Log("AskDAFS: FSYNC_VOL_LISTVOLUMES failed with code %ld reason "
4483                 "%ld (%s); trying again...\n", (long)code, (long)res.hdr.reason,
4484                 FSYNC_reason2string(res.hdr.reason));
4485             FSYNC_clientFinis();
4486             FSYNC_clientInit();
4487         }
4488     }
4489
4490     if (code) {
4491         Log("AskDAFS: could not determine DAFS-ness, assuming not DAFS\n");
4492         res.hdr.flags = 0;
4493     }
4494
4495     if ((res.hdr.flags & SYNC_FLAG_DAFS_EXTENSIONS)) {
4496         isDAFS = 1;
4497     } else {
4498         isDAFS = 0;
4499     }
4500
4501     return isDAFS;
4502 }
4503
4504 static void
4505 MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
4506 {
4507     struct VolumeDiskHeader diskHdr;
4508     int code;
4509     code = VReadVolumeDiskHeader(volumeId, salvinfo->fileSysPartition, &diskHdr);
4510     if (code) {
4511         /* volume probably does not exist; no need to bring back online */
4512         return;
4513     }
4514     AskOnline(salvinfo, volumeId);
4515 }
4516
4517 void
4518 AskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
4519 {
4520     afs_int32 code, i;
4521
4522     for (i = 0; i < 3; i++) {
4523         code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4524                            FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
4525
4526         if (code == SYNC_OK) {
4527             break;
4528         } else if (code == SYNC_DENIED) {
4529             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);
4530         } else if (code == SYNC_BAD_COMMAND) {
4531             Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
4532                 FSYNC_VOL_ON);
4533             Log("AskOnline:  please make sure file server binaries are same version.\n");
4534             break;
4535         } else if (i < 2) {
4536             /* try it again */
4537             Log("AskOnline:  request for fileserver to put volume online failed; trying again...\n");
4538             FSYNC_clientFinis();
4539             FSYNC_clientInit();
4540         }
4541     }
4542 }
4543
4544 void
4545 AskDelete(struct SalvInfo *salvinfo, VolumeId volumeId)
4546 {
4547     afs_int32 code, i;
4548     SYNC_response res;
4549
4550     for (i = 0; i < 3; i++) {
4551         memset(&res, 0, sizeof(res));
4552         code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4553                            FSYNC_VOL_DONE, FSYNC_SALVAGE, &res);
4554
4555         if (code == SYNC_OK) {
4556             break;
4557         } else if (code == SYNC_DENIED) {
4558             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);
4559         } else if (code == SYNC_BAD_COMMAND) {
4560             Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
4561                 FSYNC_VOL_DONE);
4562             if (AskDAFS()) {
4563 #ifdef AFS_DEMAND_ATTACH_FS
4564                 Log("AskOnline:  please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
4565 #else
4566                 Log("AskOnline:  fileserver is DAFS but we are not.\n");
4567 #endif
4568             } else {
4569 #ifdef AFS_DEMAND_ATTACH_FS
4570                 Log("AskOnline:  fileserver is not DAFS but we are.\n");
4571 #else
4572                 Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
4573 #endif
4574             }
4575             break;
4576         } else if (code == SYNC_FAILED &&
4577                      (res.hdr.reason == FSYNC_UNKNOWN_VOLID ||
4578                       res.hdr.reason == FSYNC_WRONG_PART)) {
4579             /* volume is already effectively 'deleted' */
4580             break;
4581         } else if (i < 2) {
4582             /* try it again */
4583             Log("AskOnline:  request for fileserver to delete volume failed; trying again...\n");
4584             FSYNC_clientFinis();
4585             FSYNC_clientInit();
4586         }
4587     }
4588 }
4589
4590 int
4591 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
4592 {
4593     /* Volume parameter is passed in case iopen is upgraded in future to
4594      * require a volume Id to be passed
4595      */
4596     char buf[4096];
4597     IHandle_t *srcH, *destH;
4598     FdHandle_t *srcFdP, *destFdP;
4599     ssize_t nBytes = 0;
4600     afs_foff_t size = 0;
4601
4602     IH_INIT(srcH, device, rwvolume, inode1);
4603     srcFdP = IH_OPEN(srcH);
4604     opr_Assert(srcFdP != NULL);
4605     IH_INIT(destH, device, rwvolume, inode2);
4606     destFdP = IH_OPEN(destH);
4607     while ((nBytes = FDH_PREAD(srcFdP, buf, sizeof(buf), size)) > 0) {
4608         opr_Verify(FDH_PWRITE(destFdP, buf, nBytes, size) == nBytes);
4609         size += nBytes;
4610     }
4611     opr_Assert(nBytes == 0);
4612     FDH_REALLYCLOSE(srcFdP);
4613     FDH_REALLYCLOSE(destFdP);
4614     IH_RELEASE(srcH);
4615     IH_RELEASE(destH);
4616     return 0;
4617 }
4618
4619 void
4620 PrintInodeList(struct SalvInfo *salvinfo)
4621 {
4622     struct ViceInodeInfo *ip;
4623     struct ViceInodeInfo *buf;
4624     int nInodes;
4625     afs_ino_str_t stmp;
4626     afs_sfsize_t st_size;
4627
4628     st_size = OS_SIZE(salvinfo->inodeFd);
4629     opr_Assert(st_size >= 0);
4630     buf = malloc(st_size);
4631     opr_Assert(buf != NULL);
4632     nInodes = st_size / sizeof(struct ViceInodeInfo);
4633     opr_Verify(OS_READ(salvinfo->inodeFd, buf, st_size) == st_size);
4634     for (ip = buf; nInodes--; ip++) {
4635         Log("Inode:%s, linkCount=%d, size=%#llx, p=(%" AFS_VOLID_FMT ",%u,%u,%u)\n", /* VolumeId in param */
4636             PrintInode(stmp, ip->inodeNumber), ip->linkCount,
4637             (afs_uintmax_t) ip->byteCount,
4638             afs_printable_VolumeId_lu(ip->u.param[0]), ip->u.param[1],
4639             ip->u.param[2], ip->u.param[3]);
4640     }
4641     free(buf);
4642 }
4643
4644 void
4645 PrintInodeSummary(struct SalvInfo *salvinfo)
4646 {
4647     int i;
4648     struct InodeSummary *isp;
4649
4650     for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
4651         isp = &salvinfo->inodeSummary[i];
4652         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);
4653     }
4654 }
4655
4656 int
4657 Fork(void)
4658 {
4659     int f;
4660 #ifdef AFS_NT40_ENV
4661     f = 0;
4662     opr_Assert(0);      /* Fork is never executed in the NT code path */
4663 #else
4664     f = fork();
4665     opr_Assert(f >= 0);
4666 #ifdef AFS_DEMAND_ATTACH_FS
4667     if ((f == 0) && (programType == salvageServer)) {
4668         /* we are a salvageserver child */
4669 #ifdef FSSYNC_BUILD_CLIENT
4670         VChildProcReconnectFS_r();
4671 #endif
4672 #ifdef SALVSYNC_BUILD_CLIENT
4673         VReconnectSALV_r();
4674 #endif
4675     }
4676 #endif /* AFS_DEMAND_ATTACH_FS */
4677 #endif /* !AFS_NT40_ENV */
4678     return f;
4679 }
4680
4681 void
4682 Exit(int code)
4683 {
4684     if (ShowLog)
4685         showlog();
4686
4687 #ifdef AFS_DEMAND_ATTACH_FS
4688     if (programType == salvageServer) {
4689         /* release all volume locks before closing down our SYNC channels.
4690          * the fileserver may try to online volumes we have checked out when
4691          * we close down FSSYNC, so we should make sure we don't have those
4692          * volumes locked when it does */
4693         struct DiskPartition64 *dp;
4694         int i;
4695         for (i = 0; i <= VOLMAXPARTS; i++) {
4696             dp = VGetPartitionById(i, 0);
4697             if (dp) {
4698                 VLockFileReinit(&dp->volLockFile);
4699             }
4700         }
4701 # ifdef SALVSYNC_BUILD_CLIENT
4702         VDisconnectSALV();
4703 # endif
4704 # ifdef FSSYNC_BUILD_CLIENT
4705         VDisconnectFS();
4706 # endif
4707     }
4708 #endif /* AFS_DEMAND_ATTACH_FS */
4709
4710 #ifdef AFS_NT40_ENV
4711     if (main_thread != pthread_self())
4712         pthread_exit((void *)code);
4713     else
4714         exit(code);
4715 #else
4716     exit(code);
4717 #endif
4718 }
4719
4720 int
4721 Wait(char *prog)
4722 {
4723     int status;
4724     int pid;
4725     pid = wait(&status);
4726     opr_Assert(pid != -1);
4727     if (WCOREDUMP(status))
4728         Log("\"%s\" core dumped!\n", prog);
4729     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
4730         return -1;
4731     return pid;
4732 }
4733
4734 static char *
4735 TimeStamp(time_t clock, int precision)
4736 {
4737     struct tm *lt;
4738     static char timestamp[20];
4739     lt = localtime(&clock);
4740     if (precision)
4741         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
4742     else
4743         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
4744     return timestamp;
4745 }
4746
4747 void
4748 CheckLogFile(char * log_path)
4749 {
4750     char oldSlvgLog[AFSDIR_PATH_MAX];
4751
4752 #ifndef AFS_NT40_ENV
4753     if (useSyslog) {
4754         ShowLog = 0;
4755         return;
4756     }
4757 #endif
4758
4759     strcpy(oldSlvgLog, log_path);
4760     strcat(oldSlvgLog, ".old");
4761     if (!logFile) {
4762         rk_rename(log_path, oldSlvgLog);
4763         logFile = afs_fopen(log_path, "a");
4764
4765         if (!logFile) {         /* still nothing, use stdout */
4766             logFile = stdout;
4767             ShowLog = 0;
4768         }
4769 #ifndef AFS_NAMEI_ENV
4770         AFS_DEBUG_IOPS_LOG(logFile);
4771 #endif
4772     }
4773 }
4774
4775 #ifndef AFS_NT40_ENV
4776 void
4777 TimeStampLogFile(char * log_path)
4778 {
4779     char stampSlvgLog[AFSDIR_PATH_MAX];
4780     struct tm *lt;
4781     time_t now;
4782
4783     now = time(0);
4784     lt = localtime(&now);
4785     snprintf(stampSlvgLog, sizeof stampSlvgLog,
4786              "%s.%04d-%02d-%02d.%02d:%02d:%02d", log_path,
4787              lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour,
4788              lt->tm_min, lt->tm_sec);
4789
4790     /* try to link the logfile to a timestamped filename */
4791     /* if it fails, oh well, nothing we can do */
4792     link(log_path, stampSlvgLog);
4793 }
4794 #endif
4795
4796 void
4797 showlog(void)
4798 {
4799     char line[256];
4800
4801 #ifndef AFS_NT40_ENV
4802     if (useSyslog) {
4803         printf("Can't show log since using syslog.\n");
4804         fflush(stdout);
4805         return;
4806     }
4807 #endif
4808
4809     if (logFile) {
4810         rewind(logFile);
4811         fclose(logFile);
4812     }
4813
4814     logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
4815
4816     if (!logFile)
4817         printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
4818     else {
4819         rewind(logFile);
4820         while (fgets(line, sizeof(line), logFile))
4821             printf("%s", line);
4822         fflush(stdout);
4823     }
4824 }
4825
4826 void
4827 Log(const char *format, ...)
4828 {
4829     struct timeval now;
4830     char tmp[1024];
4831     va_list args;
4832
4833     va_start(args, format);
4834     vsnprintf(tmp, sizeof tmp, format, args);
4835     va_end(args);
4836 #ifndef AFS_NT40_ENV
4837     if (useSyslog) {
4838         syslog(LOG_INFO, "%s", tmp);
4839     } else
4840 #endif
4841         if (logFile) {
4842             gettimeofday(&now, NULL);
4843             fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
4844             fflush(logFile);
4845         }
4846 }
4847
4848 void
4849 Abort(const char *format, ...)
4850 {
4851     va_list args;
4852     char tmp[1024];
4853
4854     va_start(args, format);
4855     vsnprintf(tmp, sizeof tmp, format, args);
4856     va_end(args);
4857 #ifndef AFS_NT40_ENV
4858     if (useSyslog) {
4859         syslog(LOG_INFO, "%s", tmp);
4860     } else
4861 #endif
4862         if (logFile) {
4863             fprintf(logFile, "%s", tmp);
4864             fflush(logFile);
4865             if (ShowLog)
4866                 showlog();
4867         }
4868
4869     if (debug)
4870         abort();
4871     Exit(1);
4872 }
4873
4874 char *
4875 ToString(const char *s)
4876 {
4877     char *p;
4878     p = strdup(s);
4879     opr_Assert(p != NULL);
4880     return p;
4881 }
4882
4883 /* Remove the FORCESALVAGE file */
4884 void
4885 RemoveTheForce(char *path)
4886 {
4887     char target[1024];
4888     struct afs_stat_st force; /* so we can use afs_stat to find it */
4889     strcpy(target,path);
4890     strcat(target,"/FORCESALVAGE");
4891     if (!Testing && ForceSalvage) {
4892         if (afs_stat(target,&force) == 0)  unlink(target);
4893     }
4894 }
4895
4896 #ifndef AFS_AIX32_ENV
4897 /*
4898  * UseTheForceLuke -    see if we can use the force
4899  */
4900 int
4901 UseTheForceLuke(char *path)
4902 {
4903     struct afs_stat_st force;
4904     char target[1024];
4905     strcpy(target,path);
4906     strcat(target,"/FORCESALVAGE");
4907
4908     return (afs_stat(target, &force) == 0);
4909 }
4910 #else
4911 /*
4912  * UseTheForceLuke -    see if we can use the force
4913  *
4914  * NOTE:
4915  *      The VRMIX fsck will not muck with the filesystem it is supposedly
4916  *      fixing and create a "FORCESALVAGE" file (by design).  Instead, we
4917  *      muck directly with the root inode, which is within the normal
4918  *      domain of fsck.
4919  *      ListViceInodes() has a side effect of setting ForceSalvage if
4920  *      it detects a need, based on root inode examination.
4921  */
4922 int
4923 UseTheForceLuke(char *path)
4924 {
4925
4926     return 0;                   /* sorry OB1    */
4927 }
4928 #endif
4929
4930 #ifdef AFS_NT40_ENV
4931 /* NT support routines */
4932
4933 static char execpathname[MAX_PATH];
4934 int
4935 nt_SalvagePartition(char *partName, int jobn)
4936 {
4937     int pid;
4938     int n;
4939     childJob_t job;
4940     if (!*execpathname) {
4941         n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
4942         if (!n || n == 1023)
4943             return -1;
4944     }
4945     job.cj_magic = SALVAGER_MAGIC;
4946     job.cj_number = jobn;
4947     (void)strcpy(job.cj_part, partName);
4948     pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
4949     return pid;
4950 }
4951
4952 int
4953 nt_SetupPartitionSalvage(void *datap, int len)
4954 {
4955     childJob_t *jobp = (childJob_t *) datap;
4956     char logname[AFSDIR_PATH_MAX];
4957
4958     if (len != sizeof(childJob_t))
4959         return -1;
4960     if (jobp->cj_magic != SALVAGER_MAGIC)
4961         return -1;
4962     myjob = *jobp;
4963
4964     /* Open logFile */
4965     (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
4966                   myjob.cj_number);
4967     logFile = afs_fopen(logname, "w");
4968     if (!logFile)
4969         logFile = stdout;
4970
4971     return 0;
4972 }
4973
4974
4975 #endif /* AFS_NT40_ENV */