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