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