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