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