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