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