death to register
[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(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(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     const struct ViceInodeInfo *p1 = _p1;
970     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(struct ViceInodeInfo *ip, int maxInodes,
1065                   struct InodeSummary *summary)
1066 {
1067     VolumeId volume = ip->u.vnode.volumeId;
1068     VolumeId rwvolume = volume;
1069     int n, nSpecial;
1070     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     const struct VolumeSummary *p1 = _p1;
1269     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(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(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(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(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(struct InodeSummary *isp, int nVols)
2018 {
2019     /* Check headers BEFORE forking */
2020     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(struct InodeSummary *isp,
2070                         struct ViceInodeInfo *inodes, int RW,
2071                         int check, int *deleteMe)
2072 {
2073     int i;
2074     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(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(struct InodeSummary *rwIsp,
2480               struct InodeSummary *thisIsp,
2481               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              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                     struct ViceInodeInfo *lip = ip;
2590                     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(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(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     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             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             dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3333         }
3334     }
3335     dirHandle = dir.dirHandle;
3336
3337     dir.name =
3338         GetDirName(bitNumberToVnodeNumber(i, vLarge),
3339                    &dirVnodeInfo->vnodes[i], path);
3340
3341     if (dirok) {
3342         /* If enumeration failed for random reasons, we will probably delete
3343          * too much stuff, so we guard against this instead.
3344          */
3345         assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3346     }
3347
3348     /* Delete the old directory if it was copied in order to salvage.
3349      * CopyOnWrite has written the new inode # to the disk, but we still
3350      * have the old one in our local structure here.  Thus, we idec the
3351      * local dude.
3352      */
3353     DFlush();
3354     if (dir.copied && !Testing) {
3355         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3356         assert(code == 0);
3357         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3358     }
3359
3360     /* Remember rootdir DirSummary _after_ it has been judged */
3361     if (dir.vnodeNumber == 1 && dir.unique == 1) {
3362         memcpy(rootdir, &dir, sizeof(struct DirSummary));
3363         *rootdirfound = 1;
3364     }
3365
3366     return;
3367 }
3368
3369 /**
3370  * Get a new FID that can be used to create a new file.
3371  *
3372  * @param[in] volHeader vol header for the volume
3373  * @param[in] class     what type of vnode we'll be creating (vLarge or vSmall)
3374  * @param[out] afid     the FID that we can use (only Vnode and Unique are set)
3375  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3376  *                          updated to the new max unique if we create a new
3377  *                          vnode
3378  */
3379 static void
3380 GetNewFID(VolumeDiskData *volHeader, VnodeClass class, AFSFid *afid,
3381           Unique *maxunique)
3382 {
3383     int i;
3384     for (i = 0; i < vnodeInfo[class].nVnodes; i++) {
3385         if (vnodeInfo[class].vnodes[i].type == vNull) {
3386             break;
3387         }
3388     }
3389     if (i == vnodeInfo[class].nVnodes) {
3390         /* no free vnodes; make a new one */
3391         vnodeInfo[class].nVnodes++;
3392         vnodeInfo[class].vnodes = realloc(vnodeInfo[class].vnodes,
3393                                           sizeof(struct VnodeEssence) * (i+1));
3394         vnodeInfo[class].vnodes[i].type = vNull;
3395     }
3396
3397     afid->Vnode = bitNumberToVnodeNumber(i, class);
3398
3399     if (volHeader->uniquifier < (*maxunique + 1)) {
3400         /* header uniq is bad; it will get bumped by 2000 later */
3401         afid->Unique = *maxunique + 1 + 2000;
3402         (*maxunique)++;
3403     } else {
3404         /* header uniq seems okay; just use that */
3405         afid->Unique = *maxunique = volHeader->uniquifier++;
3406     }
3407 }
3408
3409 /**
3410  * Create a vnode for a README file explaining not to use a recreated-root vol.
3411  *
3412  * @param[in] volHeader vol header for the volume
3413  * @param[in] alinkH    ihandle for i/o for the volume
3414  * @param[in] vid       volume id
3415  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3416  *                          updated to the new max unique if we create a new
3417  *                          vnode
3418  * @param[out] afid     FID for the new readme vnode
3419  * @param[out] ainode   the inode for the new readme file
3420  *
3421  * @return operation status
3422  *  @retval 0 success
3423  *  @retval -1 error
3424  */
3425 static int
3426 CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
3427              VolumeId vid, Unique *maxunique, AFSFid *afid, Inode *ainode)
3428 {
3429     Inode readmeinode;
3430     struct VnodeDiskObject *rvnode = NULL;
3431     afs_sfsize_t bytes;
3432     IHandle_t *readmeH = NULL;
3433     struct VnodeEssence *vep;
3434     afs_fsize_t length;
3435     time_t now = time(NULL);
3436
3437     /* Try to make the note brief, but informative. Only administrators should
3438      * be able to read this file at first, so we can hopefully assume they
3439      * know what AFS is, what a volume is, etc. */
3440     char readme[] =
3441 "This volume has been salvaged, but has lost its original root directory.\n"
3442 "The root directory that exists now has been recreated from orphan files\n"
3443 "from the rest of the volume. This recreated root directory may interfere\n"
3444 "with old cached data on clients, and there is no way the salvager can\n"
3445 "reasonably prevent that. So, it is recommended that you do not continue to\n"
3446 "use this volume, but only copy the salvaged data to a new volume.\n"
3447 "Continuing to use this volume as it exists now may cause some clients to\n"
3448 "behave oddly when accessing this volume.\n"
3449 "\n\t -- Your friendly neighborhood OpenAFS salvager\n";
3450     /* ^ the person reading this probably just lost some data, so they could
3451      * use some cheering up. */
3452
3453     /* -1 for the trailing NUL */
3454     length = sizeof(readme) - 1;
3455
3456     GetNewFID(volHeader, vSmall, afid, maxunique);
3457
3458     vep = &vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
3459
3460     /* create the inode and write the contents */
3461     readmeinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid,
3462                             afid->Vnode, afid->Unique, 1);
3463     if (!VALID_INO(readmeinode)) {
3464         Log("CreateReadme: readme IH_CREATE failed\n");
3465         goto error;
3466     }
3467
3468     IH_INIT(readmeH, fileSysDevice, vid, readmeinode);
3469     bytes = IH_IWRITE(readmeH, 0, readme, length);
3470     IH_RELEASE(readmeH);
3471
3472     if (bytes != length) {
3473         Log("CreateReadme: IWRITE failed (%d/%d)\n", (int)bytes,
3474             (int)sizeof(readme));
3475         goto error;
3476     }
3477
3478     /* create the vnode and write it out */
3479     rvnode = calloc(1, SIZEOF_SMALLDISKVNODE);
3480     if (!rvnode) {
3481         Log("CreateRootDir: error alloc'ing memory\n");
3482         goto error;
3483     }
3484
3485     rvnode->type = vFile;
3486     rvnode->cloned = 0;
3487     rvnode->modeBits = 0777;
3488     rvnode->linkCount = 1;
3489     VNDISK_SET_LEN(rvnode, length);
3490     rvnode->uniquifier = afid->Unique;
3491     rvnode->dataVersion = 1;
3492     VNDISK_SET_INO(rvnode, readmeinode);
3493     rvnode->unixModifyTime = rvnode->serverModifyTime = now;
3494     rvnode->author = 0;
3495     rvnode->owner = 0;
3496     rvnode->parent = 1;
3497     rvnode->group = 0;
3498     rvnode->vnodeMagic = VnodeClassInfo[vSmall].magic;
3499
3500     bytes = IH_IWRITE(vnodeInfo[vSmall].handle,
3501                       vnodeIndexOffset(&VnodeClassInfo[vSmall], afid->Vnode),
3502                       (char*)rvnode, SIZEOF_SMALLDISKVNODE);
3503
3504     if (bytes != SIZEOF_SMALLDISKVNODE) {
3505         Log("CreateReadme: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3506             (int)SIZEOF_SMALLDISKVNODE);
3507         goto error;
3508     }
3509
3510     /* update VnodeEssence for new readme vnode */
3511     vnodeInfo[vSmall].nAllocatedVnodes++;
3512     vep->count = 0;
3513     vep->blockCount = nBlocks(length);
3514     vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
3515     vep->parent = rvnode->parent;
3516     vep->unique = rvnode->uniquifier;
3517     vep->modeBits = rvnode->modeBits;
3518     vep->InodeNumber = VNDISK_GET_INO(rvnode);
3519     vep->type = rvnode->type;
3520     vep->author = rvnode->author;
3521     vep->owner = rvnode->owner;
3522     vep->group = rvnode->group;
3523
3524     free(rvnode);
3525     rvnode = NULL;
3526
3527     vep->claimed = 1;
3528     vep->changed = 0;
3529     vep->salvaged = 1;
3530     vep->todelete = 0;
3531
3532     *ainode = readmeinode;
3533
3534     return 0;
3535
3536  error:
3537     if (IH_DEC(alinkH, readmeinode, vid)) {
3538         Log("CreateReadme (recovery): IH_DEC failed\n");
3539     }
3540
3541     if (rvnode) {
3542         free(rvnode);
3543         rvnode = NULL;
3544     }
3545
3546     return -1;
3547 }
3548
3549 /**
3550  * create a root dir for a volume that lacks one.
3551  *
3552  * @param[in] volHeader vol header for the volume
3553  * @param[in] alinkH    ihandle for disk access for this volume group
3554  * @param[in] vid       volume id we're dealing with
3555  * @param[out] rootdir  populated with info about the new root dir
3556  * @param[inout] maxunique  max uniquifier for all vnodes in the volume;
3557  *                          updated to the new max unique if we create a new
3558  *                          vnode
3559  *
3560  * @return operation status
3561  *  @retval 0  success
3562  *  @retval -1 error
3563  */
3564 static int
3565 CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
3566               struct DirSummary *rootdir, Unique *maxunique)
3567 {
3568     FileVersion dv;
3569     int decroot = 0, decreadme = 0;
3570     AFSFid did, readmeid;
3571     afs_fsize_t length;
3572     Inode rootinode;
3573     struct VnodeDiskObject *rootvnode = NULL;
3574     struct acl_accessList *ACL;
3575     Inode *ip;
3576     afs_sfsize_t bytes;
3577     struct VnodeEssence *vep;
3578     Inode readmeinode;
3579     time_t now = time(NULL);
3580
3581     if (!vnodeInfo[vLarge].vnodes && !vnodeInfo[vSmall].vnodes) {
3582         Log("Not creating new root dir; volume appears to lack any vnodes\n");
3583         goto error;
3584     }
3585
3586     if (!vnodeInfo[vLarge].vnodes) {
3587         /* We don't have any large vnodes in the volume; allocate room
3588          * for one so we can recreate the root dir */
3589         vnodeInfo[vLarge].nVnodes = 1;
3590         vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
3591         vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
3592
3593         assert(vnodeInfo[vLarge].vnodes);
3594         assert(vnodeInfo[vLarge].inodes);
3595     }
3596
3597     vep = &vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
3598     ip = &vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
3599     if (vep->type != vNull) {
3600         Log("Not creating new root dir; existing vnode 1 is non-null\n");
3601         goto error;
3602     }
3603
3604     if (CreateReadme(volHeader, alinkH, vid, maxunique, &readmeid, &readmeinode)) {
3605         goto error;
3606     }
3607     decreadme = 1;
3608
3609     /* set the DV to a very high number, so it is unlikely that we collide
3610      * with a cached DV */
3611     dv = 1 << 30;
3612
3613     rootinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid, 1, 1, dv);
3614     if (!VALID_INO(rootinode)) {
3615         Log("CreateRootDir: IH_CREATE failed\n");
3616         goto error;
3617     }
3618     decroot = 1;
3619
3620     SetSalvageDirHandle(&rootdir->dirHandle, vid, fileSysDevice, rootinode);
3621     did.Volume = vid;
3622     did.Vnode = 1;
3623     did.Unique = 1;
3624     if (MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
3625         Log("CreateRootDir: MakeDir failed\n");
3626         goto error;
3627     }
3628     if (Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
3629         Log("CreateRootDir: Create failed\n");
3630         goto error;
3631     }
3632     DFlush();
3633     length = Length(&rootdir->dirHandle);
3634     DZap((void *)&rootdir->dirHandle);
3635
3636     /* create the new root dir vnode */
3637     rootvnode = calloc(1, SIZEOF_LARGEDISKVNODE);
3638     if (!rootvnode) {
3639         Log("CreateRootDir: malloc failed\n");
3640         goto error;
3641     }
3642
3643     /* only give 'rl' permissions to 'system:administrators'. We do this to
3644      * try to catch the attention of an administrator, that they should not
3645      * be writing to this directory or continue to use it. */
3646     ACL = VVnodeDiskACL(rootvnode);
3647     ACL->size = sizeof(struct acl_accessList);
3648     ACL->version = ACL_ACLVERSION;
3649     ACL->total = 1;
3650     ACL->positive = 1;
3651     ACL->negative = 0;
3652     ACL->entries[0].id = -204; /* system:administrators */
3653     ACL->entries[0].rights = PRSFS_READ | PRSFS_LOOKUP;
3654
3655     rootvnode->type = vDirectory;
3656     rootvnode->cloned = 0;
3657     rootvnode->modeBits = 0777;
3658     rootvnode->linkCount = 2;
3659     VNDISK_SET_LEN(rootvnode, length);
3660     rootvnode->uniquifier = 1;
3661     rootvnode->dataVersion = dv;
3662     VNDISK_SET_INO(rootvnode, rootinode);
3663     rootvnode->unixModifyTime = rootvnode->serverModifyTime = now;
3664     rootvnode->author = 0;
3665     rootvnode->owner = 0;
3666     rootvnode->parent = 0;
3667     rootvnode->group = 0;
3668     rootvnode->vnodeMagic = VnodeClassInfo[vLarge].magic;
3669
3670     /* write it out to disk */
3671     bytes = IH_IWRITE(vnodeInfo[vLarge].handle,
3672               vnodeIndexOffset(&VnodeClassInfo[vLarge], 1),
3673               (char*)rootvnode, SIZEOF_LARGEDISKVNODE);
3674
3675     if (bytes != SIZEOF_LARGEDISKVNODE) {
3676         /* just cast to int and don't worry about printing real 64-bit ints;
3677          * a large disk vnode isn't anywhere near the 32-bit limit */
3678         Log("CreateRootDir: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3679             (int)SIZEOF_LARGEDISKVNODE);
3680         goto error;
3681     }
3682
3683     /* update VnodeEssence for the new root vnode */
3684     vnodeInfo[vLarge].nAllocatedVnodes++;
3685     vep->count = 0;
3686     vep->blockCount = nBlocks(length);
3687     vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
3688     vep->parent = rootvnode->parent;
3689     vep->unique = rootvnode->uniquifier;
3690     vep->modeBits = rootvnode->modeBits;
3691     vep->InodeNumber = VNDISK_GET_INO(rootvnode);
3692     vep->type = rootvnode->type;
3693     vep->author = rootvnode->author;
3694     vep->owner = rootvnode->owner;
3695     vep->group = rootvnode->group;
3696
3697     free(rootvnode);
3698     rootvnode = NULL;
3699
3700     vep->claimed = 0;
3701     vep->changed = 0;
3702     vep->salvaged = 1;
3703     vep->todelete = 0;
3704
3705     /* update DirSummary for the new root vnode */
3706     rootdir->vnodeNumber = 1;
3707     rootdir->unique = 1;
3708     rootdir->haveDot = 1;
3709     rootdir->haveDotDot = 1;
3710     rootdir->rwVid = vid;
3711     rootdir->copied = 0;
3712     rootdir->parent = 0;
3713     rootdir->name = strdup(".");
3714     rootdir->vname = volHeader->name;
3715     rootdir->ds_linkH = alinkH;
3716
3717     *ip = rootinode;
3718
3719     return 0;
3720
3721  error:
3722     if (decroot && IH_DEC(alinkH, rootinode, vid)) {
3723         Log("CreateRootDir (recovery): IH_DEC (root) failed\n");
3724     }
3725     if (decreadme && IH_DEC(alinkH, readmeinode, vid)) {
3726         Log("CreateRootDir (recovery): IH_DEC (readme) failed\n");
3727     }
3728     if (rootvnode) {
3729         free(rootvnode);
3730         rootvnode = NULL;
3731     }
3732     return -1;
3733 }
3734
3735 int
3736 SalvageVolume(struct InodeSummary *rwIsp, IHandle_t * alinkH)
3737 {
3738     /* This routine, for now, will only be called for read-write volumes */
3739     int i, j, code;
3740     int BlocksInVolume = 0, FilesInVolume = 0;
3741     VnodeClass class;
3742     struct DirSummary rootdir, oldrootdir;
3743     struct VnodeInfo *dirVnodeInfo;
3744     struct VnodeDiskObject vnode;
3745     VolumeDiskData volHeader;
3746     VolumeId vid;
3747     int orphaned, rootdirfound = 0;
3748     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
3749     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
3750     struct VnodeEssence *vep;
3751     afs_int32 v, pv;
3752     IHandle_t *h;
3753     afs_sfsize_t nBytes;
3754     AFSFid pa;
3755     VnodeId LFVnode, ThisVnode;
3756     Unique LFUnique, ThisUnique;
3757     char npath[128];
3758     int newrootdir = 0;
3759
3760     vid = rwIsp->volSummary->header.id;
3761     IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3762     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3763     assert(nBytes == sizeof(volHeader));
3764     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3765     assert(volHeader.destroyMe != DESTROY_ME);
3766     /* (should not have gotten this far with DESTROY_ME flag still set!) */
3767
3768     DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3769                        &maxunique);
3770     DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3771                        &maxunique);
3772
3773     dirVnodeInfo = &vnodeInfo[vLarge];
3774     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3775         SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3776                    &rootdirfound);
3777     }
3778 #ifdef AFS_NT40_ENV
3779     nt_sync(fileSysDevice);
3780 #else
3781     sync();                             /* This used to be done lower level, for every dir */
3782 #endif
3783     if (Showmode) {
3784         IH_RELEASE(h);
3785         return 0;
3786     }
3787
3788     if (!rootdirfound && (orphans == ORPH_ATTACH) && !Testing) {
3789
3790         Log("Cannot find root directory for volume %lu; attempting to create "
3791             "a new one\n", afs_printable_uint32_lu(vid));
3792
3793         code = CreateRootDir(&volHeader, alinkH, vid, &rootdir, &maxunique);
3794         if (code == 0) {
3795             rootdirfound = 1;
3796             newrootdir = 1;
3797             VolumeChanged = 1;
3798         }
3799     }
3800
3801     /* Parse each vnode looking for orphaned vnodes and
3802      * connect them to the tree as orphaned (if requested).
3803      */
3804     oldrootdir = rootdir;
3805     for (class = 0; class < nVNODECLASSES; class++) {
3806         for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3807             vep = &(vnodeInfo[class].vnodes[v]);
3808             ThisVnode = bitNumberToVnodeNumber(v, class);
3809             ThisUnique = vep->unique;
3810
3811             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3812                 continue;       /* Ignore unused, claimed, and root vnodes */
3813
3814             /* This vnode is orphaned. If it is a directory vnode, then the '..'
3815              * entry in this vnode had incremented the parent link count (In
3816              * JudgeEntry()). We need to go to the parent and decrement that
3817              * link count. But if the parent's unique is zero, then the parent
3818              * link count was not incremented in JudgeEntry().
3819              */
3820             if (class == vLarge) {      /* directory vnode */
3821                 pv = vnodeIdToBitNumber(vep->parent);
3822                 if (vnodeInfo[vLarge].vnodes[pv].unique != 0) {
3823                     if (vep->parent == 1 && newrootdir) {
3824                         /* this vnode's parent was the volume root, and
3825                          * we just created the volume root. So, the parent
3826                          * dir didn't exist during JudgeEntry, so the link
3827                          * count was not inc'd there, so don't dec it here.
3828                          */
3829
3830                          /* noop */
3831
3832                     } else {
3833                         vnodeInfo[vLarge].vnodes[pv].count++;
3834                     }
3835                 }
3836             }
3837
3838             if (!rootdirfound)
3839                 continue;       /* If no rootdir, can't attach orphaned files */
3840
3841             /* Here we attach orphaned files and directories into the
3842              * root directory, LVVnode, making sure link counts stay correct.
3843              */
3844             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3845                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
3846                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
3847
3848                 /* Update this orphaned vnode's info. Its parent info and 
3849                  * link count (do for orphaned directories and files).
3850                  */
3851                 vep->parent = LFVnode;  /* Parent is the root dir */
3852                 vep->unique = LFUnique;
3853                 vep->changed = 1;
3854                 vep->claimed = 1;
3855                 vep->count--;   /* Inc link count (root dir will pt to it) */
3856
3857                 /* If this orphaned vnode is a directory, change '..'. 
3858                  * The name of the orphaned dir/file is unknown, so we
3859                  * build a unique name. No need to CopyOnWrite the directory
3860                  * since it is not connected to tree in BK or RO volume and
3861                  * won't be visible there.
3862                  */
3863                 if (class == vLarge) {
3864                     AFSFid pa;
3865                     DirHandle dh;
3866
3867                     /* Remove and recreate the ".." entry in this orphaned directory */
3868                     SetSalvageDirHandle(&dh, vid, fileSysDevice,
3869                                         vnodeInfo[class].inodes[v]);
3870                     pa.Vnode = LFVnode;
3871                     pa.Unique = LFUnique;
3872                     assert(Delete(&dh, "..") == 0);
3873                     assert(Create(&dh, "..", &pa) == 0);
3874
3875                     /* The original parent's link count was decremented above.
3876                      * Here we increment the new parent's link count.
3877                      */
3878                     pv = vnodeIdToBitNumber(LFVnode);
3879                     vnodeInfo[vLarge].vnodes[pv].count--;
3880
3881                 }
3882
3883                 /* Go to the root dir and add this entry. The link count of the
3884                  * root dir was incremented when ".." was created. Try 10 times.
3885                  */
3886                 for (j = 0; j < 10; j++) {
3887                     pa.Vnode = ThisVnode;
3888                     pa.Unique = ThisUnique;
3889
3890                     (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3891                                        ((class ==
3892                                          vLarge) ? "__ORPHANDIR__" :
3893                                         "__ORPHANFILE__"), ThisVnode,
3894                                        ThisUnique);
3895
3896                     CopyOnWrite(&rootdir);
3897                     code = Create(&rootdir.dirHandle, npath, &pa);
3898                     if (!code)
3899                         break;
3900
3901                     ThisUnique += 50;   /* Try creating a different file */
3902                 }
3903                 assert(code == 0);
3904                 Log("Attaching orphaned %s to volume's root dir as %s\n",
3905                     ((class == vLarge) ? "directory" : "file"), npath);
3906             }
3907         }                       /* for each vnode in the class */
3908     }                           /* for each class of vnode */
3909
3910     /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3911     DFlush();
3912     if (rootdirfound && !oldrootdir.copied && rootdir.copied) {
3913         code =
3914             IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3915                    oldrootdir.rwVid);
3916         assert(code == 0);
3917         /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3918     }
3919
3920     DFlush();                   /* Flush the changes */
3921     if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3922         Log("Cannot attach orphaned files and directories: Root directory not found\n");
3923         orphans = ORPH_IGNORE;
3924     }
3925
3926     /* Write out all changed vnodes. Orphaned files and directories
3927      * will get removed here also (if requested).
3928      */
3929     for (class = 0; class < nVNODECLASSES; class++) {
3930         int nVnodes = vnodeInfo[class].nVnodes;
3931         struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3932         struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3933         FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3934         BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3935         for (i = 0; i < nVnodes; i++) {
3936             struct VnodeEssence *vnp = &vnodes[i];
3937             VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3938
3939             /* If the vnode is good but is unclaimed (not listed in
3940              * any directory entries), then it is orphaned.
3941              */
3942             orphaned = -1;
3943             if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3944                 vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
3945                 vnp->changed = 1;
3946             }
3947
3948             if (vnp->changed || vnp->count) {
3949                 int oldCount;
3950                 nBytes =
3951                     IH_IREAD(vnodeInfo[class].handle,
3952                              vnodeIndexOffset(vcp, vnodeNumber),
3953                              (char *)&vnode, sizeof(vnode));
3954                 assert(nBytes == sizeof(vnode));
3955
3956                 vnode.parent = vnp->parent;
3957                 oldCount = vnode.linkCount;
3958                 vnode.linkCount = vnode.linkCount - vnp->count;
3959
3960                 if (orphaned == -1)
3961                     orphaned = IsVnodeOrphaned(vnodeNumber);
3962                 if (orphaned) {
3963                     if (!vnp->todelete) {
3964                         /* Orphans should have already been attached (if requested) */
3965                         assert(orphans != ORPH_ATTACH);
3966                         oblocks += vnp->blockCount;
3967                         ofiles++;
3968                     }
3969                     if (((orphans == ORPH_REMOVE) || vnp->todelete)
3970                         && !Testing) {
3971                         BlocksInVolume -= vnp->blockCount;
3972                         FilesInVolume--;
3973                         if (VNDISK_GET_INO(&vnode)) {
3974                             code =
3975                                 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3976                             assert(code == 0);
3977                         }
3978                         memset(&vnode, 0, sizeof(vnode));
3979                     }
3980                 } else if (vnp->count) {
3981                     if (!Showmode) {
3982                         Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3983                     }
3984                 } else {
3985                     vnode.modeBits = vnp->modeBits;
3986                 }
3987
3988                 vnode.dataVersion++;
3989                 if (!Testing) {
3990                     nBytes =
3991                         IH_IWRITE(vnodeInfo[class].handle,
3992                                   vnodeIndexOffset(vcp, vnodeNumber),
3993                                   (char *)&vnode, sizeof(vnode));
3994                     assert(nBytes == sizeof(vnode));
3995                 }
3996                 VolumeChanged = 1;
3997             }
3998         }
3999     }
4000     if (!Showmode && ofiles) {
4001         Log("%s %d orphaned files and directories (approx. %u KB)\n",
4002             (!Testing
4003              && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
4004             oblocks);
4005     }
4006
4007     for (class = 0; class < nVNODECLASSES; class++) {
4008         struct VnodeInfo *vip = &vnodeInfo[class];
4009         for (i = 0; i < vip->nVnodes; i++)
4010             if (vip->vnodes[i].name)
4011                 free(vip->vnodes[i].name);
4012         if (vip->vnodes)
4013             free(vip->vnodes);
4014         if (vip->inodes)
4015             free(vip->inodes);
4016     }
4017
4018     /* Set correct resource utilization statistics */
4019     volHeader.filecount = FilesInVolume;
4020     volHeader.diskused = BlocksInVolume;
4021
4022     /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
4023     if (volHeader.uniquifier < (maxunique + 1)) {
4024         if (!Showmode)
4025             Log("Volume uniquifier is too low; fixed\n");
4026         /* Plus 2,000 in case there are workstations out there with
4027          * cached vnodes that have since been deleted
4028          */
4029         volHeader.uniquifier = (maxunique + 1 + 2000);
4030     }
4031
4032     if (newrootdir) {
4033         Log("*** WARNING: Root directory recreated, but volume is fragile! "
4034             "Only use this salvaged volume to copy data to another volume; "
4035             "do not continue to use this volume (%lu) as-is.\n",
4036             afs_printable_uint32_lu(vid));
4037     }
4038
4039 #ifdef FSSYNC_BUILD_CLIENT
4040     if (!Testing && VolumeChanged) {
4041         afs_int32 fsync_code;
4042
4043         fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
4044         if (fsync_code) {
4045             Log("Error trying to tell the fileserver to break callbacks for "
4046                 "changed volume %lu; error code %ld\n",
4047                 afs_printable_uint32_lu(vid),
4048                 afs_printable_int32_ld(fsync_code));
4049         } else {
4050             VolumeChanged = 0;
4051         }
4052     }
4053 #endif /* FSSYNC_BUILD_CLIENT */
4054
4055     /* Turn off the inUse bit; the volume's been salvaged! */
4056     volHeader.inUse = 0;        /* clear flag indicating inUse@last crash */
4057     volHeader.needsSalvaged = 0;        /* clear 'damaged' flag */
4058     volHeader.inService = 1;    /* allow service again */
4059     volHeader.needsCallback = (VolumeChanged != 0);
4060     volHeader.dontSalvage = DONT_SALVAGE;
4061     VolumeChanged = 0;
4062     if (!Testing) {
4063         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4064         assert(nBytes == sizeof(volHeader));
4065     }
4066     if (!Showmode) {
4067         Log("%sSalvaged %s (%u): %d files, %d blocks\n",
4068             (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
4069             FilesInVolume, BlocksInVolume);
4070     }
4071     IH_RELEASE(vnodeInfo[vSmall].handle);
4072     IH_RELEASE(vnodeInfo[vLarge].handle);
4073     IH_RELEASE(h);
4074     return 0;
4075 }
4076
4077 void
4078 ClearROInUseBit(struct VolumeSummary *summary)
4079 {
4080     IHandle_t *h = summary->volumeInfoHandle;
4081     afs_sfsize_t nBytes;
4082
4083     VolumeDiskData volHeader;
4084
4085     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
4086     assert(nBytes == sizeof(volHeader));
4087     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
4088     volHeader.inUse = 0;
4089     volHeader.needsSalvaged = 0;
4090     volHeader.inService = 1;
4091     volHeader.dontSalvage = DONT_SALVAGE;
4092     if (!Testing) {
4093         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4094         assert(nBytes == sizeof(volHeader));
4095     }
4096 }
4097
4098 /* MaybeZapVolume
4099  * Possible delete the volume.
4100  *
4101  * deleteMe - Always do so, only a partial volume.
4102  */
4103 void
4104 MaybeZapVolume(struct InodeSummary *isp, char *message, int deleteMe,
4105                int check)
4106 {
4107     if (readOnly(isp) || deleteMe) {
4108         if (isp->volSummary && isp->volSummary->fileName) {
4109             if (deleteMe) {
4110                 if (!Showmode)
4111                     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);
4112                 if (!Showmode)
4113                     Log("It will be deleted on this server (you may find it elsewhere)\n");
4114             } else {
4115                 if (!Showmode)
4116                     Log("Volume %u needs to be salvaged.  Since it is read-only, however,\n", isp->volumeId);
4117                 if (!Showmode)
4118                     Log("it will be deleted instead.  It should be recloned.\n");
4119             }
4120             if (!Testing) {
4121                 afs_int32 code;
4122                 char path[64];
4123                 sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
4124
4125                 code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
4126                 if (code) {
4127                     Log("Error %ld destroying volume disk header for volume %lu\n",
4128                         afs_printable_int32_ld(code),
4129                         afs_printable_uint32_lu(isp->volumeId));
4130                 }
4131
4132                 /* make sure we actually delete the fileName file; ENOENT
4133                  * is fine, since VDestroyVolumeDiskHeader probably already
4134                  * unlinked it */
4135                 if (unlink(path) && errno != ENOENT) {
4136                     Log("Unable to unlink %s (errno = %d)\n", path, errno);
4137                 }
4138             }
4139         }
4140     } else if (!check) {
4141         Log("%s salvage was unsuccessful: read-write volume %u\n", message,
4142             isp->volumeId);
4143         Abort("Salvage of volume %u aborted\n", isp->volumeId);
4144     }
4145 }
4146
4147 #ifdef AFS_DEMAND_ATTACH_FS
4148 /**
4149  * Locks a volume on disk for salvaging.
4150  *
4151  * @param[in] volumeId   volume ID to lock
4152  *
4153  * @return operation status
4154  *  @retval 0  success
4155  *  @retval -1 volume lock raced with a fileserver restart; all volumes must
4156  *             checked out and locked again
4157  *
4158  * @note DAFS only
4159  */
4160 static int
4161 LockVolume(VolumeId volumeId)
4162 {
4163     afs_int32 code;
4164     int locktype;
4165
4166     /* should always be WRITE_LOCK, but keep the lock-type logic all
4167      * in one place, in VVolLockType. Params will be ignored, but
4168      * try to provide what we're logically doing. */
4169     locktype = VVolLockType(V_VOLUPD, 1);
4170
4171     code = VLockVolumeByIdNB(volumeId, fileSysPartition, locktype);
4172     if (code) {
4173         if (code == EBUSY) {
4174             Abort("Someone else appears to be using volume %lu; Aborted\n",
4175                   afs_printable_uint32_lu(volumeId));
4176         }
4177         Abort("Error %ld trying to lock volume %lu; Aborted\n",
4178               afs_printable_int32_ld(code),
4179               afs_printable_uint32_lu(volumeId));
4180     }
4181
4182     code = FSYNC_VerifyCheckout(volumeId, fileSysPathName, FSYNC_VOL_OFF, FSYNC_SALVAGE);
4183     if (code == SYNC_DENIED) {
4184         /* need to retry checking out volumes */
4185         return -1;
4186     }
4187     if (code != SYNC_OK) {
4188         Abort("FSYNC_VerifyCheckout failed for volume %lu with code %ld\n",
4189               afs_printable_uint32_lu(volumeId), afs_printable_int32_ld(code));
4190     }
4191
4192     /* set inUse = programType in the volume header to ensure that nobody
4193      * tries to use this volume again without salvaging, if we somehow crash
4194      * or otherwise exit before finishing the salvage.
4195      */
4196     if (!Testing) {
4197        IHandle_t *h;
4198        struct VolumeHeader header;
4199        struct VolumeDiskHeader diskHeader;
4200        struct VolumeDiskData volHeader;
4201
4202        code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
4203        if (code) {
4204            return 0;
4205        }
4206
4207        DiskToVolumeHeader(&header, &diskHeader);
4208
4209        IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
4210        if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
4211            volHeader.stamp.magic != VOLUMEINFOMAGIC) {
4212
4213            IH_RELEASE(h);
4214            return 0;
4215        }
4216
4217        volHeader.inUse = programType;
4218
4219        /* If we can't re-write the header, bail out and error. We don't
4220         * assert when reading the header, since it's possible the
4221         * header isn't really there (when there's no data associated
4222         * with the volume; we just delete the vol header file in that
4223         * case). But if it's there enough that we can read it, but
4224         * somehow we cannot write to it to signify we're salvaging it,
4225         * we've got a big problem and we cannot continue. */
4226        assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
4227
4228        IH_RELEASE(h);
4229     }
4230
4231     return 0;
4232 }
4233 #endif /* AFS_DEMAND_ATTACH_FS */
4234
4235 void
4236 AskOffline(VolumeId volumeId, char * partition)
4237 {
4238     afs_int32 code, i;
4239     SYNC_response res;
4240
4241     memset(&res, 0, sizeof(res));
4242
4243     for (i = 0; i < 3; i++) {
4244         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
4245
4246         if (code == SYNC_OK) {
4247             break;
4248         } else if (code == SYNC_DENIED) {
4249 #ifdef DEMAND_ATTACH_ENABLE
4250             Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
4251 #else
4252             Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
4253 #endif
4254             Abort("Salvage aborted\n");
4255         } else if (code == SYNC_BAD_COMMAND) {
4256             Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
4257                 FSYNC_VOL_OFF);
4258 #ifdef DEMAND_ATTACH_ENABLE
4259             Log("AskOffline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
4260 #else
4261             Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
4262 #endif
4263             Abort("Salvage aborted\n");
4264         } else if (i < 2) {
4265             /* try it again */
4266             Log("AskOffline:  request for fileserver to take volume offline failed; trying again...\n");
4267             FSYNC_clientFinis();
4268             FSYNC_clientInit();
4269         }
4270     }
4271     if (code != SYNC_OK) {
4272         Log("AskOffline:  request for fileserver to take volume offline failed; salvage aborting.\n");
4273         Abort("Salvage aborted\n");
4274     }
4275 }
4276
4277 void
4278 AskOnline(VolumeId volumeId, char *partition)
4279 {
4280     afs_int32 code, i;
4281
4282     for (i = 0; i < 3; i++) {
4283         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
4284
4285         if (code == SYNC_OK) {
4286             break;
4287         } else if (code == SYNC_DENIED) {
4288             Log("AskOnline:  file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
4289         } else if (code == SYNC_BAD_COMMAND) {
4290             Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
4291                 FSYNC_VOL_ON);
4292 #ifdef DEMAND_ATTACH_ENABLE
4293             Log("AskOnline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
4294 #else
4295             Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
4296 #endif
4297             break;
4298         } else if (i < 2) {
4299             /* try it again */
4300             Log("AskOnline:  request for fileserver to take volume offline failed; trying again...\n");
4301             FSYNC_clientFinis();
4302             FSYNC_clientInit();
4303         }
4304     }
4305 }
4306
4307 int
4308 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
4309 {
4310     /* Volume parameter is passed in case iopen is upgraded in future to
4311      * require a volume Id to be passed
4312      */
4313     char buf[4096];
4314     IHandle_t *srcH, *destH;
4315     FdHandle_t *srcFdP, *destFdP;
4316     ssize_t nBytes = 0;
4317
4318     IH_INIT(srcH, device, rwvolume, inode1);
4319     srcFdP = IH_OPEN(srcH);
4320     assert(srcFdP != NULL);
4321     IH_INIT(destH, device, rwvolume, inode2);
4322     destFdP = IH_OPEN(destH);
4323     while ((nBytes = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
4324         assert(FDH_WRITE(destFdP, buf, nBytes) == nBytes);
4325     assert(nBytes == 0);
4326     FDH_REALLYCLOSE(srcFdP);
4327     FDH_REALLYCLOSE(destFdP);
4328     IH_RELEASE(srcH);
4329     IH_RELEASE(destH);
4330     return 0;
4331 }
4332
4333 void
4334 PrintInodeList(void)
4335 {
4336     struct ViceInodeInfo *ip;
4337     struct ViceInodeInfo *buf;
4338     struct afs_stat status;
4339     int nInodes;
4340
4341     assert(afs_fstat(inodeFd, &status) == 0);
4342     buf = (struct ViceInodeInfo *)malloc(status.st_size);
4343     assert(buf != NULL);
4344     nInodes = status.st_size / sizeof(struct ViceInodeInfo);
4345     assert(read(inodeFd, buf, status.st_size) == status.st_size);
4346     for (ip = buf; nInodes--; ip++) {
4347         Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
4348             PrintInode(NULL, ip->inodeNumber), ip->linkCount,
4349             (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
4350             ip->u.param[2], ip->u.param[3]);
4351     }
4352     free(buf);
4353 }
4354
4355 void
4356 PrintInodeSummary(void)
4357 {
4358     int i;
4359     struct InodeSummary *isp;
4360
4361     for (i = 0; i < nVolumesInInodeFile; i++) {
4362         isp = &inodeSummary[i];
4363         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);
4364     }
4365 }
4366
4367 void
4368 PrintVolumeSummary(void)
4369 {
4370     int i;
4371     struct VolumeSummary *vsp;
4372
4373     for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
4374         Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
4375     }
4376 }
4377
4378 int
4379 Fork(void)
4380 {
4381     int f;
4382 #ifdef AFS_NT40_ENV
4383     f = 0;
4384     assert(0);                  /* Fork is never executed in the NT code path */
4385 #else
4386     f = fork();
4387     assert(f >= 0);
4388 #ifdef AFS_DEMAND_ATTACH_FS
4389     if ((f == 0) && (programType == salvageServer)) {
4390         /* we are a salvageserver child */
4391 #ifdef FSSYNC_BUILD_CLIENT
4392         VChildProcReconnectFS_r();
4393 #endif
4394 #ifdef SALVSYNC_BUILD_CLIENT
4395         VReconnectSALV_r();
4396 #endif
4397     }
4398 #endif /* AFS_DEMAND_ATTACH_FS */
4399 #endif /* !AFS_NT40_ENV */
4400     return f;
4401 }
4402
4403 void
4404 Exit(int code)
4405 {
4406     if (ShowLog)
4407         showlog();
4408
4409 #ifdef AFS_DEMAND_ATTACH_FS
4410     if (programType == salvageServer) {
4411 #ifdef SALVSYNC_BUILD_CLIENT
4412         VDisconnectSALV();
4413 #endif
4414 #ifdef FSSYNC_BUILD_CLIENT
4415         VDisconnectFS();
4416 #endif
4417     }
4418 #endif /* AFS_DEMAND_ATTACH_FS */
4419
4420 #ifdef AFS_NT40_ENV
4421     if (main_thread != pthread_self())
4422         pthread_exit((void *)code);
4423     else
4424         exit(code);
4425 #else
4426     exit(code);
4427 #endif
4428 }
4429
4430 int
4431 Wait(char *prog)
4432 {
4433     int status;
4434     int pid;
4435     pid = wait(&status);
4436     assert(pid != -1);
4437     if (WCOREDUMP(status))
4438         Log("\"%s\" core dumped!\n", prog);
4439     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
4440         return -1;
4441     return pid;
4442 }
4443
4444 static char *
4445 TimeStamp(time_t clock, int precision)
4446 {
4447     struct tm *lt;
4448     static char timestamp[20];
4449     lt = localtime(&clock);
4450     if (precision)
4451         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
4452     else
4453         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
4454     return timestamp;
4455 }
4456
4457 void
4458 CheckLogFile(char * log_path)
4459 {
4460     char oldSlvgLog[AFSDIR_PATH_MAX];
4461
4462 #ifndef AFS_NT40_ENV
4463     if (useSyslog) {
4464         ShowLog = 0;
4465         return;
4466     }
4467 #endif
4468
4469     strcpy(oldSlvgLog, log_path);
4470     strcat(oldSlvgLog, ".old");
4471     if (!logFile) {
4472         renamefile(log_path, oldSlvgLog);
4473         logFile = afs_fopen(log_path, "a");
4474
4475         if (!logFile) {         /* still nothing, use stdout */
4476             logFile = stdout;
4477             ShowLog = 0;
4478         }
4479 #ifndef AFS_NAMEI_ENV
4480         AFS_DEBUG_IOPS_LOG(logFile);
4481 #endif
4482     }
4483 }
4484
4485 #ifndef AFS_NT40_ENV
4486 void
4487 TimeStampLogFile(char * log_path)
4488 {
4489     char stampSlvgLog[AFSDIR_PATH_MAX];
4490     struct tm *lt;
4491     time_t now;
4492
4493     now = time(0);
4494     lt = localtime(&now);
4495     (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
4496                        "%s.%04d-%02d-%02d.%02d:%02d:%02d",
4497                        log_path, lt->tm_year + 1900,
4498                        lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
4499                        lt->tm_sec);
4500
4501     /* try to link the logfile to a timestamped filename */
4502     /* if it fails, oh well, nothing we can do */
4503     link(log_path, stampSlvgLog);
4504 }
4505 #endif
4506
4507 void
4508 showlog(void)
4509 {
4510     char line[256];
4511
4512 #ifndef AFS_NT40_ENV
4513     if (useSyslog) {
4514         printf("Can't show log since using syslog.\n");
4515         fflush(stdout);
4516         return;
4517     }
4518 #endif
4519
4520     if (logFile) {
4521         rewind(logFile);
4522         fclose(logFile);
4523     }
4524
4525     logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
4526
4527     if (!logFile)
4528         printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
4529     else {
4530         rewind(logFile);
4531         while (fgets(line, sizeof(line), logFile))
4532             printf("%s", line);
4533         fflush(stdout);
4534     }
4535 }
4536
4537 void
4538 Log(const char *format, ...)
4539 {
4540     struct timeval now;
4541     char tmp[1024];
4542     va_list args;
4543
4544     va_start(args, format);
4545     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4546     va_end(args);
4547 #ifndef AFS_NT40_ENV
4548     if (useSyslog) {
4549         syslog(LOG_INFO, "%s", tmp);
4550     } else
4551 #endif
4552         if (logFile) {
4553             gettimeofday(&now, 0);
4554             fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
4555             fflush(logFile);
4556         }
4557 }
4558
4559 void
4560 Abort(const char *format, ...)
4561 {
4562     va_list args;
4563     char tmp[1024];
4564
4565     va_start(args, format);
4566     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4567     va_end(args);
4568 #ifndef AFS_NT40_ENV
4569     if (useSyslog) {
4570         syslog(LOG_INFO, "%s", tmp);
4571     } else
4572 #endif
4573         if (logFile) {
4574             fprintf(logFile, "%s", tmp);
4575             fflush(logFile);
4576             if (ShowLog)
4577                 showlog();
4578         }
4579
4580     if (debug)
4581         abort();
4582     Exit(1);
4583 }
4584
4585 char *
4586 ToString(const char *s)
4587 {
4588     char *p;
4589     p = (char *)malloc(strlen(s) + 1);
4590     assert(p != NULL);
4591     strcpy(p, s);
4592     return p;
4593 }
4594
4595 /* Remove the FORCESALVAGE file */
4596 void
4597 RemoveTheForce(char *path)
4598 {
4599     char target[1024];
4600     struct afs_stat force; /* so we can use afs_stat to find it */
4601     strcpy(target,path);
4602     strcat(target,"/FORCESALVAGE");
4603     if (!Testing && ForceSalvage) {
4604         if (afs_stat(target,&force) == 0)  unlink(target);
4605     }
4606 }
4607
4608 #ifndef AFS_AIX32_ENV
4609 /*
4610  * UseTheForceLuke -    see if we can use the force
4611  */
4612 int
4613 UseTheForceLuke(char *path)
4614 {
4615     struct afs_stat force;
4616     char target[1024];
4617     strcpy(target,path);
4618     strcat(target,"/FORCESALVAGE");
4619
4620     return (afs_stat(target, &force) == 0);
4621 }
4622 #else
4623 /*
4624  * UseTheForceLuke -    see if we can use the force
4625  *
4626  * NOTE:
4627  *      The VRMIX fsck will not muck with the filesystem it is supposedly
4628  *      fixing and create a "FORCESALVAGE" file (by design).  Instead, we
4629  *      muck directly with the root inode, which is within the normal
4630  *      domain of fsck.
4631  *      ListViceInodes() has a side effect of setting ForceSalvage if
4632  *      it detects a need, based on root inode examination.
4633  */
4634 int
4635 UseTheForceLuke(char *path)
4636 {
4637
4638     return 0;                   /* sorry OB1    */
4639 }
4640 #endif
4641
4642 #ifdef AFS_NT40_ENV
4643 /* NT support routines */
4644
4645 static char execpathname[MAX_PATH];
4646 int
4647 nt_SalvagePartition(char *partName, int jobn)
4648 {
4649     int pid;
4650     int n;
4651     childJob_t job;
4652     if (!*execpathname) {
4653         n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
4654         if (!n || n == 1023)
4655             return -1;
4656     }
4657     job.cj_magic = SALVAGER_MAGIC;
4658     job.cj_number = jobn;
4659     (void)strcpy(job.cj_part, partName);
4660     pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
4661     return pid;
4662 }
4663
4664 int
4665 nt_SetupPartitionSalvage(void *datap, int len)
4666 {
4667     childJob_t *jobp = (childJob_t *) datap;
4668     char logname[AFSDIR_PATH_MAX];
4669
4670     if (len != sizeof(childJob_t))
4671         return -1;
4672     if (jobp->cj_magic != SALVAGER_MAGIC)
4673         return -1;
4674     myjob = *jobp;
4675
4676     /* Open logFile */
4677     (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
4678                   myjob.cj_number);
4679     logFile = afs_fopen(logname, "w");
4680     if (!logFile)
4681         logFile = stdout;
4682
4683     return 0;
4684 }
4685
4686
4687 #endif /* AFS_NT40_ENV */