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