f747ff588a29caf602e1b081f707d91c2698e514
[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 #ifndef AFS_NT40_ENV
93 #include <sys/param.h>
94 #include <sys/file.h>
95 #ifndef ITIMER_REAL
96 #include <sys/time.h>
97 #endif /* ITIMER_REAL */
98 #endif
99 #include <stdlib.h>
100 #include <stdio.h>
101 #include <string.h>
102 #include <dirent.h>
103 #include <sys/stat.h>
104 #include <time.h>
105 #include <errno.h>
106 #ifdef AFS_NT40_ENV
107 #include <io.h>
108 #include <WINNT/afsevent.h>
109 #endif
110 #ifndef WCOREDUMP
111 #define WCOREDUMP(x)    ((x) & 0200)
112 #endif
113 #include <rx/xdr.h>
114 #include <afs/afsint.h>
115 #include <afs/afs_assert.h>
116 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
117 #if defined(AFS_VFSINCL_ENV)
118 #include <sys/vnode.h>
119 #ifdef  AFS_SUN5_ENV
120 #include <sys/fs/ufs_inode.h>
121 #else
122 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
123 #include <ufs/ufs/dinode.h>
124 #include <ufs/ffs/fs.h>
125 #else
126 #include <ufs/inode.h>
127 #endif
128 #endif
129 #else /* AFS_VFSINCL_ENV */
130 #ifdef  AFS_OSF_ENV
131 #include <ufs/inode.h>
132 #else /* AFS_OSF_ENV */
133 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_ARM_DARWIN_ENV)
134 #include <sys/inode.h>
135 #endif
136 #endif
137 #endif /* AFS_VFSINCL_ENV */
138 #endif /* AFS_SGI_ENV */
139 #ifdef  AFS_AIX_ENV
140 #include <sys/vfs.h>
141 #include <sys/lockf.h>
142 #else
143 #ifdef  AFS_HPUX_ENV
144 #include <unistd.h>
145 #include <checklist.h>
146 #else
147 #if defined(AFS_SGI_ENV)
148 #include <unistd.h>
149 #include <fcntl.h>
150 #include <mntent.h>
151 #else
152 #if     defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
153 #ifdef    AFS_SUN5_ENV
154 #include <unistd.h>
155 #include <sys/mnttab.h>
156 #include <sys/mntent.h>
157 #else
158 #include <mntent.h>
159 #endif
160 #else
161 #endif /* AFS_SGI_ENV */
162 #endif /* AFS_HPUX_ENV */
163 #endif
164 #endif
165 #include <fcntl.h>
166 #ifndef AFS_NT40_ENV
167 #include <afs/osi_inode.h>
168 #endif
169 #include <afs/cmd.h>
170 #include <afs/dir.h>
171 #include <afs/afsutil.h>
172 #include <afs/fileutil.h>
173 #include <afs/procmgmt.h>       /* signal(), kill(), wait(), etc. */
174 #ifndef AFS_NT40_ENV
175 #include <syslog.h>
176 #endif
177
178 #include "nfs.h"
179 #include "lwp.h"
180 #include "lock.h"
181 #include <afs/afssyscalls.h>
182 #include "ihandle.h"
183 #include "vnode.h"
184 #include "volume.h"
185 #include "partition.h"
186 #include "daemon_com.h"
187 #include "fssync.h"
188 #include "volume_inline.h"
189 #include "salvsync.h"
190 #include "viceinode.h"
191 #include "salvage.h"
192 #include "volinodes.h"          /* header magic number, etc. stuff */
193 #include "vol-salvage.h"
194 #include "common.h"
195 #include "vol_internal.h"
196 #include <afs/acl.h>
197 #include <afs/prs_fs.h>
198
199 #ifdef FSSYNC_BUILD_CLIENT
200 #include "vg_cache.h"
201 #endif
202
203 #ifdef AFS_NT40_ENV
204 #include <pthread.h>
205 #endif
206
207 #ifdef  AFS_OSF_ENV
208 extern void *calloc();
209 #endif
210 static char *TimeStamp(time_t clock, int precision);
211
212
213 int debug;                      /* -d flag */
214 extern int Testing;             /* -n flag */
215 int ListInodeOption;            /* -i flag */
216 int ShowRootFiles;              /* -r flag */
217 int RebuildDirs;                /* -sal flag */
218 int Parallel = 4;               /* -para X flag */
219 int PartsPerDisk = 8;           /* Salvage up to 8 partitions on same disk sequentially */
220 int forceR = 0;                 /* -b flag */
221 int ShowLog = 0;                /* -showlog flag */
222 int ShowSuid = 0;               /* -showsuid flag */
223 int ShowMounts = 0;             /* -showmounts flag */
224 int orphans = ORPH_IGNORE;      /* -orphans option */
225 int Showmode = 0;
226
227
228 #ifndef AFS_NT40_ENV
229 int useSyslog = 0;              /* -syslog flag */
230 int useSyslogFacility = LOG_DAEMON;     /* -syslogfacility option */
231 #endif
232
233 #ifdef AFS_NT40_ENV
234 int canfork = 0;
235 #else
236 int canfork = 1;
237 #endif
238
239 #define MAXPARALLEL     32
240
241 int OKToZap;                    /* -o flag */
242 int ForceSalvage;               /* If salvage should occur despite the DONT_SALVAGE flag
243                                  * in the volume header */
244
245 FILE *logFile = 0;      /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
246
247 #define ROOTINODE       2       /* Root inode of a 4.2 Unix file system
248                                  * partition */
249 /**
250  * information that is 'global' to a particular salvage job.
251  */
252 struct SalvInfo {
253     Device fileSysDevice;    /**< The device number of the current partition
254                               *   being salvaged */
255     char fileSysPath[8];     /**< The path of the mounted partition currently
256                               *   being salvaged, i.e. the directory containing
257                               *   the volume headers */
258     char *fileSysPathName;   /**< NT needs this to make name pretty log. */
259     IHandle_t *VGLinkH;      /**< Link handle for current volume group. */
260     int VGLinkH_cnt;         /**< # of references to lnk handle. */
261     struct DiskPartition64 *fileSysPartition; /**< Partition being salvaged */
262
263 #ifndef AFS_NT40_ENV
264     char *fileSysDeviceName; /**< The block device where the file system being
265                               *   salvaged was mounted */
266     char *filesysfulldev;
267 #endif
268     int VolumeChanged;       /**< Set by any routine which would change the
269                               *   volume in a way which would require callbacks
270                               *   to be broken if the volume was put back on
271                               *   on line by an active file server */
272
273     VolumeDiskData VolInfo;  /**< A copy of the last good or salvaged volume
274                               *   header dealt with */
275
276     int nVolumesInInodeFile; /**< Number of read-write volumes summarized */
277     FD_t inodeFd;             /**< File descriptor for inode file */
278
279     struct VolumeSummary *volumeSummaryp; /**< Holds all the volumes in a part */
280     int nVolumes;            /**< Number of volumes (read-write and read-only)
281                               *   in volume summary */
282     struct InodeSummary *inodeSummary; /**< contains info on all the relevant
283                                         *   inodes */
284
285     struct VnodeInfo vnodeInfo[nVNODECLASSES]; /**< contains info on all of the
286                                                 *   vnodes in the volume that
287                                                 *   we are currently looking
288                                                 *   at */
289     int useFSYNC; /**< 0 if the fileserver is unavailable; 1 if we should try
290                    *   to contact the fileserver over FSYNC */
291 };
292
293 char *tmpdir = NULL;
294
295
296
297 /* Forward declarations */
298 static int IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode);
299 static int AskVolumeSummary(struct SalvInfo *salvinfo,
300                             VolumeId singleVolumeNumber);
301 static void MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId);
302
303 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
304 static int LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId);
305 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
306
307 /* Uniquifier stored in the Inode */
308 static Unique
309 IUnique(Unique u)
310 {
311 #ifdef  AFS_3DISPARES
312     return (u & 0x3fffff);
313 #else
314 #if defined(AFS_SGI_EXMAG)
315     return (u & SGI_UNIQMASK);
316 #else
317     return (u);
318 #endif /* AFS_SGI_EXMAG */
319 #endif
320 }
321
322 static int
323 BadError(int aerror)
324 {
325     if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
326         return 1;
327     return 0;                   /* otherwise may be transient, e.g. EMFILE */
328 }
329
330 #define MAX_ARGS 128
331 #ifdef AFS_NT40_ENV
332 char *save_args[MAX_ARGS];
333 int n_save_args = 0;
334 extern pthread_t main_thread;
335 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
336 #endif
337
338 /**
339  * Get the salvage lock if not already held. Hold until process exits.
340  *
341  * @param[in] locktype READ_LOCK or WRITE_LOCK
342  */
343 static void
344 _ObtainSalvageLock(int locktype)
345 {
346     struct VLockFile salvageLock;
347     int offset = 0;
348     int nonblock = 1;
349     int code;
350
351     VLockFileInit(&salvageLock, AFSDIR_SERVER_SLVGLOCK_FILEPATH);
352
353     code = VLockFileLock(&salvageLock, offset, locktype, nonblock);
354     if (code == EBUSY) {
355         fprintf(stderr,
356                 "salvager:  There appears to be another salvager running!  "
357                 "Aborted.\n");
358         Exit(1);
359     } else if (code) {
360         fprintf(stderr,
361                 "salvager:  Error %d trying to acquire salvage lock!  "
362                 "Aborted.\n", code);
363         Exit(1);
364     }
365 }
366 void
367 ObtainSalvageLock(void)
368 {
369     _ObtainSalvageLock(WRITE_LOCK);
370 }
371 void
372 ObtainSharedSalvageLock(void)
373 {
374     _ObtainSalvageLock(READ_LOCK);
375 }
376
377
378 #ifdef AFS_SGI_XFS_IOPS_ENV
379 /* Check if the given partition is mounted. For XFS, the root inode is not a
380  * constant. So we check the hard way.
381  */
382 int
383 IsPartitionMounted(char *part)
384 {
385     FILE *mntfp;
386     struct mntent *mntent;
387
388     osi_Assert(mntfp = setmntent(MOUNTED, "r"));
389     while (mntent = getmntent(mntfp)) {
390         if (!strcmp(part, mntent->mnt_dir))
391             break;
392     }
393     endmntent(mntfp);
394
395     return mntent ? 1 : 1;
396 }
397 #endif
398 /* Check if the given inode is the root of the filesystem. */
399 #ifndef AFS_SGI_XFS_IOPS_ENV
400 int
401 IsRootInode(struct afs_stat_st *status)
402 {
403     /*
404      * The root inode is not a fixed value in XFS partitions. So we need to
405      * see if the partition is in the list of mounted partitions. This only
406      * affects the SalvageFileSys path, so we check there.
407      */
408     return (status->st_ino == ROOTINODE);
409 }
410 #endif
411
412 #ifdef AFS_AIX42_ENV
413 #ifndef AFS_NAMEI_ENV
414 /* We don't want to salvage big files filesystems, since we can't put volumes on
415  * them.
416  */
417 int
418 CheckIfBigFilesFS(char *mountPoint, char *devName)
419 {
420     struct superblock fs;
421     char name[128];
422
423     if (strncmp(devName, "/dev/", 5)) {
424         (void)sprintf(name, "/dev/%s", devName);
425     } else {
426         (void)strcpy(name, devName);
427     }
428
429     if (ReadSuper(&fs, name) < 0) {
430         Log("Unable to read superblock. Not salvaging partition %s.\n",
431             mountPoint);
432         return 1;
433     }
434     if (IsBigFilesFileSystem(&fs)) {
435         Log("Partition %s is a big files filesystem, not salvaging.\n",
436             mountPoint);
437         return 1;
438     }
439     return 0;
440 }
441 #endif
442 #endif
443
444 #ifdef AFS_NT40_ENV
445 #define HDSTR "\\Device\\Harddisk"
446 #define HDLEN  (sizeof(HDSTR)-1)        /* Length of "\Device\Harddisk" */
447 int
448 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
449 {
450 #define RES_LEN 256
451     char res1[RES_LEN];
452     char res2[RES_LEN];
453
454     static int dowarn = 1;
455
456     if (!QueryDosDevice(p1->devName, res1, RES_LEN - 1))
457         return 1;
458     if (strncmp(res1, HDSTR, HDLEN)) {
459         if (dowarn) {
460             dowarn = 0;
461             Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
462                 res1, HDSTR, p1->devName);
463         }
464     }
465     if (!QueryDosDevice(p2->devName, res2, RES_LEN - 1))
466         return 1;
467     if (strncmp(res2, HDSTR, HDLEN)) {
468         if (dowarn) {
469             dowarn = 0;
470             Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
471                 res2, HDSTR, p2->devName);
472         }
473     }
474
475     return (0 == _strnicmp(res1, res2, RES_LEN - 1));
476 }
477 #else
478 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
479 #endif
480
481 /* This assumes that two partitions with the same device number divided by
482  * PartsPerDisk are on the same disk.
483  */
484 void
485 SalvageFileSysParallel(struct DiskPartition64 *partP)
486 {
487     struct job {
488         struct DiskPartition64 *partP;
489         int pid;                /* Pid for this job */
490         int jobnumb;            /* Log file job number */
491         struct job *nextjob;    /* Next partition on disk to salvage */
492     };
493     static struct job *jobs[MAXPARALLEL] = { 0 };       /* Need to zero this */
494     struct job *thisjob = 0;
495     static int numjobs = 0;
496     static int jobcount = 0;
497     char buf[1024];
498     int wstatus;
499     struct job *oldjob;
500     int startjob;
501     FILE *passLog;
502     char logFileName[256];
503     int i, j, pid;
504
505     if (partP) {
506         /* We have a partition to salvage. Copy it into thisjob */
507         thisjob = (struct job *)malloc(sizeof(struct job));
508         if (!thisjob) {
509             Log("Can't salvage '%s'. Not enough memory\n", partP->name);
510             return;
511         }
512         memset(thisjob, 0, sizeof(struct job));
513         thisjob->partP = partP;
514         thisjob->jobnumb = jobcount;
515         jobcount++;
516     } else if (jobcount == 0) {
517         /* We are asking to wait for all jobs (partp == 0), yet we never
518          * started any.
519          */
520         Log("No file system partitions named %s* found; not salvaged\n",
521             VICE_PARTITION_PREFIX);
522         return;
523     }
524
525     if (debug || Parallel == 1) {
526         if (thisjob) {
527             SalvageFileSys(thisjob->partP, 0);
528             free(thisjob);
529         }
530         return;
531     }
532
533     if (thisjob) {
534         /* Check to see if thisjob is for a disk that we are already
535          * salvaging. If it is, link it in as the next job to do. The
536          * jobs array has 1 entry per disk being salvages. numjobs is
537          * the total number of disks currently being salvaged. In
538          * order to keep thejobs array compact, when a disk is
539          * completed, the hightest element in the jobs array is moved
540          * down to now open slot.
541          */
542         for (j = 0; j < numjobs; j++) {
543             if (SameDisk(jobs[j]->partP, thisjob->partP)) {
544                 /* On same disk, add it to this list and return */
545                 thisjob->nextjob = jobs[j]->nextjob;
546                 jobs[j]->nextjob = thisjob;
547                 thisjob = 0;
548                 break;
549             }
550         }
551     }
552
553     /* Loop until we start thisjob or until all existing jobs are finished */
554     while (thisjob || (!partP && (numjobs > 0))) {
555         startjob = -1;          /* No new job to start */
556
557         if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
558             /* Either the max jobs are running or we have to wait for all
559              * the jobs to finish. In either case, we wait for at least one
560              * job to finish. When it's done, clean up after it.
561              */
562             pid = wait(&wstatus);
563             osi_Assert(pid != -1);
564             for (j = 0; j < numjobs; j++) {     /* Find which job it is */
565                 if (pid == jobs[j]->pid)
566                     break;
567             }
568             osi_Assert(j < numjobs);
569             if (WCOREDUMP(wstatus)) {   /* Say if the job core dumped */
570                 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
571             }
572
573             numjobs--;          /* job no longer running */
574             oldjob = jobs[j];   /* remember */
575             jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
576             free(oldjob);       /* free the old job */
577
578             /* If there is another partition on the disk to salvage, then
579              * say we will start it (startjob). If not, then put thisjob there
580              * and say we will start it.
581              */
582             if (jobs[j]) {      /* Another partitions to salvage */
583                 startjob = j;   /* Will start it */
584             } else {            /* There is not another partition to salvage */
585                 if (thisjob) {
586                     jobs[j] = thisjob;  /* Add thisjob */
587                     thisjob = 0;
588                     startjob = j;       /* Will start it */
589                 } else {
590                     jobs[j] = jobs[numjobs];    /* Move last job up to this slot */
591                     startjob = -1;      /* Don't start it - already running */
592                 }
593             }
594         } else {
595             /* We don't have to wait for a job to complete */
596             if (thisjob) {
597                 jobs[numjobs] = thisjob;        /* Add this job */
598                 thisjob = 0;
599                 startjob = numjobs;     /* Will start it */
600             }
601         }
602
603         /* Start up a new salvage job on a partition in job slot "startjob" */
604         if (startjob != -1) {
605             if (!Showmode)
606                 Log("Starting salvage of file system partition %s\n",
607                     jobs[startjob]->partP->name);
608 #ifdef AFS_NT40_ENV
609             /* For NT, we not only fork, but re-exec the salvager. Pass in the
610              * commands and pass the child job number via the data path.
611              */
612             pid =
613                 nt_SalvagePartition(jobs[startjob]->partP->name,
614                                     jobs[startjob]->jobnumb);
615             jobs[startjob]->pid = pid;
616             numjobs++;
617 #else
618             pid = Fork();
619             if (pid) {
620                 jobs[startjob]->pid = pid;
621                 numjobs++;
622             } else {
623                 int fd;
624
625                 ShowLog = 0;
626                 for (fd = 0; fd < 16; fd++)
627                     close(fd);
628                 open(OS_DIRSEP, 0);
629                 dup2(0, 1);
630                 dup2(0, 2);
631 #ifndef AFS_NT40_ENV
632                 if (useSyslog) {
633                     openlog("salvager", LOG_PID, useSyslogFacility);
634                 } else
635 #endif
636                 {
637                     (void)afs_snprintf(logFileName, sizeof logFileName,
638                                        "%s.%d",
639                                        AFSDIR_SERVER_SLVGLOG_FILEPATH,
640                                        jobs[startjob]->jobnumb);
641                     logFile = afs_fopen(logFileName, "w");
642                 }
643                 if (!logFile)
644                     logFile = stdout;
645
646                 SalvageFileSys1(jobs[startjob]->partP, 0);
647                 Exit(0);
648             }
649 #endif
650         }
651     }                           /* while ( thisjob || (!partP && numjobs > 0) ) */
652
653     /* If waited for all jobs to complete, now collect log files and return */
654 #ifndef AFS_NT40_ENV
655     if (!useSyslog)             /* if syslogging - no need to collect */
656 #endif
657         if (!partP) {
658             for (i = 0; i < jobcount; i++) {
659                 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
660                                    AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
661                 if ((passLog = afs_fopen(logFileName, "r"))) {
662                     while (fgets(buf, sizeof(buf), passLog)) {
663                         fputs(buf, logFile);
664                     }
665                     fclose(passLog);
666                 }
667                 (void)unlink(logFileName);
668             }
669             fflush(logFile);
670         }
671     return;
672 }
673
674
675 void
676 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
677 {
678     if (!canfork || debug || Fork() == 0) {
679         SalvageFileSys1(partP, singleVolumeNumber);
680         if (canfork && !debug) {
681             ShowLog = 0;
682             Exit(0);
683         }
684     } else
685         Wait("SalvageFileSys");
686 }
687
688 char *
689 get_DevName(char *pbuffer, char *wpath)
690 {
691     char pbuf[128], *ptr;
692     strcpy(pbuf, pbuffer);
693     ptr = (char *)strrchr(pbuf, OS_DIRSEPC);
694     if (ptr) {
695         *ptr = '\0';
696         strcpy(wpath, pbuf);
697     } else
698         return NULL;
699     ptr = (char *)strrchr(pbuffer, OS_DIRSEPC);
700     if (ptr) {
701         strcpy(pbuffer, ptr + 1);
702         return pbuffer;
703     } else
704         return NULL;
705 }
706
707 void
708 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
709 {
710     char *name, *tdir;
711     char inodeListPath[256];
712     FD_t inodeFile = INVALID_FD;
713     static char tmpDevName[100];
714     static char wpath[100];
715     struct VolumeSummary *vsp, *esp;
716     int i, j;
717     int code;
718     int tries = 0;
719     struct SalvInfo l_salvinfo;
720     struct SalvInfo *salvinfo = &l_salvinfo;
721
722  retry:
723     memset(salvinfo, 0, sizeof(*salvinfo));
724
725     tries++;
726     if (inodeFile != INVALID_FD) {
727         OS_CLOSE(inodeFile);
728         inodeFile = INVALID_FD;
729     }
730     if (tries > VOL_MAX_CHECKOUT_RETRIES) {
731         Abort("Raced too many times with fileserver restarts while trying to "
732               "checkout/lock volumes; Aborted\n");
733     }
734 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
735     if (tries > 1) {
736         /* unlock all previous volume locks, since we're about to lock them
737          * again */
738         VLockFileReinit(&partP->volLockFile);
739     }
740 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
741
742     salvinfo->fileSysPartition = partP;
743     salvinfo->fileSysDevice = salvinfo->fileSysPartition->device;
744     salvinfo->fileSysPathName = VPartitionPath(salvinfo->fileSysPartition);
745
746 #ifdef AFS_NT40_ENV
747     /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
748     (void)sprintf(salvinfo->fileSysPath, "%s" OS_DIRSEP, salvinfo->fileSysPathName);
749     name = partP->devName;
750 #else
751     strlcpy(salvinfo->fileSysPath, salvinfo->fileSysPathName, sizeof(salvinfo->fileSysPath));
752     strcpy(tmpDevName, partP->devName);
753     name = get_DevName(tmpDevName, wpath);
754     salvinfo->fileSysDeviceName = name;
755     salvinfo->filesysfulldev = wpath;
756 #endif
757
758     if (singleVolumeNumber) {
759 #if !(defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL))
760         /* only non-DAFS locks the partition when salvaging a single volume;
761          * DAFS will lock the individual volumes in the VG */
762         VLockPartition(partP->name);
763 #endif /* !(AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL) */
764
765         ForceSalvage = 1;
766
767         /* salvageserver already setup fssync conn for us */
768         if ((programType != salvageServer) && !VConnectFS()) {
769             Abort("Couldn't connect to file server\n");
770         }
771
772         salvinfo->useFSYNC = 1;
773         AskOffline(salvinfo, singleVolumeNumber);
774 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
775         if (LockVolume(salvinfo, singleVolumeNumber)) {
776             goto retry;
777         }
778 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
779
780     } else {
781         salvinfo->useFSYNC = 0;
782         VLockPartition(partP->name);
783         if (ForceSalvage) {
784             ForceSalvage = 1;
785         } else {
786             ForceSalvage = UseTheForceLuke(salvinfo->fileSysPath);
787         }
788         if (!Showmode)
789             Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
790                 partP->name, name, (Testing ? "(READONLY mode)" : ""));
791         if (ForceSalvage)
792             Log("***Forced salvage of all volumes on this partition***\n");
793     }
794
795
796     /*
797      * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
798      * files
799      */
800     {
801         DIR *dirp;
802         struct dirent *dp;
803
804         osi_Assert((dirp = opendir(salvinfo->fileSysPath)) != NULL);
805         while ((dp = readdir(dirp))) {
806             if (!strncmp(dp->d_name, "salvage.inodes.", 15)
807                 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
808                 char npath[1024];
809                 Log("Removing old salvager temp files %s\n", dp->d_name);
810                 strcpy(npath, salvinfo->fileSysPath);
811                 strcat(npath, OS_DIRSEP);
812                 strcat(npath, dp->d_name);
813                 OS_UNLINK(npath);
814             }
815         }
816         closedir(dirp);
817     }
818     tdir = (tmpdir ? tmpdir : salvinfo->fileSysPath);
819 #ifdef AFS_NT40_ENV
820     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
821     (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
822 #else
823     snprintf(inodeListPath, 255, "%s" OS_DIRSEP "salvage.inodes.%s.%d", tdir, name,
824              getpid());
825 #endif
826
827     inodeFile = OS_OPEN(inodeListPath, O_RDWR|O_TRUNC|O_CREAT, 0666);
828     if (inodeFile == INVALID_FD) {
829         Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
830     }
831 #ifdef AFS_NT40_ENV
832     /* Using nt_unlink here since we're really using the delete on close
833      * semantics of unlink. In most places in the salvager, we really do
834      * mean to unlink the file at that point. Those places have been
835      * modified to actually do that so that the NT crt can be used there.
836      *
837      * jaltman - On NT delete on close cannot be applied to a file while the
838      * process has an open file handle that does not have DELETE file
839      * access and FILE_SHARE_DELETE.  fopen() calls CreateFile() without
840      * delete privileges.  As a result the nt_unlink() call will always
841      * fail.
842      */
843     code = nt_unlink(inodeListPath);
844 #else
845     code = unlink(inodeListPath);
846 #endif
847     if (code < 0) {
848         Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
849     }
850
851     if (GetInodeSummary(salvinfo, inodeFile, singleVolumeNumber) < 0) {
852         OS_CLOSE(inodeFile);
853         if (singleVolumeNumber) {
854             /* the volume group -- let alone the volume -- does not exist,
855              * but we checked it out, so give it back to the fileserver */
856             AskDelete(salvinfo, singleVolumeNumber);
857         }
858         return;
859     }
860     salvinfo->inodeFd = inodeFile;
861     if (salvinfo->inodeFd == INVALID_FD)
862         Abort("Temporary file %s is missing...\n", inodeListPath);
863     OS_SEEK(salvinfo->inodeFd, 0L, SEEK_SET);
864     if (ListInodeOption) {
865         PrintInodeList(salvinfo);
866         if (singleVolumeNumber) {
867             /* We've checked out the volume from the fileserver, and we need
868              * to give it back. We don't know if the volume exists or not,
869              * so we don't know whether to AskOnline or not. Try to determine
870              * if the volume exists by trying to read the volume header, and
871              * AskOnline if it is readable. */
872             MaybeAskOnline(salvinfo, singleVolumeNumber);
873         }
874         return;
875     }
876     /* enumerate volumes in the partition.
877      * figure out sets of read-only + rw volumes.
878      * salvage each set, read-only volumes first, then read-write.
879      * Fix up inodes on last volume in set (whether it is read-write
880      * or read-only).
881      */
882     if (GetVolumeSummary(salvinfo, singleVolumeNumber)) {
883         goto retry;
884     }
885
886     for (i = j = 0, vsp = salvinfo->volumeSummaryp, esp = vsp + salvinfo->nVolumes;
887          i < salvinfo->nVolumesInInodeFile; i = j) {
888         VolumeId rwvid = salvinfo->inodeSummary[i].RWvolumeId;
889         for (j = i;
890              j < salvinfo->nVolumesInInodeFile && salvinfo->inodeSummary[j].RWvolumeId == rwvid;
891              j++) {
892             VolumeId vid = salvinfo->inodeSummary[j].volumeId;
893             struct VolumeSummary *tsp;
894             /* Scan volume list (from partition root directory) looking for the
895              * current rw volume number in the volume list from the inode scan.
896              * If there is one here that is not in the inode volume list,
897              * delete it now. */
898             for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
899                 if (vsp->fileName)
900                     DeleteExtraVolumeHeaderFile(salvinfo, vsp);
901             }
902             /* Now match up the volume summary info from the root directory with the
903              * entry in the volume list obtained from scanning inodes */
904             salvinfo->inodeSummary[j].volSummary = NULL;
905             for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
906                 if (tsp->header.id == vid) {
907                     salvinfo->inodeSummary[j].volSummary = tsp;
908                     tsp->fileName = 0;
909                     break;
910                 }
911             }
912         }
913         /* Salvage the group of volumes (several read-only + 1 read/write)
914          * starting with the current read-only volume we're looking at.
915          */
916         SalvageVolumeGroup(salvinfo, &salvinfo->inodeSummary[i], j - i);
917     }
918
919     /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
920     for (; vsp < esp; vsp++) {
921         if (vsp->fileName)
922             DeleteExtraVolumeHeaderFile(salvinfo, vsp);
923     }
924
925     if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
926         RemoveTheForce(salvinfo->fileSysPath);
927
928     if (!Testing && singleVolumeNumber) {
929         int foundSVN = 0;
930 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
931         /* unlock vol headers so the fs can attach them when we AskOnline */
932         VLockFileReinit(&salvinfo->fileSysPartition->volLockFile);
933 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
934
935         /* Step through the volumeSummary list and set all volumes on-line.
936          * Most volumes were taken off-line in GetVolumeSummary.
937          * If a volume was deleted, don't tell the fileserver anything, since
938          * we already told the fileserver the volume was deleted back when we
939          * we destroyed the volume header.
940          * Also, make sure we bring the singleVolumeNumber back online first.
941          */
942
943         for (j = 0; j < salvinfo->nVolumes; j++) {
944             if (salvinfo->volumeSummaryp[j].header.id == singleVolumeNumber) {
945                 foundSVN = 1;
946                 if (!salvinfo->volumeSummaryp[j].deleted) {
947                     AskOnline(salvinfo, singleVolumeNumber);
948                 }
949             }
950         }
951
952         if (!foundSVN) {
953             /* If singleVolumeNumber is not in our volumeSummary, it means that
954              * at least one other volume in the VG is on the partition, but the
955              * RW volume is not. We've already AskOffline'd it by now, though,
956              * so make sure we don't still have the volume checked out. */
957             AskDelete(salvinfo, singleVolumeNumber);
958         }
959
960         for (j = 0; j < salvinfo->nVolumes; j++) {
961             if (salvinfo->volumeSummaryp[j].header.id != singleVolumeNumber) {
962                 if (!salvinfo->volumeSummaryp[j].deleted) {
963                     AskOnline(salvinfo, salvinfo->volumeSummaryp[j].header.id);
964                 }
965             }
966         }
967     } else {
968         if (!Showmode)
969             Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
970                 salvinfo->fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
971     }
972
973     OS_CLOSE(inodeFile);                /* SalvageVolumeGroup was the last which needed it. */
974 }
975
976 void
977 DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
978 {
979     char path[64];
980     sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, vsp->fileName);
981
982     if (!Showmode)
983         Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
984     if (!Testing) {
985         afs_int32 code;
986         code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, vsp->header.id, vsp->header.parent);
987         if (code) {
988             Log("Error %ld destroying volume disk header for volume %lu\n",
989                 afs_printable_int32_ld(code),
990                 afs_printable_uint32_lu(vsp->header.id));
991         }
992
993         /* make sure we actually delete the fileName file; ENOENT
994          * is fine, since VDestroyVolumeDiskHeader probably already
995          * unlinked it */
996         if (unlink(path) && errno != ENOENT) {
997             Log("Unable to unlink %s (errno = %d)\n", path, errno);
998         }
999         if (salvinfo->useFSYNC) {
1000             AskDelete(salvinfo, vsp->header.id);
1001         }
1002         vsp->deleted = 1;
1003     }
1004     vsp->fileName = 0;
1005 }
1006
1007 int
1008 CompareInodes(const void *_p1, const void *_p2)
1009 {
1010     const struct ViceInodeInfo *p1 = _p1;
1011     const struct ViceInodeInfo *p2 = _p2;
1012     if (p1->u.vnode.vnodeNumber == INODESPECIAL
1013         || p2->u.vnode.vnodeNumber == INODESPECIAL) {
1014         VolumeId p1rwid, p2rwid;
1015         p1rwid =
1016             (p1->u.vnode.vnodeNumber ==
1017              INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
1018         p2rwid =
1019             (p2->u.vnode.vnodeNumber ==
1020              INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
1021         if (p1rwid < p2rwid)
1022             return -1;
1023         if (p1rwid > p2rwid)
1024             return 1;
1025         if (p1->u.vnode.vnodeNumber == INODESPECIAL
1026             && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1027             if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1028                 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
1029             if (p1->u.vnode.volumeId == p1rwid)
1030                 return -1;
1031             if (p2->u.vnode.volumeId == p2rwid)
1032                 return 1;
1033             return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
1034         }
1035         if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1036             return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
1037         return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
1038     }
1039     if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1040         return -1;
1041     if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1042         return 1;
1043     if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1044         return -1;
1045     if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1046         return 1;
1047     /* The following tests are reversed, so that the most desirable
1048      * of several similar inodes comes first */
1049     if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1050 #ifdef  AFS_3DISPARES
1051         if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
1052             p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1053             return 1;
1054 #endif
1055 #ifdef  AFS_SGI_EXMAG
1056         if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
1057             p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1058             return 1;
1059 #endif
1060         return -1;
1061     }
1062     if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1063 #ifdef  AFS_3DISPARES
1064         if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */  &&
1065             p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1066             return -1;
1067 #endif
1068 #ifdef  AFS_SGI_EXMAG
1069         if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */  &&
1070             p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1071             return 1;
1072 #endif
1073         return 1;
1074     }
1075     if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1076 #ifdef  AFS_3DISPARES
1077         if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
1078             p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1079             return 1;
1080 #endif
1081 #ifdef  AFS_SGI_EXMAG
1082         if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
1083             p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1084             return 1;
1085 #endif
1086         return -1;
1087     }
1088     if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1089 #ifdef  AFS_3DISPARES
1090         if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */  &&
1091             p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1092             return -1;
1093 #endif
1094 #ifdef  AFS_SGI_EXMAG
1095         if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */  &&
1096             p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1097             return 1;
1098 #endif
1099         return 1;
1100     }
1101     return 0;
1102 }
1103
1104 void
1105 CountVolumeInodes(struct ViceInodeInfo *ip, int maxInodes,
1106                   struct InodeSummary *summary)
1107 {
1108     VolumeId volume = ip->u.vnode.volumeId;
1109     VolumeId rwvolume = volume;
1110     int n, nSpecial;
1111     Unique maxunique;
1112     n = nSpecial = 0;
1113     maxunique = 0;
1114     while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1115         n++;
1116         if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1117             nSpecial++;
1118             rwvolume = ip->u.special.parentId;
1119             /* This isn't quite right, as there could (in error) be different
1120              * parent inodes in different special vnodes */
1121         } else {
1122             if (maxunique < ip->u.vnode.vnodeUniquifier)
1123                 maxunique = ip->u.vnode.vnodeUniquifier;
1124         }
1125         ip++;
1126     }
1127     summary->volumeId = volume;
1128     summary->RWvolumeId = rwvolume;
1129     summary->nInodes = n;
1130     summary->nSpecialInodes = nSpecial;
1131     summary->maxUniquifier = maxunique;
1132 }
1133
1134 int
1135 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1136 {
1137     if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1138         return (inodeinfo->u.special.parentId == singleVolumeNumber);
1139     return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1140 }
1141
1142 /* GetInodeSummary
1143  *
1144  * Collect list of inodes in file named by path. If a truly fatal error,
1145  * unlink the file and abort. For lessor errors, return -1. The file will
1146  * be unlinked by the caller.
1147  */
1148 int
1149 GetInodeSummary(struct SalvInfo *salvinfo, FD_t inodeFile, VolumeId singleVolumeNumber)
1150 {
1151     int forceSal, err;
1152     int code;
1153     struct ViceInodeInfo *ip, *ip_save;
1154     struct InodeSummary summary;
1155     char summaryFileName[50];
1156     FD_t summaryFile = INVALID_FD;
1157 #ifdef AFS_NT40_ENV
1158     char *dev = salvinfo->fileSysPath;
1159     char *wpath = salvinfo->fileSysPath;
1160 #else
1161     char *dev = salvinfo->fileSysDeviceName;
1162     char *wpath = salvinfo->filesysfulldev;
1163 #endif
1164     char *part = salvinfo->fileSysPath;
1165     char *tdir;
1166     int i;
1167     afs_sfsize_t st_size;
1168
1169     /* This file used to come from vfsck; cobble it up ourselves now... */
1170     if ((err =
1171          ListViceInodes(dev, salvinfo->fileSysPath, inodeFile,
1172                         singleVolumeNumber ? OnlyOneVolume : 0,
1173                         singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1174         if (err == -2) {
1175             Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1176             return -1;
1177         }
1178         Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1179     }
1180     if (forceSal && !ForceSalvage) {
1181         Log("***Forced salvage of all volumes on this partition***\n");
1182         ForceSalvage = 1;
1183     }
1184     OS_SEEK(inodeFile, 0L, SEEK_SET);
1185     salvinfo->inodeFd = inodeFile;
1186     if (salvinfo->inodeFd == INVALID_FD ||
1187         (st_size = OS_SIZE(salvinfo->inodeFd)) == -1) {
1188         Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1189     }
1190     tdir = (tmpdir ? tmpdir : part);
1191 #ifdef AFS_NT40_ENV
1192     (void)_putenv("TMP=");      /* If "TMP" is set, then that overrides tdir. */
1193     (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp."));
1194 #else
1195     (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1196                        "%s" OS_DIRSEP "salvage.temp.%d", tdir, getpid());
1197 #endif
1198     summaryFile = OS_OPEN(summaryFileName, O_RDWR|O_APPEND|O_CREAT, 0666);
1199     if (summaryFile == INVALID_FD) {
1200         Abort("Unable to create inode summary file\n");
1201     }
1202
1203 #ifdef AFS_NT40_ENV
1204     /* Using nt_unlink here since we're really using the delete on close
1205      * semantics of unlink. In most places in the salvager, we really do
1206      * mean to unlink the file at that point. Those places have been
1207      * modified to actually do that so that the NT crt can be used there.
1208      *
1209      * jaltman - As commented elsewhere, this cannot work because fopen()
1210      * does not open files with DELETE and FILE_SHARE_DELETE.
1211      */
1212     code = nt_unlink(summaryFileName);
1213 #else
1214     code = unlink(summaryFileName);
1215 #endif
1216     if (code < 0) {
1217         Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1218     }
1219
1220     if (!canfork || debug || Fork() == 0) {
1221         int nInodes = st_size / sizeof(struct ViceInodeInfo);
1222         if (nInodes == 0) {
1223             OS_CLOSE(summaryFile);
1224             if (!singleVolumeNumber)    /* Remove the FORCESALVAGE file */
1225                 RemoveTheForce(salvinfo->fileSysPath);
1226             else {
1227                 struct VolumeSummary *vsp;
1228                 int i;
1229
1230                 GetVolumeSummary(salvinfo, singleVolumeNumber);
1231
1232                 for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; i++) {
1233                     if (vsp->fileName)
1234                         DeleteExtraVolumeHeaderFile(salvinfo, vsp);
1235                 }
1236             }
1237             Log("%s vice inodes on %s; not salvaged\n",
1238                 singleVolumeNumber ? "No applicable" : "No", dev);
1239             return -1;
1240         }
1241         ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1242         if (ip == NULL) {
1243             OS_CLOSE(summaryFile);
1244             Abort
1245                 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1246                  dev);
1247         }
1248         if (OS_READ(salvinfo->inodeFd, ip, st_size) != st_size) {
1249             OS_CLOSE(summaryFile);
1250             Abort("Unable to read inode table; %s not salvaged\n", dev);
1251         }
1252         qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1253         if (OS_SEEK(salvinfo->inodeFd, 0, SEEK_SET) == -1
1254             || OS_WRITE(salvinfo->inodeFd, ip, st_size) != st_size) {
1255             OS_CLOSE(summaryFile);
1256             Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1257         }
1258         summary.index = 0;
1259         ip_save = ip;
1260         while (nInodes) {
1261             CountVolumeInodes(ip, nInodes, &summary);
1262             if (OS_WRITE(summaryFile, &summary, sizeof(summary)) != sizeof(summary)) {
1263                 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1264                 OS_CLOSE(summaryFile);
1265                 return -1;
1266             }
1267             summary.index += (summary.nInodes);
1268             nInodes -= summary.nInodes;
1269             ip += summary.nInodes;
1270         }
1271         free(ip_save);
1272         ip = ip_save = NULL;
1273         /* Following fflush is not fclose, because if it was debug mode would not work */
1274         if (OS_SYNC(summaryFile) == -1) {
1275             Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1276             OS_CLOSE(summaryFile);
1277             return -1;
1278         }
1279         if (canfork && !debug) {
1280             ShowLog = 0;
1281             Exit(0);
1282         }
1283     } else {
1284         if (Wait("Inode summary") == -1) {
1285             OS_CLOSE(summaryFile);
1286             Exit(1);            /* salvage of this partition aborted */
1287         }
1288     }
1289
1290     st_size = OS_SIZE(summaryFile);
1291     osi_Assert(st_size >= 0);
1292     if (st_size != 0) {
1293         int ret;
1294         salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_size);
1295         osi_Assert(salvinfo->inodeSummary != NULL);
1296         /* For GNU we need to do lseek to get the file pointer moved. */
1297         osi_Assert(OS_SEEK(summaryFile, 0, SEEK_SET) == 0);
1298         ret = OS_READ(summaryFile, salvinfo->inodeSummary, st_size);
1299         osi_Assert(ret == st_size);
1300     }
1301     salvinfo->nVolumesInInodeFile = st_size / sizeof(struct InodeSummary);
1302     for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
1303         salvinfo->inodeSummary[i].volSummary = NULL;
1304     }
1305     Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)st_size);
1306     OS_CLOSE(summaryFile);
1307     return 0;
1308 }
1309
1310 /* Comparison routine for volume sort.
1311    This is setup so that a read-write volume comes immediately before
1312    any read-only clones of that volume */
1313 int
1314 CompareVolumes(const void *_p1, const void *_p2)
1315 {
1316     const struct VolumeSummary *p1 = _p1;
1317     const struct VolumeSummary *p2 = _p2;
1318     if (p1->header.parent != p2->header.parent)
1319         return p1->header.parent < p2->header.parent ? -1 : 1;
1320     if (p1->header.id == p1->header.parent)     /* p1 is rw volume */
1321         return -1;
1322     if (p2->header.id == p2->header.parent)     /* p2 is rw volume */
1323         return 1;
1324     return p1->header.id < p2->header.id ? -1 : 1;      /* Both read-only */
1325 }
1326
1327 /**
1328  * Gleans volumeSummary information by asking the fileserver
1329  *
1330  * @param[in] singleVolumeNumber  the volume we're salvaging. 0 if we're
1331  *                                salvaging a whole partition
1332  *
1333  * @return whether we obtained the volume summary information or not
1334  *  @retval 0  success; we obtained the volume summary information
1335  *  @retval -1 we raced with a fileserver restart; volume locks and checkout
1336  *             must be retried
1337  *  @retval 1  we did not get the volume summary information; either the
1338  *             fileserver responded with an error, or we are not supposed to
1339  *             ask the fileserver for the information (e.g. we are salvaging
1340  *             the entire partition or we are not the salvageserver)
1341  *
1342  * @note for non-DAFS, always returns 1
1343  */
1344 static int
1345 AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1346 {
1347     afs_int32 code = 1;
1348 #if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
1349     if (programType == salvageServer) {
1350         if (singleVolumeNumber) {
1351             FSSYNC_VGQry_response_t q_res;
1352             SYNC_response res;
1353             struct VolumeSummary *vsp;
1354             int i;
1355             struct VolumeDiskHeader diskHdr;
1356
1357             memset(&res, 0, sizeof(res));
1358
1359             code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1360
1361             /*
1362              * We must wait for the partition to finish scanning before
1363              * can continue, since we will not know if we got the entire
1364              * VG membership unless the partition is fully scanned.
1365              * We could, in theory, just scan the partition ourselves if
1366              * the VG cache is not ready, but we would be doing the exact
1367              * same scan the fileserver is doing; it will almost always
1368              * be faster to wait for the fileserver. The only exceptions
1369              * are if the partition does not take very long to scan, and
1370              * in that case it's fast either way, so who cares?
1371              */
1372             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1373                 Log("waiting for fileserver to finish scanning partition %s...\n",
1374                     salvinfo->fileSysPartition->name);
1375
1376                 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1377                     /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1378                      * just so small partitions don't need to wait over 10
1379                      * seconds every time, and large partitions are generally
1380                      * polled only once every ten seconds. */
1381                     sleep((i > 10) ? (i = 10) : i);
1382
1383                     code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1384                 }
1385             }
1386
1387             if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1388                 /* This can happen if there's no header for the volume
1389                  * we're salvaging, or no headers exist for the VG (if
1390                  * we're salvaging an RW). Act as if we got a response
1391                  * with no VG members. The headers may be created during
1392                  * salvaging, if there are inodes in this VG. */
1393                 code = 0;
1394                 memset(&q_res, 0, sizeof(q_res));
1395                 q_res.rw = singleVolumeNumber;
1396             }
1397
1398             if (code) {
1399                 Log("fileserver refused VGCQuery request for volume %lu on "
1400                     "partition %s, code %ld reason %ld\n",
1401                     afs_printable_uint32_lu(singleVolumeNumber),
1402                     salvinfo->fileSysPartition->name,
1403                     afs_printable_int32_ld(code),
1404                     afs_printable_int32_ld(res.hdr.reason));
1405                 goto done;
1406             }
1407
1408             if (q_res.rw != singleVolumeNumber) {
1409                 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1410                     afs_printable_uint32_lu(singleVolumeNumber),
1411                     afs_printable_uint32_lu(q_res.rw));
1412 #ifdef SALVSYNC_BUILD_CLIENT
1413                 if (SALVSYNC_LinkVolume(q_res.rw,
1414                                        singleVolumeNumber,
1415                                        salvinfo->fileSysPartition->name,
1416                                        NULL) != SYNC_OK) {
1417                     Log("schedule request failed\n");
1418                 }
1419 #endif /* SALVSYNC_BUILD_CLIENT */
1420                 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1421             }
1422
1423             salvinfo->volumeSummaryp = calloc(VOL_VG_MAX_VOLS, sizeof(struct VolumeSummary));
1424             osi_Assert(salvinfo->volumeSummaryp != NULL);
1425
1426             salvinfo->nVolumes = 0;
1427             vsp = salvinfo->volumeSummaryp;
1428
1429             for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1430                 char name[VMAXPATHLEN];
1431
1432                 if (!q_res.children[i]) {
1433                     continue;
1434                 }
1435
1436                 /* AskOffline for singleVolumeNumber was called much earlier */
1437                 if (q_res.children[i] != singleVolumeNumber) {
1438                     AskOffline(salvinfo, q_res.children[i]);
1439                     if (LockVolume(salvinfo, q_res.children[i])) {
1440                         /* need to retry */
1441                         return -1;
1442                     }
1443                 }
1444
1445                 code = VReadVolumeDiskHeader(q_res.children[i], salvinfo->fileSysPartition, &diskHdr);
1446                 if (code) {
1447                     Log("Cannot read header for %lu; trying to salvage group anyway\n",
1448                         afs_printable_uint32_lu(q_res.children[i]));
1449                     code = 0;
1450                     continue;
1451                 }
1452
1453                 DiskToVolumeHeader(&vsp->header, &diskHdr);
1454                 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1455                 vsp->fileName = ToString(name);
1456                 salvinfo->nVolumes++;
1457                 vsp++;
1458             }
1459
1460             qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1461                   CompareVolumes);
1462         }
1463       done:
1464         if (code) {
1465             Log("Cannot get volume summary from fileserver; falling back to scanning "
1466                 "entire partition\n");
1467         }
1468     }
1469 #endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
1470     return code;
1471 }
1472
1473 /**
1474  * count how many volume headers are found by VWalkVolumeHeaders.
1475  *
1476  * @param[in] dp   the disk partition (unused)
1477  * @param[in] name full path to the .vol header (unused)
1478  * @param[in] hdr  the header data (unused)
1479  * @param[in] last whether this is the last try or not (unused)
1480  * @param[in] rock actually an afs_int32*; the running count of how many
1481  *                 volumes we have found
1482  *
1483  * @retval 0 always
1484  */
1485 static int
1486 CountHeader(struct DiskPartition64 *dp, const char *name,
1487             struct VolumeDiskHeader *hdr, int last, void *rock)
1488 {
1489     afs_int32 *nvols = (afs_int32 *)rock;
1490     (*nvols)++;
1491     return 0;
1492 }
1493
1494 /**
1495  * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1496  * data.
1497  */
1498 struct SalvageScanParams {
1499     VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1500                                   * vol id of the VG we're salvaging */
1501     struct VolumeSummary *vsp;   /**< ptr to the current volume summary object
1502                                   * we're filling in */
1503     afs_int32 nVolumes;          /**< # of vols we've encountered */
1504     afs_int32 totalVolumes;      /**< max # of vols we should encounter (the
1505                                   * # of vols we've alloc'd memory for) */
1506     int retry;  /**< do we need to retry vol lock/checkout? */
1507     struct SalvInfo *salvinfo; /**< salvage job info */
1508 };
1509
1510 /**
1511  * records volume summary info found from VWalkVolumeHeaders.
1512  *
1513  * Found volumes are also taken offline if they are in the specific volume
1514  * group we are looking for.
1515  *
1516  * @param[in] dp   the disk partition
1517  * @param[in] name full path to the .vol header
1518  * @param[in] hdr  the header data
1519  * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1520  * @param[in] rock actually a struct SalvageScanParams*, containing the
1521  *                 information needed to record the volume summary data
1522  *
1523  * @return operation status
1524  *  @retval 0  success
1525  *  @retval -1 volume locking raced with fileserver restart; checking out
1526  *             and locking volumes needs to be retried
1527  *  @retval 1  volume header is mis-named and should be deleted
1528  */
1529 static int
1530 RecordHeader(struct DiskPartition64 *dp, const char *name,
1531              struct VolumeDiskHeader *hdr, int last, void *rock)
1532 {
1533     char nameShouldBe[64];
1534     struct SalvageScanParams *params;
1535     struct VolumeSummary summary;
1536     VolumeId singleVolumeNumber;
1537     struct SalvInfo *salvinfo;
1538
1539     params = (struct SalvageScanParams *)rock;
1540
1541     singleVolumeNumber = params->singleVolumeNumber;
1542     salvinfo = params->salvinfo;
1543
1544     DiskToVolumeHeader(&summary.header, hdr);
1545
1546     if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1547         && summary.header.parent != singleVolumeNumber) {
1548
1549         if (programType == salvageServer) {
1550 #ifdef SALVSYNC_BUILD_CLIENT
1551             Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1552                 summary.header.id, summary.header.parent);
1553             if (SALVSYNC_LinkVolume(summary.header.parent,
1554                                     summary.header.id,
1555                                     dp->name,
1556                                     NULL) != SYNC_OK) {
1557                 Log("schedule request failed\n");
1558             }
1559 #endif
1560             Exit(SALSRV_EXIT_VOLGROUP_LINK);
1561
1562         } else {
1563             Log("%u is a read-only volume; not salvaged\n",
1564                 singleVolumeNumber);
1565             Exit(1);
1566         }
1567     }
1568
1569     if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1570         || summary.header.parent == singleVolumeNumber) {
1571
1572         /* check if the header file is incorrectly named */
1573         int badname = 0;
1574         const char *base = strrchr(name, OS_DIRSEPC);
1575         if (base) {
1576             base++;
1577         } else {
1578             base = name;
1579         }
1580
1581         (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1582                            VFORMAT, afs_printable_uint32_lu(summary.header.id));
1583
1584
1585         if (strcmp(nameShouldBe, base)) {
1586             /* .vol file has wrong name; retry/delete */
1587             badname = 1;
1588         }
1589
1590         if (!badname || last) {
1591             /* only offline the volume if the header is good, or if this is
1592              * the last try looking at it; avoid AskOffline'ing the same vol
1593              * multiple times */
1594
1595             if (singleVolumeNumber
1596                 && summary.header.id != singleVolumeNumber) {
1597                 /* don't offline singleVolumeNumber; we already did that
1598                  * earlier */
1599
1600                 AskOffline(salvinfo, summary.header.id);
1601
1602 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
1603                 if (!badname) {
1604                     /* don't lock the volume if the header is bad, since we're
1605                      * about to delete it anyway. */
1606                     if (LockVolume(salvinfo, summary.header.id)) {
1607                         params->retry = 1;
1608                         return -1;
1609                     }
1610                 }
1611 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
1612             }
1613         }
1614         if (badname) {
1615             if (last && !Showmode) {
1616                 Log("Volume header file %s is incorrectly named (should be %s "
1617                     "not %s); %sdeleted (it will be recreated later, if "
1618                     "necessary)\n", name, nameShouldBe, base,
1619                     (Testing ? "it would have been " : ""));
1620             }
1621             return 1;
1622         }
1623
1624         summary.fileName = ToString(base);
1625         params->nVolumes++;
1626
1627         if (params->nVolumes > params->totalVolumes) {
1628             /* We found more volumes than we found on the first partition walk;
1629              * apparently something created a volume while we were
1630              * partition-salvaging, or we found more than 20 vols when salvaging a
1631              * particular volume. Abort if we detect this, since other programs
1632              * supposed to not touch the partition while it is partition-salvaging,
1633              * and we shouldn't find more than 20 vols in a VG.
1634              */
1635             Abort("Found %ld vol headers, but should have found at most %ld! "
1636                   "Make sure the volserver/fileserver are not running at the "
1637                   "same time as a partition salvage\n",
1638                   afs_printable_int32_ld(params->nVolumes),
1639                   afs_printable_int32_ld(params->totalVolumes));
1640         }
1641
1642         memcpy(params->vsp, &summary, sizeof(summary));
1643         params->vsp++;
1644     }
1645
1646     return 0;
1647 }
1648
1649 /**
1650  * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1651  *
1652  * If the header could not be read in at all, the header is always unlinked.
1653  * If instead RecordHeader said the header was bad (that is, the header file
1654  * is mis-named), we only unlink if we are doing a partition salvage, as
1655  * opposed to salvaging a specific volume group.
1656  *
1657  * @param[in] dp   the disk partition
1658  * @param[in] name full path to the .vol header
1659  * @param[in] hdr  header data, or NULL if the header could not be read
1660  * @param[in] rock actually a struct SalvageScanParams*, with some information
1661  *                 about the scan
1662  */
1663 static void
1664 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1665              struct VolumeDiskHeader *hdr, void *rock)
1666 {
1667     struct SalvageScanParams *params;
1668     int dounlink = 0;
1669
1670     params = (struct SalvageScanParams *)rock;
1671
1672     if (!hdr) {
1673         /* no header; header is too bogus to read in at all */
1674         if (!Showmode) {
1675             Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1676         }
1677         if (!Testing) {
1678             dounlink = 1;
1679         }
1680
1681     } else if (!params->singleVolumeNumber) {
1682         /* We were able to read in a header, but RecordHeader said something
1683          * was wrong with it. We only unlink those if we are doing a partition
1684          * salvage. */
1685         if (!Testing) {
1686             dounlink = 1;
1687         }
1688     }
1689
1690     if (dounlink && unlink(name)) {
1691         Log("Error %d while trying to unlink %s\n", errno, name);
1692     }
1693 }
1694
1695 /**
1696  * Populates salvinfo->volumeSummaryp with volume summary information, either by asking
1697  * the fileserver for VG information, or by scanning the /vicepX partition.
1698  *
1699  * @param[in] singleVolumeNumber  the volume ID of the single volume group we
1700  *                                are salvaging, or 0 if this is a partition
1701  *                                salvage
1702  *
1703  * @return operation status
1704  *  @retval 0  success
1705  *  @retval -1 we raced with a fileserver restart; checking out and locking
1706  *             volumes must be retried
1707  */
1708 int
1709 GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1710 {
1711     afs_int32 nvols = 0;
1712     struct SalvageScanParams params;
1713     int code;
1714
1715     code = AskVolumeSummary(salvinfo, singleVolumeNumber);
1716     if (code == 0) {
1717         /* we successfully got the vol information from the fileserver; no
1718          * need to scan the partition */
1719         return 0;
1720     }
1721     if (code < 0) {
1722         /* we need to retry volume checkout */
1723         return code;
1724     }
1725
1726     if (!singleVolumeNumber) {
1727         /* Count how many volumes we have in /vicepX */
1728         code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, CountHeader,
1729                                   NULL, &nvols);
1730         if (code < 0) {
1731             Abort("Can't read directory %s; not salvaged\n", salvinfo->fileSysPath);
1732         }
1733         if (!nvols)
1734             nvols = 1;
1735     } else {
1736         nvols = VOL_VG_MAX_VOLS;
1737     }
1738
1739     salvinfo->volumeSummaryp = calloc(nvols, sizeof(struct VolumeSummary));
1740     osi_Assert(salvinfo->volumeSummaryp != NULL);
1741
1742     params.singleVolumeNumber = singleVolumeNumber;
1743     params.vsp = salvinfo->volumeSummaryp;
1744     params.nVolumes = 0;
1745     params.totalVolumes = nvols;
1746     params.retry = 0;
1747     params.salvinfo = salvinfo;
1748
1749     /* walk the partition directory of volume headers and record the info
1750      * about them; unlinking invalid headers */
1751     code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, RecordHeader,
1752                               UnlinkHeader, &params);
1753     if (params.retry) {
1754         /* we apparently need to retry checking-out/locking volumes */
1755         return -1;
1756     }
1757     if (code < 0) {
1758         Abort("Failed to get volume header summary\n");
1759     }
1760     salvinfo->nVolumes = params.nVolumes;
1761
1762     qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1763           CompareVolumes);
1764
1765     return 0;
1766 }
1767
1768 /* Find the link table. This should be associated with the RW volume or, if
1769  * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1770  */
1771 Inode
1772 FindLinkHandle(struct InodeSummary *isp, int nVols,
1773                struct ViceInodeInfo *allInodes)
1774 {
1775     int i, j;
1776     struct ViceInodeInfo *ip;
1777
1778     for (i = 0; i < nVols; i++) {
1779         ip = allInodes + isp[i].index;
1780         for (j = 0; j < isp[i].nSpecialInodes; j++) {
1781             if (ip[j].u.special.type == VI_LINKTABLE)
1782                 return ip[j].inodeNumber;
1783         }
1784     }
1785     return (Inode) - 1;
1786 }
1787
1788 int
1789 CreateLinkTable(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
1790 {
1791     struct versionStamp version;
1792     FdHandle_t *fdP;
1793
1794     if (!VALID_INO(ino))
1795         ino =
1796             IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
1797                       INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1798     if (!VALID_INO(ino))
1799         Abort
1800             ("Unable to allocate link table inode for volume %u (error = %d)\n",
1801              isp->RWvolumeId, errno);
1802     IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
1803     fdP = IH_OPEN(salvinfo->VGLinkH);
1804     if (fdP == NULL)
1805         Abort("Can't open link table for volume %u (error = %d)\n",
1806               isp->RWvolumeId, errno);
1807
1808     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1809         Abort("Can't truncate link table for volume %u (error = %d)\n",
1810               isp->RWvolumeId, errno);
1811
1812     version.magic = LINKTABLEMAGIC;
1813     version.version = LINKTABLEVERSION;
1814
1815     if (FDH_PWRITE(fdP, (char *)&version, sizeof(version), 0)
1816         != sizeof(version))
1817         Abort("Can't truncate link table for volume %u (error = %d)\n",
1818               isp->RWvolumeId, errno);
1819
1820     FDH_REALLYCLOSE(fdP);
1821
1822     /* If the volume summary exits (i.e.,  the V*.vol header file exists),
1823      * then set this inode there as well.
1824      */
1825     if (isp->volSummary)
1826         isp->volSummary->header.linkTable = ino;
1827
1828     return 0;
1829 }
1830
1831 #ifdef AFS_NT40_ENV
1832 void *
1833 nt_SVG(void *arg)
1834 {
1835     SVGParms_t *parms = (SVGParms_t *) arg;
1836     DoSalvageVolumeGroup(parms->svgp_salvinfo, parms->svgp_inodeSummaryp, parms->svgp_count);
1837     return NULL;
1838 }
1839
1840 void
1841 SalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
1842 {
1843     pthread_t tid;
1844     pthread_attr_t tattr;
1845     int code;
1846     SVGParms_t parms;
1847
1848     /* Initialize per volume global variables, even if later code does so */
1849     salvinfo->VolumeChanged = 0;
1850     salvinfo->VGLinkH = NULL;
1851     salvinfo->VGLinkH_cnt = 0;
1852     memset(&salvinfo->VolInfo, 0, sizeof(salvinfo->VolInfo));
1853
1854     parms.svgp_inodeSummaryp = isp;
1855     parms.svgp_count = nVols;
1856     parms.svgp_salvinfo = salvinfo;
1857     code = pthread_attr_init(&tattr);
1858     if (code) {
1859         Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1860             isp->RWvolumeId);
1861         return;
1862     }
1863     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1864     if (code) {
1865         Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1866         return;
1867     }
1868     code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1869     if (code) {
1870         Log("Failed to create thread to salvage volume group %u\n",
1871             isp->RWvolumeId);
1872         return;
1873     }
1874     (void)pthread_join(tid, NULL);
1875 }
1876 #endif /* AFS_NT40_ENV */
1877
1878 void
1879 DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
1880 {
1881     struct ViceInodeInfo *inodes, *allInodes, *ip;
1882     int i, totalInodes, size, salvageTo;
1883     int haveRWvolume;
1884     int check;
1885     Inode ino;
1886     int dec_VGLinkH = 0;
1887     int VGLinkH_p1 =0;
1888     FdHandle_t *fdP = NULL;
1889
1890     salvinfo->VGLinkH_cnt = 0;
1891     haveRWvolume = (isp->volumeId == isp->RWvolumeId
1892                     && isp->nSpecialInodes > 0);
1893     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1894         if (!ForceSalvage && QuickCheck(salvinfo, isp, nVols))
1895             return;
1896     }
1897     if (ShowMounts && !haveRWvolume)
1898         return;
1899     if (canfork && !debug && Fork() != 0) {
1900         (void)Wait("Salvage volume group");
1901         return;
1902     }
1903     for (i = 0, totalInodes = 0; i < nVols; i++)
1904         totalInodes += isp[i].nInodes;
1905     size = totalInodes * sizeof(struct ViceInodeInfo);
1906     inodes = (struct ViceInodeInfo *)malloc(size);
1907     allInodes = inodes - isp->index;    /* this would the base of all the inodes
1908                                          * for the partition, if all the inodes
1909                                          * had been read into memory */
1910     osi_Assert(OS_SEEK
1911            (salvinfo->inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1912             SEEK_SET) != -1);
1913     osi_Assert(OS_READ(salvinfo->inodeFd, inodes, size) == size);
1914
1915     /* Don't try to salvage a read write volume if there isn't one on this
1916      * partition */
1917     salvageTo = haveRWvolume ? 0 : 1;
1918
1919 #ifdef AFS_NAMEI_ENV
1920     ino = FindLinkHandle(isp, nVols, allInodes);
1921     if (VALID_INO(ino)) {
1922         IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
1923         fdP = IH_OPEN(salvinfo->VGLinkH);
1924     }
1925     if (!VALID_INO(ino) || fdP == NULL) {
1926         Log("%s link table for volume %u.\n",
1927             Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1928         if (Testing) {
1929             IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
1930         } else {
1931             int i, j;
1932             struct ViceInodeInfo *ip;
1933             CreateLinkTable(salvinfo, isp, ino);
1934             fdP = IH_OPEN(salvinfo->VGLinkH);
1935             /* Sync fake 1 link counts to the link table, now that it exists */
1936             if (fdP) {
1937                 for (i = 0; i < nVols; i++) {
1938                         ip = allInodes + isp[i].index;
1939                          for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1940                                  namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1941                     }
1942                 }
1943             }
1944         }
1945     }
1946     if (fdP)
1947         FDH_REALLYCLOSE(fdP);
1948 #else
1949     IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
1950 #endif
1951
1952     /* Salvage in reverse order--read/write volume last; this way any
1953      * Inodes not referenced by the time we salvage the read/write volume
1954      * can be picked up by the read/write volume */
1955     /* ACTUALLY, that's not done right now--the inodes just vanish */
1956     for (i = nVols - 1; i >= salvageTo; i--) {
1957         int rw = (i == 0);
1958         struct InodeSummary *lisp = &isp[i];
1959 #ifdef AFS_NAMEI_ENV
1960         /* If only the RO is present on this partition, the link table
1961          * shows up as a RW volume special file. Need to make sure the
1962          * salvager doesn't try to salvage the non-existent RW.
1963          */
1964         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1965             /* If this only special inode is the link table, continue */
1966             if (inodes->u.special.type == VI_LINKTABLE) {
1967                 haveRWvolume = 0;
1968                 continue;
1969             }
1970         }
1971 #endif
1972         if (!Showmode)
1973             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1974                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1975         /* Check inodes twice.  The second time do things seriously.  This
1976          * way the whole RO volume can be deleted, below, if anything goes wrong */
1977         for (check = 1; check >= 0; check--) {
1978             int deleteMe;
1979             if (SalvageVolumeHeaderFile(salvinfo, lisp, allInodes, rw, check, &deleteMe)
1980                 == -1) {
1981                 MaybeZapVolume(salvinfo, lisp, "Volume header", deleteMe, check);
1982                 if (rw && deleteMe) {
1983                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
1984                                          * volume won't be called */
1985                     break;
1986                 }
1987                 if (!rw)
1988                     break;
1989             }
1990             if (rw && check == 1)
1991                 continue;
1992             if (SalvageVnodes(salvinfo, isp, lisp, allInodes, check) == -1) {
1993                 MaybeZapVolume(salvinfo, lisp, "Vnode index", 0, check);
1994                 break;
1995             }
1996         }
1997     }
1998
1999     /* Fix actual inode counts */
2000     if (!Showmode) {
2001         afs_ino_str_t stmp;
2002         Log("totalInodes %d\n",totalInodes);
2003         for (ip = inodes; totalInodes; ip++, totalInodes--) {
2004             static int TraceBadLinkCounts = 0;
2005 #ifdef AFS_NAMEI_ENV
2006             if (salvinfo->VGLinkH->ih_ino == ip->inodeNumber) {
2007                 dec_VGLinkH = ip->linkCount - salvinfo->VGLinkH_cnt;
2008                 VGLinkH_p1 = ip->u.param[0];
2009                 continue;       /* Deal with this last. */
2010             }
2011 #endif
2012             if (ip->linkCount != 0 && TraceBadLinkCounts) {
2013                 TraceBadLinkCounts--;   /* Limit reports, per volume */
2014                 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]);
2015             }
2016             while (ip->linkCount > 0) {
2017                 /* below used to assert, not break */
2018                 if (!Testing) {
2019                     if (IH_DEC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2020                         Log("idec failed. inode %s errno %d\n",
2021                             PrintInode(stmp, ip->inodeNumber), errno);
2022                         break;
2023                     }
2024                 }
2025                 ip->linkCount--;
2026             }
2027             while (ip->linkCount < 0) {
2028                 /* these used to be asserts */
2029                 if (!Testing) {
2030                     if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2031                         Log("iinc failed. inode %s errno %d\n",
2032                             PrintInode(stmp, ip->inodeNumber), errno);
2033                         break;
2034                     }
2035                 }
2036                 ip->linkCount++;
2037             }
2038         }
2039 #ifdef AFS_NAMEI_ENV
2040         while (dec_VGLinkH > 0) {
2041             if (IH_DEC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2042                 Log("idec failed on link table, errno = %d\n", errno);
2043             }
2044             dec_VGLinkH--;
2045         }
2046         while (dec_VGLinkH < 0) {
2047             if (IH_INC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2048                 Log("iinc failed on link table, errno = %d\n", errno);
2049             }
2050             dec_VGLinkH++;
2051         }
2052 #endif
2053     }
2054     free(inodes);
2055     /* Directory consistency checks on the rw volume */
2056     if (haveRWvolume)
2057         SalvageVolume(salvinfo, isp, salvinfo->VGLinkH);
2058     IH_RELEASE(salvinfo->VGLinkH);
2059
2060     if (canfork && !debug) {
2061         ShowLog = 0;
2062         Exit(0);
2063     }
2064 }
2065
2066 int
2067 QuickCheck(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
2068 {
2069     /* Check headers BEFORE forking */
2070     int i;
2071     IHandle_t *h;
2072
2073     for (i = 0; i < nVols; i++) {
2074         struct VolumeSummary *vs = isp[i].volSummary;
2075         VolumeDiskData volHeader;
2076         if (!vs) {
2077             /* Don't salvage just because phantom rw volume is there... */
2078             /* (If a read-only volume exists, read/write inodes must also exist) */
2079             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2080                 continue;
2081             return 0;
2082         }
2083         IH_INIT(h, salvinfo->fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2084         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2085             == sizeof(volHeader)
2086             && volHeader.stamp.magic == VOLUMEINFOMAGIC
2087             && volHeader.dontSalvage == DONT_SALVAGE
2088             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2089             if (volHeader.inUse != 0) {
2090                 volHeader.inUse = 0;
2091                 volHeader.inService = 1;
2092                 if (!Testing) {
2093                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2094                         != sizeof(volHeader)) {
2095                         IH_RELEASE(h);
2096                         return 0;
2097                     }
2098                 }
2099             }
2100             IH_RELEASE(h);
2101         } else {
2102             IH_RELEASE(h);
2103             return 0;
2104         }
2105     }
2106     return 1;
2107 }
2108
2109
2110 /* SalvageVolumeHeaderFile
2111  *
2112  * Salvage the top level V*.vol header file. Make sure the special files
2113  * exist and that there are no duplicates.
2114  *
2115  * Calls SalvageHeader for each possible type of volume special file.
2116  */
2117
2118 int
2119 SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
2120                         struct ViceInodeInfo *inodes, int RW,
2121                         int check, int *deleteMe)
2122 {
2123     int i;
2124     struct ViceInodeInfo *ip;
2125     int allinodesobsolete = 1;
2126     struct VolumeDiskHeader diskHeader;
2127     afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
2128     int *skip;
2129     struct VolumeHeader tempHeader;
2130     struct afs_inode_info stuff[MAXINODETYPE];
2131
2132     /* keeps track of special inodes that are probably 'good'; they are
2133      * referenced in the vol header, and are included in the given inodes
2134      * array */
2135     struct {
2136         int valid;
2137         Inode inode;
2138     } goodspecial[MAXINODETYPE];
2139
2140     if (deleteMe)
2141         *deleteMe = 0;
2142
2143     memset(goodspecial, 0, sizeof(goodspecial));
2144
2145     skip = malloc(isp->nSpecialInodes * sizeof(*skip));
2146     if (skip) {
2147         memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
2148     } else {
2149         Log("cannot allocate memory for inode skip array when salvaging "
2150             "volume %lu; not performing duplicate special inode recovery\n",
2151             afs_printable_uint32_lu(isp->volumeId));
2152         /* still try to perform the salvage; the skip array only does anything
2153          * if we detect duplicate special inodes */
2154     }
2155
2156     init_inode_info(&tempHeader, stuff);
2157
2158     /*
2159      * First, look at the special inodes and see if any are referenced by
2160      * the existing volume header. If we find duplicate special inodes, we
2161      * can use this information to use the referenced inode (it's more
2162      * likely to be the 'good' one), and throw away the duplicates.
2163      */
2164     if (isp->volSummary && skip) {
2165         /* use tempHeader, so we can use the stuff[] array to easily index
2166          * into the isp->volSummary special inodes */
2167         memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2168
2169         for (i = 0; i < isp->nSpecialInodes; i++) {
2170             ip = &inodes[isp->index + i];
2171             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2172                 /* will get taken care of in a later loop */
2173                 continue;
2174             }
2175             if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2176                 goodspecial[ip->u.special.type-1].valid = 1;
2177                 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2178             }
2179         }
2180     }
2181
2182     memset(&tempHeader, 0, sizeof(tempHeader));
2183     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2184     tempHeader.stamp.version = VOLUMEHEADERVERSION;
2185     tempHeader.id = isp->volumeId;
2186     tempHeader.parent = isp->RWvolumeId;
2187
2188     /* Check for duplicates (inodes are sorted by type field) */
2189     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2190         ip = &inodes[isp->index + i];
2191         if (ip->u.special.type == (ip + 1)->u.special.type) {
2192             afs_ino_str_t stmp1, stmp2;
2193
2194             if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2195                 /* Will be caught in the loop below */
2196                 continue;
2197             }
2198             if (!Showmode) {
2199                 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2200                     ip->u.special.type, isp->volumeId,
2201                     PrintInode(stmp1, ip->inodeNumber),
2202                     PrintInode(stmp2, (ip+1)->inodeNumber));
2203             }
2204             if (skip && goodspecial[ip->u.special.type-1].valid) {
2205                 Inode gi = goodspecial[ip->u.special.type-1].inode;
2206
2207                 if (!Showmode) {
2208                     Log("using special inode referenced by vol header (%s)\n",
2209                         PrintInode(stmp1, gi));
2210                 }
2211
2212                 /* the volume header references some special inode of
2213                  * this type in the inodes array; are we it? */
2214                 if (ip->inodeNumber != gi) {
2215                     skip[i] = 1;
2216                 } else if ((ip+1)->inodeNumber != gi) {
2217                     /* in case this is the last iteration; we need to
2218                      * make sure we check ip+1, too */
2219                     skip[i+1] = 1;
2220                 }
2221             } else {
2222                 if (!Showmode)
2223                     Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2224                 if (skip) {
2225                     free(skip);
2226                 }
2227                 return -1;
2228             }
2229         }
2230     }
2231     for (i = 0; i < isp->nSpecialInodes; i++) {
2232         afs_ino_str_t stmp;
2233         ip = &inodes[isp->index + i];
2234         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2235             if (check) {
2236                 Log("Rubbish header inode %s of type %d\n",
2237                     PrintInode(stmp, ip->inodeNumber),
2238                     ip->u.special.type);
2239                 if (skip) {
2240                     free(skip);
2241                 }
2242                 return -1;
2243             }
2244             Log("Rubbish header inode %s of type %d; deleted\n",
2245                 PrintInode(stmp, ip->inodeNumber),
2246                 ip->u.special.type);
2247         } else if (!stuff[ip->u.special.type - 1].obsolete) {
2248             if (skip && skip[i]) {
2249                 if (orphans == ORPH_REMOVE) {
2250                     Log("Removing orphan special inode %s of type %d\n",
2251                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2252                     continue;
2253                 } else {
2254                     Log("Ignoring orphan special inode %s of type %d\n",
2255                         PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2256                     /* fall through to the ip->linkCount--; line below */
2257                 }
2258             } else {
2259                 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2260                 allinodesobsolete = 0;
2261             }
2262             if (!check && ip->u.special.type != VI_LINKTABLE)
2263                 ip->linkCount--;        /* Keep the inode around */
2264         }
2265     }
2266     if (skip) {
2267         free(skip);
2268     }
2269     skip = NULL;
2270
2271     if (allinodesobsolete) {
2272         if (deleteMe)
2273             *deleteMe = 1;
2274         return -1;
2275     }
2276
2277     if (!check)
2278         salvinfo->VGLinkH_cnt++;                /* one for every header. */
2279
2280     if (!RW && !check && isp->volSummary) {
2281         ClearROInUseBit(isp->volSummary);
2282         return 0;
2283     }
2284
2285     for (i = 0; i < MAXINODETYPE; i++) {
2286         if (stuff[i].inodeType == VI_LINKTABLE) {
2287             /* Gross hack: SalvageHeader does a bcmp on the volume header.
2288              * And we may have recreated the link table earlier, so set the
2289              * RW header as well.
2290              */
2291             if (VALID_INO(salvinfo->VGLinkH->ih_ino)) {
2292                 *stuff[i].inode = salvinfo->VGLinkH->ih_ino;
2293             }
2294             continue;
2295         }
2296         if (SalvageHeader(salvinfo, &stuff[i], isp, check, deleteMe) == -1 && check)
2297             return -1;
2298     }
2299
2300     if (isp->volSummary == NULL) {
2301         char path[64];
2302         char headerName[64];
2303         (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2304         (void)afs_snprintf(path, sizeof path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, headerName);
2305         if (check) {
2306             Log("No header file for volume %u\n", isp->volumeId);
2307             return -1;
2308         }
2309         if (!Showmode)
2310             Log("No header file for volume %u; %screating %s\n",
2311                 isp->volumeId, (Testing ? "it would have been " : ""),
2312                 path);
2313         isp->volSummary = calloc(1, sizeof(struct VolumeSummary));
2314         isp->volSummary->fileName = ToString(headerName);
2315
2316         writefunc = VCreateVolumeDiskHeader;
2317     } else {
2318         char path[64];
2319         char headerName[64];
2320         /* hack: these two fields are obsolete... */
2321         isp->volSummary->header.volumeAcl = 0;
2322         isp->volSummary->header.volumeMountTable = 0;
2323
2324         if (memcmp
2325             (&isp->volSummary->header, &tempHeader,
2326              sizeof(struct VolumeHeader))) {
2327             /* We often remove the name before calling us, so we make a fake one up */
2328             if (isp->volSummary->fileName) {
2329                 strcpy(headerName, isp->volSummary->fileName);
2330             } else {
2331                 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2332                 isp->volSummary->fileName = ToString(headerName);
2333             }
2334             (void)afs_snprintf(path, sizeof path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, headerName);
2335
2336             Log("Header file %s is damaged or no longer valid%s\n", path,
2337                 (check ? "" : "; repairing"));
2338             if (check)
2339                 return -1;
2340
2341             writefunc = VWriteVolumeDiskHeader;
2342         }
2343     }
2344     if (writefunc) {
2345         memcpy(&isp->volSummary->header, &tempHeader,
2346                sizeof(struct VolumeHeader));
2347         if (Testing) {
2348             if (!Showmode)
2349                 Log("It would have written a new header file for volume %u\n",
2350                     isp->volumeId);
2351         } else {
2352             afs_int32 code;
2353             VolumeHeaderToDisk(&diskHeader, &tempHeader);
2354             code = (*writefunc)(&diskHeader, salvinfo->fileSysPartition);
2355             if (code) {
2356                 Log("Error %ld writing volume header file for volume %lu\n",
2357                     afs_printable_int32_ld(code),
2358                     afs_printable_uint32_lu(diskHeader.id));
2359                 return -1;
2360             }
2361         }
2362     }
2363     IH_INIT(isp->volSummary->volumeInfoHandle, salvinfo->fileSysDevice, isp->RWvolumeId,
2364             isp->volSummary->header.volumeInfo);
2365     return 0;
2366 }
2367
2368 int
2369 SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
2370               struct InodeSummary *isp, int check, int *deleteMe)
2371 {
2372     union {
2373         VolumeDiskData volumeInfo;
2374         struct versionStamp fileHeader;
2375     } header;
2376     IHandle_t *specH;
2377     int recreate = 0;
2378     ssize_t nBytes;
2379     FdHandle_t *fdP;
2380
2381     if (sp->obsolete)
2382         return 0;
2383 #ifndef AFS_NAMEI_ENV
2384     if (sp->inodeType == VI_LINKTABLE)
2385         return 0;
2386 #endif
2387     if (*(sp->inode) == 0) {
2388         if (check) {
2389             Log("Missing inode in volume header (%s)\n", sp->description);
2390             return -1;
2391         }
2392         if (!Showmode)
2393             Log("Missing inode in volume header (%s); %s\n", sp->description,
2394                 (Testing ? "it would have recreated it" : "recreating"));
2395         if (!Testing) {
2396             *(sp->inode) =
2397                 IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
2398                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2399             if (!VALID_INO(*(sp->inode)))
2400                 Abort
2401                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2402                      sp->description, errno);
2403         }
2404         recreate = 1;
2405     }
2406
2407     IH_INIT(specH, salvinfo->fileSysDevice, isp->RWvolumeId, *(sp->inode));
2408     fdP = IH_OPEN(specH);
2409     if (OKToZap && (fdP == NULL) && BadError(errno)) {
2410         /* bail out early and destroy the volume */
2411         if (!Showmode)
2412             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2413         if (deleteMe)
2414             *deleteMe = 1;
2415         IH_RELEASE(specH);
2416         return -1;
2417     }
2418     if (fdP == NULL)
2419         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2420               sp->description, errno);
2421
2422     if (!recreate
2423         && (FDH_PREAD(fdP, (char *)&header, sp->size, 0) != sp->size
2424             || header.fileHeader.magic != sp->stamp.magic)) {
2425         if (check) {
2426             Log("Part of the header (%s) is corrupted\n", sp->description);
2427             FDH_REALLYCLOSE(fdP);
2428             IH_RELEASE(specH);
2429             return -1;
2430         }
2431         Log("Part of the header (%s) is corrupted; recreating\n",
2432             sp->description);
2433         recreate = 1;
2434         /* header can be garbage; make sure we don't read garbage data from
2435          * it below */
2436         memset(&header, 0, sizeof(header));
2437     }
2438     if (sp->inodeType == VI_VOLINFO
2439         && header.volumeInfo.destroyMe == DESTROY_ME) {
2440         if (deleteMe)
2441             *deleteMe = 1;
2442         FDH_REALLYCLOSE(fdP);
2443         IH_RELEASE(specH);
2444         return -1;
2445     }
2446     if (recreate && !Testing) {
2447         if (check)
2448             Abort
2449                 ("Internal error: recreating volume header (%s) in check mode\n",
2450                  sp->description);
2451         nBytes = FDH_TRUNC(fdP, 0);
2452         if (nBytes == -1)
2453             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2454                   sp->description, errno);
2455
2456         /* The following code should be moved into vutil.c */
2457         if (sp->inodeType == VI_VOLINFO) {
2458             struct timeval tp;
2459             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2460             header.volumeInfo.stamp = sp->stamp;
2461             header.volumeInfo.id = isp->volumeId;
2462             header.volumeInfo.parentId = isp->RWvolumeId;
2463             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2464             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2465                 isp->volumeId, isp->volumeId);
2466             header.volumeInfo.inService = 0;
2467             header.volumeInfo.blessed = 0;
2468             /* The + 1000 is a hack in case there are any files out in venus caches */
2469             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2470             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
2471             header.volumeInfo.needsCallback = 0;
2472             gettimeofday(&tp, 0);
2473             header.volumeInfo.creationDate = tp.tv_sec;
2474             nBytes =
2475                 FDH_PWRITE(fdP, (char *)&header.volumeInfo,
2476                            sizeof(header.volumeInfo), 0);
2477             if (nBytes != sizeof(header.volumeInfo)) {
2478                 if (nBytes < 0)
2479                     Abort
2480                         ("Unable to write volume header file (%s) (errno = %d)\n",
2481                          sp->description, errno);
2482                 Abort("Unable to write entire volume header file (%s)\n",
2483                       sp->description);
2484             }
2485         } else {
2486             nBytes = FDH_PWRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp), 0);
2487             if (nBytes != sizeof(sp->stamp)) {
2488                 if (nBytes < 0)
2489                     Abort
2490                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2491                          sp->description, errno);
2492                 Abort
2493                     ("Unable to write entire version stamp in volume header file (%s)\n",
2494                      sp->description);
2495             }
2496         }
2497     }
2498     FDH_REALLYCLOSE(fdP);
2499     IH_RELEASE(specH);
2500     if (sp->inodeType == VI_VOLINFO) {
2501         salvinfo->VolInfo = header.volumeInfo;
2502         if (check) {
2503             char update[25];
2504
2505             if (salvinfo->VolInfo.updateDate) {
2506                 strcpy(update, TimeStamp(salvinfo->VolInfo.updateDate, 0));
2507                 if (!Showmode)
2508                     Log("%s (%u) %supdated %s\n", salvinfo->VolInfo.name,
2509                         salvinfo->VolInfo.id,
2510                         (Testing ? "it would have been " : ""), update);
2511             } else {
2512                 strcpy(update, TimeStamp(salvinfo->VolInfo.creationDate, 0));
2513                 if (!Showmode)
2514                     Log("%s (%u) not updated (created %s)\n",
2515                         salvinfo->VolInfo.name, salvinfo->VolInfo.id, update);
2516             }
2517
2518         }
2519     }
2520
2521     return 0;
2522 }
2523
2524 int
2525 SalvageVnodes(struct SalvInfo *salvinfo,
2526               struct InodeSummary *rwIsp,
2527               struct InodeSummary *thisIsp,
2528               struct ViceInodeInfo *inodes, int check)
2529 {
2530     int ilarge, ismall, ioffset, RW, nInodes;
2531     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
2532     if (Showmode)
2533         return 0;
2534     RW = (rwIsp == thisIsp);
2535     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2536     ismall =
2537         SalvageIndex(salvinfo, thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2538                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2539     if (check && ismall == -1)
2540         return -1;
2541     ilarge =
2542         SalvageIndex(salvinfo, thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2543                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2544     return (ilarge == 0 && ismall == 0 ? 0 : -1);
2545 }
2546
2547 int
2548 SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
2549              struct ViceInodeInfo *ip, int nInodes,
2550              struct VolumeSummary *volSummary, int check)
2551 {
2552     char buf[SIZEOF_LARGEDISKVNODE];
2553     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2554     int err = 0;
2555     StreamHandle_t *file;
2556     struct VnodeClassInfo *vcp;
2557     afs_sfsize_t size;
2558     afs_sfsize_t nVnodes;
2559     afs_fsize_t vnodeLength;
2560     int vnodeIndex;
2561     afs_ino_str_t stmp1, stmp2;
2562     IHandle_t *handle;
2563     FdHandle_t *fdP;
2564
2565     IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
2566     fdP = IH_OPEN(handle);
2567     osi_Assert(fdP != NULL);
2568     file = FDH_FDOPEN(fdP, "r+");
2569     osi_Assert(file != NULL);
2570     vcp = &VnodeClassInfo[class];
2571     size = OS_SIZE(fdP->fd_fd);
2572     osi_Assert(size != -1);
2573     nVnodes = (size / vcp->diskSize) - 1;
2574     if (nVnodes > 0) {
2575         osi_Assert((nVnodes + 1) * vcp->diskSize == size);
2576         osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
2577     } else {
2578         nVnodes = 0;
2579     }
2580     for (vnodeIndex = 0;
2581          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2582          nVnodes--, vnodeIndex++) {
2583         if (vnode->type != vNull) {
2584             int vnodeChanged = 0;
2585             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2586             if (VNDISK_GET_INO(vnode) == 0) {
2587                 if (RW) {
2588                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2589                     memset(vnode, 0, vcp->diskSize);
2590                     vnodeChanged = 1;
2591                 }
2592             } else {
2593                 if (vcp->magic != vnode->vnodeMagic) {
2594                     /* bad magic #, probably partially created vnode */
2595                     if (check) {
2596                        Log("Partially allocated vnode %d: bad magic (is %lx should be %lx)\n",
2597                            vnodeNumber, afs_printable_uint32_lu(vnode->vnodeMagic),
2598                            afs_printable_uint32_lu(vcp->magic));
2599                        memset(vnode, 0, vcp->diskSize);
2600                        err = -1;
2601                        goto zooks;
2602                     }
2603                     Log("Partially allocated vnode %d deleted.\n",
2604                         vnodeNumber);
2605                     memset(vnode, 0, vcp->diskSize);
2606                     vnodeChanged = 1;
2607                     goto vnodeDone;
2608                 }
2609                 /* ****** Should do a bit more salvage here:  e.g. make sure
2610                  * vnode type matches what it should be given the index */
2611                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2612 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2613  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2614  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2615  *                  }
2616  */
2617                     ip++;
2618                     nInodes--;
2619                 }
2620                 if (!RW) {
2621                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2622                         /* The following doesn't work, because the version number
2623                          * is not maintained correctly by the file server */
2624                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2625                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2626                          * break; */
2627                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2628                             break;
2629                         ip++;
2630                         nInodes--;
2631                     }
2632                 } else {
2633                     /* For RW volume, look for vnode with matching inode number;
2634                      * if no such match, take the first determined by our sort
2635                      * order */
2636                     struct ViceInodeInfo *lip = ip;
2637                     int lnInodes = nInodes;
2638                     while (lnInodes
2639                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2640                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2641                             ip = lip;
2642                             nInodes = lnInodes;
2643                             break;
2644                         }
2645                         lip++;
2646                         lnInodes--;
2647                     }
2648                 }
2649                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2650                     /* "Matching" inode */
2651                     if (RW) {
2652                         Unique vu, iu;
2653                         FileVersion vd, id;
2654                         vu = vnode->uniquifier;
2655                         iu = ip->u.vnode.vnodeUniquifier;
2656                         vd = vnode->dataVersion;
2657                         id = ip->u.vnode.inodeDataVersion;
2658                         /*
2659                          * Because of the possibility of the uniquifier overflows (> 4M)
2660                          * we compare them modulo the low 22-bits; we shouldn't worry
2661                          * about mismatching since they shouldn't to many old
2662                          * uniquifiers of the same vnode...
2663                          */
2664                         if (IUnique(vu) != IUnique(iu)) {
2665                             if (!Showmode) {
2666                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2667                             }
2668
2669                             vnode->uniquifier = iu;
2670 #ifdef  AFS_3DISPARES
2671                             vnode->dataVersion = (id >= vd ?
2672                                                   /* 90% of 2.1M */
2673                                                   ((id - vd) >
2674                                                    1887437 ? vd : id) :
2675                                                   /* 90% of 2.1M */
2676                                                   ((vd - id) >
2677                                                    1887437 ? id : vd));
2678 #else
2679 #if defined(AFS_SGI_EXMAG)
2680                             vnode->dataVersion = (id >= vd ?
2681                                                   /* 90% of 16M */
2682                                                   ((id - vd) >
2683                                                    15099494 ? vd : id) :
2684                                                   /* 90% of 16M */
2685                                                   ((vd - id) >
2686                                                    15099494 ? id : vd));
2687 #else
2688                             vnode->dataVersion = (id > vd ? id : vd);
2689 #endif /* AFS_SGI_EXMAG */
2690 #endif /* AFS_3DISPARES */
2691                             vnodeChanged = 1;
2692                         } else {
2693                             /* don't bother checking for vd > id any more, since
2694                              * partial file transfers always result in this state,
2695                              * and you can't do much else anyway (you've already
2696                              * found the best data you can) */
2697 #ifdef  AFS_3DISPARES
2698                             if (!vnodeIsDirectory(vnodeNumber)
2699                                 && ((vd < id && (id - vd) < 1887437)
2700                                     || ((vd > id && (vd - id) > 1887437)))) {
2701 #else
2702 #if defined(AFS_SGI_EXMAG)
2703                             if (!vnodeIsDirectory(vnodeNumber)
2704                                 && ((vd < id && (id - vd) < 15099494)
2705                                     || ((vd > id && (vd - id) > 15099494)))) {
2706 #else
2707                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2708 #endif /* AFS_SGI_EXMAG */
2709 #endif
2710                                 if (!Showmode)
2711                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2712                                 vnode->dataVersion = id;
2713                                 vnodeChanged = 1;
2714                             }
2715                         }
2716                     }
2717                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2718                         if (check) {
2719                             if (!Showmode) {
2720                                 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);
2721                             }
2722                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2723                             err = -1;
2724                             goto zooks;
2725                         }
2726                         if (!Showmode) {
2727                             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);
2728                         }
2729                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2730                         vnodeChanged = 1;
2731                     }
2732                     VNDISK_GET_LEN(vnodeLength, vnode);
2733                     if (ip->byteCount != vnodeLength) {
2734                         if (check) {
2735                             if (!Showmode)
2736                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2737                             err = -1;
2738                             goto zooks;
2739                         }
2740                         if (!Showmode)
2741                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2742                         VNDISK_SET_LEN(vnode, ip->byteCount);
2743                         vnodeChanged = 1;
2744                     }
2745                     if (!check)
2746                         ip->linkCount--;        /* Keep the inode around */
2747                     ip++;
2748                     nInodes--;
2749                 } else {        /* no matching inode */
2750                     afs_ino_str_t stmp;
2751                     if (VNDISK_GET_INO(vnode) != 0
2752                         || vnode->type == vDirectory) {
2753                         /* No matching inode--get rid of the vnode */
2754                         if (check) {
2755                             if (VNDISK_GET_INO(vnode)) {
2756                                 if (!Showmode) {
2757                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)));
2758                                 }
2759                             } else {
2760                                 if (!Showmode)
2761                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2762                             }
2763                             err = -1;
2764                             goto zooks;
2765                         }
2766                         if (VNDISK_GET_INO(vnode)) {
2767                             if (!Showmode) {
2768                                 time_t serverModifyTime = vnode->serverModifyTime;
2769                                 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));
2770                             }
2771                         } else {
2772                             if (!Showmode) {
2773                                 time_t serverModifyTime = vnode->serverModifyTime;
2774                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2775                             }
2776                         }
2777                         memset(vnode, 0, vcp->diskSize);
2778                         vnodeChanged = 1;
2779                     } else {
2780                         /* Should not reach here becuase we checked for
2781                          * (inodeNumber == 0) above. And where we zero the vnode,
2782                          * we also goto vnodeDone.
2783                          */
2784                     }
2785                 }
2786                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2787                     ip++;
2788                     nInodes--;
2789                 }
2790             }                   /* VNDISK_GET_INO(vnode) != 0 */
2791           vnodeDone:
2792             osi_Assert(!(vnodeChanged && check));
2793             if (vnodeChanged && !Testing) {
2794                 osi_Assert(IH_IWRITE
2795                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2796                         (char *)vnode, vcp->diskSize)
2797                        == vcp->diskSize);
2798                 salvinfo->VolumeChanged = 1;    /* For break call back */
2799             }
2800         }
2801     }
2802   zooks:
2803     STREAM_CLOSE(file);
2804     FDH_CLOSE(fdP);
2805     IH_RELEASE(handle);
2806     return err;
2807 }
2808
2809 struct VnodeEssence *
2810 CheckVnodeNumber(struct SalvInfo *salvinfo, VnodeId vnodeNumber)
2811 {
2812     VnodeClass class;
2813     struct VnodeInfo *vip;
2814     int offset;
2815
2816     class = vnodeIdToClass(vnodeNumber);
2817     vip = &salvinfo->vnodeInfo[class];
2818     offset = vnodeIdToBitNumber(vnodeNumber);
2819     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2820 }
2821
2822 void
2823 CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
2824 {
2825     /* Copy the directory unconditionally if we are going to change it:
2826      * not just if was cloned.
2827      */
2828     struct VnodeDiskObject vnode;
2829     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2830     Inode oldinode, newinode;
2831     afs_sfsize_t code;
2832
2833     if (dir->copied || Testing)
2834         return;
2835     DFlush();                   /* Well justified paranoia... */
2836
2837     code =
2838         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2839                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2840                  sizeof(vnode));
2841     osi_Assert(code == sizeof(vnode));
2842     oldinode = VNDISK_GET_INO(&vnode);
2843     /* Increment the version number by a whole lot to avoid problems with
2844      * clients that were promised new version numbers--but the file server
2845      * crashed before the versions were written to disk.
2846      */
2847     newinode =
2848         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2849                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2850                   200);
2851     osi_Assert(VALID_INO(newinode));
2852     osi_Assert(CopyInode(salvinfo->fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2853     vnode.cloned = 0;
2854     VNDISK_SET_INO(&vnode, newinode);
2855     code =
2856         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
2857                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2858                   sizeof(vnode));
2859     osi_Assert(code == sizeof(vnode));
2860
2861     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2862                         salvinfo->fileSysDevice, newinode,
2863                         &salvinfo->VolumeChanged);
2864     /* Don't delete the original inode right away, because the directory is
2865      * still being scanned.
2866      */
2867     dir->copied = 1;
2868 }
2869
2870 /*
2871  * This function should either successfully create a new dir, or give up
2872  * and leave things the way they were.  In particular, if it fails to write
2873  * the new dir properly, it should return w/o changing the reference to the
2874  * old dir.
2875  */
2876 void
2877 CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
2878 {
2879     struct VnodeDiskObject vnode;
2880     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2881     Inode oldinode, newinode;
2882     DirHandle newdir;
2883     FdHandle_t *fdP;
2884     afs_int32 code;
2885     afs_sfsize_t lcode;
2886     afs_int32 parentUnique = 1;
2887     struct VnodeEssence *vnodeEssence;
2888     afs_fsize_t length;
2889
2890     if (Testing)
2891         return;
2892     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2893     lcode =
2894         IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
2895                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2896                  sizeof(vnode));
2897     osi_Assert(lcode == sizeof(vnode));
2898     oldinode = VNDISK_GET_INO(&vnode);
2899     /* Increment the version number by a whole lot to avoid problems with
2900      * clients that were promised new version numbers--but the file server
2901      * crashed before the versions were written to disk.
2902      */
2903     newinode =
2904         IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
2905                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2906                   200);
2907     osi_Assert(VALID_INO(newinode));
2908     SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
2909                         &salvinfo->VolumeChanged);
2910
2911     /* Assign . and .. vnode numbers from dir and vnode.parent.
2912      * The uniquifier for . is in the vnode.
2913      * The uniquifier for .. might be set to a bogus value of 1 and
2914      * the salvager will later clean it up.
2915      */
2916     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(salvinfo, vnode.parent))) {
2917         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2918     }
2919     code =
2920         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2921                    vnode.uniquifier,
2922                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2923                    parentUnique);
2924     if (code == 0)
2925         code = DFlush();
2926     if (code) {
2927         /* didn't really build the new directory properly, let's just give up. */
2928         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2929         Log("Directory salvage returned code %d, continuing.\n", code);
2930         if (code) {
2931             Log("also failed to decrement link count on new inode");
2932         }
2933         osi_Assert(1 == 2);
2934     }
2935     Log("Checking the results of the directory salvage...\n");
2936     if (!DirOK(&newdir)) {
2937         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2938         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2939         osi_Assert(code == 0);
2940         osi_Assert(1 == 2);
2941     }
2942     vnode.cloned = 0;
2943     VNDISK_SET_INO(&vnode, newinode);
2944     length = Length(&newdir);
2945     VNDISK_SET_LEN(&vnode, length);
2946     lcode =
2947         IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
2948                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2949                   sizeof(vnode));
2950     osi_Assert(lcode == sizeof(vnode));
2951 #if 0
2952 #ifdef AFS_NT40_ENV
2953     nt_sync(salvinfo->fileSysDevice);
2954 #else
2955     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2956                                  * an open FD on the file itself to fsync.
2957                                  */
2958 #endif
2959 #else
2960     salvinfo->vnodeInfo[vLarge].handle->ih_synced = 1;
2961 #endif
2962     /* make sure old directory file is really closed */
2963     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2964     FDH_REALLYCLOSE(fdP);
2965
2966     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2967     osi_Assert(code == 0);
2968     dir->dirHandle = newdir;
2969 }
2970
2971 /**
2972  * arguments for JudgeEntry.
2973  */
2974 struct judgeEntry_params {
2975     struct DirSummary *dir;    /**< directory we're examining entries in */
2976     struct SalvInfo *salvinfo; /**< SalvInfo for the current salvage job */
2977 };
2978
2979 int
2980 JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
2981            afs_int32 unique)
2982 {
2983     struct judgeEntry_params *params = arock;
2984     struct DirSummary *dir = params->dir;
2985     struct SalvInfo *salvinfo = params->salvinfo;
2986     struct VnodeEssence *vnodeEssence;
2987     afs_int32 dirOrphaned, todelete;
2988
2989     dirOrphaned = IsVnodeOrphaned(salvinfo, dir->vnodeNumber);
2990
2991     vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
2992     if (vnodeEssence == NULL) {
2993         if (!Showmode) {
2994             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);
2995         }
2996         if (!Testing) {
2997             CopyOnWrite(salvinfo, dir);
2998             osi_Assert(Delete(&dir->dirHandle, name) == 0);
2999         }
3000         return 0;
3001     }
3002 #ifdef AFS_AIX_ENV
3003 #ifndef AFS_NAMEI_ENV
3004     /* On AIX machines, don't allow entries to point to inode 0. That is a special
3005      * mount inode for the partition. If this inode were deleted, it would crash
3006      * the machine.
3007      */
3008     if (vnodeEssence->InodeNumber == 0) {
3009         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"));
3010         if (!Testing) {
3011             CopyOnWrite(salvinfo, dir);
3012             osi_Assert(Delete(&dir->dirHandle, name) == 0);
3013         }
3014         return 0;
3015     }
3016 #endif
3017 #endif
3018
3019     if (!(vnodeNumber & 1) && !Showmode
3020         && !(vnodeEssence->count || vnodeEssence->unique
3021              || vnodeEssence->modeBits)) {
3022         Log("dir vnode %u: invalid entry: %s" OS_DIRSEP "%s (vnode %u, unique %u)%s\n",
3023             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
3024             vnodeNumber, unique,
3025             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
3026              ""));
3027         if (!unique) {
3028             if (!Testing) {
3029                 CopyOnWrite(salvinfo, dir);
3030                 osi_Assert(Delete(&dir->dirHandle, name) == 0);
3031             }
3032             return 0;
3033         }
3034     }
3035
3036     /* Check if the Uniquifiers match. If not, change the directory entry
3037      * so its unique matches the vnode unique. Delete if the unique is zero
3038      * or if the directory is orphaned.
3039      */
3040     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
3041         if (!vnodeEssence->unique
3042             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
3043             /* This is an orphaned directory. Don't delete the . or ..
3044              * entry. Otherwise, it will get created in the next
3045              * salvage and deleted again here. So Just skip it.
3046              */
3047             return 0;
3048         }
3049
3050         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
3051
3052         if (!Showmode) {
3053             Log("dir vnode %u: %s" OS_DIRSEP "%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
3054         }
3055         if (!Testing) {
3056             AFSFid fid;
3057             fid.Vnode = vnodeNumber;
3058             fid.Unique = vnodeEssence->unique;
3059             CopyOnWrite(salvinfo, dir);
3060             osi_Assert(Delete(&dir->dirHandle, name) == 0);
3061             if (!todelete)
3062                 osi_Assert(Create(&dir->dirHandle, name, &fid) == 0);
3063         }
3064         if (todelete)
3065             return 0;           /* no need to continue */
3066     }
3067
3068     if (strcmp(name, ".") == 0) {
3069         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
3070             AFSFid fid;
3071             if (!Showmode)
3072                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3073             if (!Testing) {
3074                 CopyOnWrite(salvinfo, dir);
3075                 osi_Assert(Delete(&dir->dirHandle, ".") == 0);
3076                 fid.Vnode = dir->vnodeNumber;
3077                 fid.Unique = dir->unique;
3078                 osi_Assert(Create(&dir->dirHandle, ".", &fid) == 0);
3079             }
3080
3081             vnodeNumber = fid.Vnode;    /* Get the new Essence */
3082             unique = fid.Unique;
3083             vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3084         }
3085         dir->haveDot = 1;
3086     } else if (strcmp(name, "..") == 0) {
3087         AFSFid pa;
3088         if (dir->parent) {
3089             struct VnodeEssence *dotdot;
3090             pa.Vnode = dir->parent;
3091             dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
3092             osi_Assert(dotdot != NULL); /* XXX Should not be assert */
3093             pa.Unique = dotdot->unique;
3094         } else {
3095             pa.Vnode = dir->vnodeNumber;
3096             pa.Unique = dir->unique;
3097         }
3098         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
3099             if (!Showmode)
3100                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3101             if (!Testing) {
3102                 CopyOnWrite(salvinfo, dir);
3103                 osi_Assert(Delete(&dir->dirHandle, "..") == 0);
3104                 osi_Assert(Create(&dir->dirHandle, "..", &pa) == 0);
3105             }
3106
3107             vnodeNumber = pa.Vnode;     /* Get the new Essence */
3108             unique = pa.Unique;
3109             vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3110         }
3111         dir->haveDotDot = 1;
3112     } else if (strncmp(name, ".__afs", 6) == 0) {
3113         if (!Showmode) {
3114             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);
3115         }
3116         if (!Testing) {
3117             CopyOnWrite(salvinfo, dir);
3118             osi_Assert(Delete(&dir->dirHandle, name) == 0);
3119         }
3120         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
3121         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
3122         return 0;
3123     } else {
3124         if (ShowSuid && (vnodeEssence->modeBits & 06000))
3125             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);
3126         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
3127             && !(vnodeEssence->modeBits & 0111)) {
3128             afs_sfsize_t nBytes;
3129             afs_sfsize_t size;
3130             char buf[1025];
3131             IHandle_t *ihP;
3132             FdHandle_t *fdP;
3133
3134             IH_INIT(ihP, salvinfo->fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3135                     vnodeEssence->InodeNumber);
3136             fdP = IH_OPEN(ihP);
3137             if (fdP == NULL) {
3138                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
3139                 IH_RELEASE(ihP);
3140                 return 0;
3141             }
3142             size = FDH_SIZE(fdP);
3143             if (size < 0) {
3144                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, (int)size, vnodeNumber);
3145                 FDH_REALLYCLOSE(fdP);
3146                 IH_RELEASE(ihP);
3147                 return 0;
3148             }
3149
3150             if (size > 1024)
3151                 size = 1024;
3152             nBytes = FDH_PREAD(fdP, buf, size, 0);
3153             if (nBytes == size) {
3154                 buf[size] = '\0';
3155                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
3156                     Log("Volume %u (%s) mount point %s" OS_DIRSEP "%s to '%s' invalid, %s to symbolic link\n",
3157                         dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
3158                         Testing ? "would convert" : "converted");
3159                     vnodeEssence->modeBits |= 0111;
3160                     vnodeEssence->changed = 1;
3161                 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s" OS_DIRSEP "%s to '%s'\n",
3162                     dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3163                     dir->name ? dir->name : "??", name, buf);
3164             } else {
3165                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3166                     dir->vname, vnodeNumber, (int)size, (int)nBytes);
3167             }
3168             FDH_REALLYCLOSE(fdP);
3169             IH_RELEASE(ihP);
3170         }
3171         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3172             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);
3173         if (vnodeIdToClass(vnodeNumber) == vLarge
3174             && vnodeEssence->name == NULL) {
3175             char *n;
3176             if ((n = (char *)malloc(strlen(name) + 1)))
3177                 strcpy(n, name);
3178             vnodeEssence->name = n;
3179         }
3180
3181         /* The directory entry points to the vnode. Check to see if the
3182          * vnode points back to the directory. If not, then let the
3183          * directory claim it (else it might end up orphaned). Vnodes
3184          * already claimed by another directory are deleted from this
3185          * directory: hardlinks to the same vnode are not allowed
3186          * from different directories.
3187          */
3188         if (vnodeEssence->parent != dir->vnodeNumber) {
3189             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3190                 /* Vnode does not point back to this directory.
3191                  * Orphaned dirs cannot claim a file (it may belong to
3192                  * another non-orphaned dir).
3193                  */
3194                 if (!Showmode) {
3195                     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);
3196                 }
3197                 vnodeEssence->parent = dir->vnodeNumber;
3198                 vnodeEssence->changed = 1;
3199             } else {
3200                 /* Vnode was claimed by another directory */
3201                 if (!Showmode) {
3202                     if (dirOrphaned) {
3203                         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 " : ""));
3204                     } else if (vnodeNumber == 1) {
3205                         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 " : ""));
3206                     } else {
3207                         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 " : ""));
3208                     }
3209                 }
3210                 if (!Testing) {
3211                     CopyOnWrite(salvinfo, dir);
3212                     osi_Assert(Delete(&dir->dirHandle, name) == 0);
3213                 }
3214                 return 0;
3215             }
3216         }
3217         /* This directory claims the vnode */
3218         vnodeEssence->claimed = 1;
3219     }
3220     vnodeEssence->count--;
3221     return 0;
3222 }
3223
3224 void
3225 DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
3226                    VnodeClass class, Inode ino, Unique * maxu)
3227 {
3228     struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
3229     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3230     char buf[SIZEOF_LARGEDISKVNODE];
3231     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3232     afs_sfsize_t size;
3233     StreamHandle_t *file;
3234     int vnodeIndex;
3235     int nVnodes;
3236     FdHandle_t *fdP;
3237
3238     IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
3239     fdP = IH_OPEN(vip->handle);
3240     osi_Assert(fdP != NULL);
3241     file = FDH_FDOPEN(fdP, "r+");
3242     osi_Assert(file != NULL);
3243     size = OS_SIZE(fdP->fd_fd);
3244     osi_Assert(size != -1);
3245     vip->nVnodes = (size / vcp->diskSize) - 1;
3246     if (vip->nVnodes > 0) {
3247         osi_Assert((vip->nVnodes + 1) * vcp->diskSize == size);
3248         osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
3249         osi_Assert((vip->vnodes = (struct VnodeEssence *)
3250                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3251         if (class == vLarge) {
3252             osi_Assert((vip->inodes = (Inode *)
3253                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3254         } else {
3255             vip->inodes = NULL;
3256         }
3257     } else {
3258         vip->nVnodes = 0;
3259         vip->vnodes = NULL;
3260         vip->inodes = NULL;
3261     }
3262     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3263     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3264          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3265          nVnodes--, vnodeIndex++) {
3266         if (vnode->type != vNull) {
3267             struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3268             afs_fsize_t vnodeLength;
3269             vip->nAllocatedVnodes++;
3270             vep->count = vnode->linkCount;
3271             VNDISK_GET_LEN(vnodeLength, vnode);
3272             vep->blockCount = nBlocks(vnodeLength);
3273             vip->volumeBlockCount += vep->blockCount;
3274             vep->parent = vnode->parent;
3275             vep->unique = vnode->uniquifier;
3276             if (*maxu < vnode->uniquifier)
3277                 *maxu = vnode->uniquifier;
3278             vep->modeBits = vnode->modeBits;
3279             vep->InodeNumber = VNDISK_GET_INO(vnode);
3280             vep->type = vnode->type;
3281             vep->author = vnode->author;
3282             vep->owner = vnode->owner;
3283             vep->group = vnode->group;
3284             if (vnode->type == vDirectory) {
3285                 if (class != vLarge) {
3286                     VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3287                     vip->nAllocatedVnodes--;
3288                     memset(vnode, 0, sizeof(vnode));
3289                     IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3290                               vnodeIndexOffset(vcp, vnodeNumber),
3291                               (char *)&vnode, sizeof(vnode));
3292                     salvinfo->VolumeChanged = 1;
3293                 } else
3294                     vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3295             }
3296         }
3297     }
3298     STREAM_CLOSE(file);
3299     FDH_CLOSE(fdP);
3300 }
3301
3302 static char *
3303 GetDirName(struct SalvInfo *salvinfo, VnodeId vnode, struct VnodeEssence *vp,
3304            char *path)
3305 {
3306     struct VnodeEssence *parentvp;
3307
3308     if (vnode == 1) {
3309         strcpy(path, ".");
3310         return path;
3311     }
3312     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(salvinfo, vp->parent))
3313         && GetDirName(salvinfo, vp->parent, parentvp, path)) {
3314         strcat(path, OS_DIRSEP);
3315         strcat(path, vp->name);
3316         return path;
3317     }
3318     return 0;
3319 }
3320
3321 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3322  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3323  */
3324 static int
3325 IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode)
3326 {
3327     struct VnodeEssence *vep;
3328
3329     if (vnode == 0)
3330         return (1);             /* Vnode zero does not exist */
3331     if (vnode == 1)
3332         return (0);             /* The root dir vnode is always claimed */
3333     vep = CheckVnodeNumber(salvinfo, vnode);    /* Get the vnode essence */
3334     if (!vep || !vep->claimed)
3335         return (1);             /* Vnode is not claimed - it is orphaned */
3336
3337     return (IsVnodeOrphaned(salvinfo, vep->parent));
3338 }
3339
3340 void
3341 SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
3342            struct VnodeInfo *dirVnodeInfo, IHandle_t * alinkH, int i,
3343            struct DirSummary *rootdir, int *rootdirfound)
3344 {
3345     static struct DirSummary dir;
3346     static struct DirHandle dirHandle;
3347     struct VnodeEssence *parent;
3348     static char path[MAXPATHLEN];
3349     int dirok, code;
3350
3351     if (dirVnodeInfo->vnodes[i].salvaged)
3352         return;                 /* already salvaged */
3353
3354     dir.rwVid = rwVid;
3355     dirVnodeInfo->vnodes[i].salvaged = 1;
3356
3357     if (dirVnodeInfo->inodes[i] == 0)
3358         return;                 /* Not allocated to a directory */
3359
3360     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3361         if (dirVnodeInfo->vnodes[i].parent) {
3362             Log("Bad parent, vnode 1; %s...\n",
3363                 (Testing ? "skipping" : "salvaging"));
3364             dirVnodeInfo->vnodes[i].parent = 0;
3365             dirVnodeInfo->vnodes[i].changed = 1;
3366         }
3367     } else {
3368         parent = CheckVnodeNumber(salvinfo, dirVnodeInfo->vnodes[i].parent);
3369         if (parent && parent->salvaged == 0)
3370             SalvageDir(salvinfo, name, rwVid, dirVnodeInfo, alinkH,
3371                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3372                        rootdir, rootdirfound);
3373     }
3374
3375     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3376     dir.unique = dirVnodeInfo->vnodes[i].unique;
3377     dir.copied = 0;
3378     dir.vname = name;
3379     dir.parent = dirVnodeInfo->vnodes[i].parent;
3380     dir.haveDot = dir.haveDotDot = 0;
3381     dir.ds_linkH = alinkH;
3382     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, salvinfo->fileSysDevice,
3383                         dirVnodeInfo->inodes[i], &salvinfo->VolumeChanged);
3384
3385     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3386     if (!dirok) {
3387         if (!RebuildDirs) {
3388             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3389                 (Testing ? "skipping" : "salvaging"));
3390         }
3391         if (!Testing) {
3392             CopyAndSalvage(salvinfo, &dir);
3393             dirok = 1;
3394             dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3395         }
3396     }
3397     dirHandle = dir.dirHandle;
3398
3399     dir.name =
3400         GetDirName(salvinfo, bitNumberToVnodeNumber(i, vLarge),
3401                    &dirVnodeInfo->vnodes[i], path);
3402
3403     if (dirok) {
3404         /* If enumeration failed for random reasons, we will probably delete
3405          * too much stuff, so we guard against this instead.
3406          */