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