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