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