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