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