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