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