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