e0992bf946cf318a29dc2daee327a44714b68a15
[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 DiskPartition64 *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 DiskPartition64 *p1, struct DiskPartition64 *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 DiskPartition64 *partP)
484 {
485     struct job {
486         struct DiskPartition64 *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 DiskPartition64 *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 DiskPartition64 *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, partP->name);
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 = NULL;
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                         && vsp->header.id != singleVolumeNumber)
1293                         AskOffline(vsp->header.id, fileSysPartition->name);
1294                     if (strcmp(nameShouldBe, dp->d_name)) {
1295                         if (!Showmode)
1296                             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 " : ""));
1297                         if (!Testing)
1298                             unlink(dp->d_name);
1299                     } else {
1300                         vsp->fileName = ToString(dp->d_name);
1301                         nVolumes++;
1302                         vsp++;
1303                     }
1304                 }
1305             }
1306             close(fd);
1307         }
1308     }
1309     closedir(dirp);
1310     qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1311           CompareVolumes);
1312 }
1313
1314 /* Find the link table. This should be associated with the RW volume or, if
1315  * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1316  */
1317 Inode
1318 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1319                struct ViceInodeInfo *allInodes)
1320 {
1321     int i, j;
1322     struct ViceInodeInfo *ip;
1323
1324     for (i = 0; i < nVols; i++) {
1325         ip = allInodes + isp[i].index;
1326         for (j = 0; j < isp[i].nSpecialInodes; j++) {
1327             if (ip[j].u.special.type == VI_LINKTABLE)
1328                 return ip[j].inodeNumber;
1329         }
1330     }
1331     return (Inode) - 1;
1332 }
1333
1334 int
1335 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1336 {
1337     struct versionStamp version;
1338     FdHandle_t *fdP;
1339
1340     if (!VALID_INO(ino))
1341         ino =
1342             IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1343                       INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1344     if (!VALID_INO(ino))
1345         Abort
1346             ("Unable to allocate link table inode for volume %u (error = %d)\n",
1347              isp->RWvolumeId, errno);
1348     IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1349     fdP = IH_OPEN(VGLinkH);
1350     if (fdP == NULL)
1351         Abort("Can't open link table for volume %u (error = %d)\n",
1352               isp->RWvolumeId, errno);
1353
1354     if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1355         Abort("Can't truncate link table for volume %u (error = %d)\n",
1356               isp->RWvolumeId, errno);
1357
1358     version.magic = LINKTABLEMAGIC;
1359     version.version = LINKTABLEVERSION;
1360
1361     if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1362         != sizeof(version))
1363         Abort("Can't truncate link table for volume %u (error = %d)\n",
1364               isp->RWvolumeId, errno);
1365
1366     FDH_REALLYCLOSE(fdP);
1367
1368     /* If the volume summary exits (i.e.,  the V*.vol header file exists),
1369      * then set this inode there as well.
1370      */
1371     if (isp->volSummary)
1372         isp->volSummary->header.linkTable = ino;
1373
1374     return 0;
1375 }
1376
1377 #ifdef AFS_NT40_ENV
1378 void *
1379 nt_SVG(void *arg)
1380 {
1381     SVGParms_t *parms = (SVGParms_t *) arg;
1382     DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1383     return NULL;
1384 }
1385
1386 void
1387 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1388 {
1389     pthread_t tid;
1390     pthread_attr_t tattr;
1391     int code;
1392     SVGParms_t parms;
1393
1394     /* Initialize per volume global variables, even if later code does so */
1395     VolumeChanged = 0;
1396     VGLinkH = NULL;
1397     VGLinkH_cnt = 0;
1398     memset(&VolInfo, 0, sizeof(VolInfo));
1399
1400     parms.svgp_inodeSummaryp = isp;
1401     parms.svgp_count = nVols;
1402     code = pthread_attr_init(&tattr);
1403     if (code) {
1404         Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1405             isp->RWvolumeId);
1406         return;
1407     }
1408     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1409     if (code) {
1410         Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1411         return;
1412     }
1413     code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1414     if (code) {
1415         Log("Failed to create thread to salvage volume group %u\n",
1416             isp->RWvolumeId);
1417         return;
1418     }
1419     (void)pthread_join(tid, NULL);
1420 }
1421 #endif /* AFS_NT40_ENV */
1422
1423 void
1424 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1425 {
1426     struct ViceInodeInfo *inodes, *allInodes, *ip;
1427     int i, totalInodes, size, salvageTo;
1428     int haveRWvolume;
1429     int check;
1430     Inode ino;
1431     int dec_VGLinkH = 0;
1432     int VGLinkH_p1 =0;
1433     FdHandle_t *fdP = NULL;
1434
1435     VGLinkH_cnt = 0;
1436     haveRWvolume = (isp->volumeId == isp->RWvolumeId
1437                     && isp->nSpecialInodes > 0);
1438     if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1439         if (!ForceSalvage && QuickCheck(isp, nVols))
1440             return;
1441     }
1442     if (ShowMounts && !haveRWvolume)
1443         return;
1444     if (canfork && !debug && Fork() != 0) {
1445         (void)Wait("Salvage volume group");
1446         return;
1447     }
1448     for (i = 0, totalInodes = 0; i < nVols; i++)
1449         totalInodes += isp[i].nInodes;
1450     size = totalInodes * sizeof(struct ViceInodeInfo);
1451     inodes = (struct ViceInodeInfo *)malloc(size);
1452     allInodes = inodes - isp->index;    /* this would the base of all the inodes
1453                                          * for the partition, if all the inodes
1454                                          * had been read into memory */
1455     assert(afs_lseek
1456            (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1457             SEEK_SET) != -1);
1458     assert(read(inodeFd, inodes, size) == size);
1459
1460     /* Don't try to salvage a read write volume if there isn't one on this
1461      * partition */
1462     salvageTo = haveRWvolume ? 0 : 1;
1463
1464 #ifdef AFS_NAMEI_ENV
1465     ino = FindLinkHandle(isp, nVols, allInodes);
1466     if (VALID_INO(ino)) {
1467         IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1468         fdP = IH_OPEN(VGLinkH);
1469     }
1470     if (!VALID_INO(ino) || fdP == NULL) {
1471         Log("%s link table for volume %u.\n",
1472             Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1473         if (Testing) {
1474             IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1475         } else {
1476             int i, j;
1477             struct ViceInodeInfo *ip;
1478             CreateLinkTable(isp, ino);
1479             fdP = IH_OPEN(VGLinkH);
1480             /* Sync fake 1 link counts to the link table, now that it exists */
1481             if (fdP) {
1482                 for (i = 0; i < nVols; i++) {
1483                         ip = allInodes + isp[i].index;
1484                          for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1485 #ifdef AFS_NT40_ENV
1486                                  nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1487 #else
1488                                  namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1489 #endif
1490                     }
1491                 }
1492             }
1493         }
1494     }
1495     if (fdP)
1496         FDH_REALLYCLOSE(fdP);
1497 #else
1498     IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1499 #endif
1500
1501     /* Salvage in reverse order--read/write volume last; this way any
1502      * Inodes not referenced by the time we salvage the read/write volume
1503      * can be picked up by the read/write volume */
1504     /* ACTUALLY, that's not done right now--the inodes just vanish */
1505     for (i = nVols - 1; i >= salvageTo; i--) {
1506         int rw = (i == 0);
1507         struct InodeSummary *lisp = &isp[i];
1508 #ifdef AFS_NAMEI_ENV
1509         /* If only the RO is present on this partition, the link table
1510          * shows up as a RW volume special file. Need to make sure the
1511          * salvager doesn't try to salvage the non-existent RW.
1512          */
1513         if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1514             /* If this only special inode is the link table, continue */
1515             if (inodes->u.special.type == VI_LINKTABLE) {
1516                 haveRWvolume = 0;
1517                 continue;
1518             }
1519         }
1520 #endif
1521         if (!Showmode)
1522             Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1523                 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1524         /* Check inodes twice.  The second time do things seriously.  This
1525          * way the whole RO volume can be deleted, below, if anything goes wrong */
1526         for (check = 1; check >= 0; check--) {
1527             int deleteMe;
1528             if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1529                 == -1) {
1530                 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1531                 if (rw && deleteMe) {
1532                     haveRWvolume = 0;   /* This will cause its inodes to be deleted--since salvage
1533                                          * volume won't be called */
1534                     break;
1535                 }
1536                 if (!rw)
1537                     break;
1538             }
1539             if (rw && check == 1)
1540                 continue;
1541             if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1542                 MaybeZapVolume(lisp, "Vnode index", 0, check);
1543                 break;
1544             }
1545         }
1546     }
1547
1548     /* Fix actual inode counts */
1549     if (!Showmode) {
1550         Log("totalInodes %d\n",totalInodes);
1551         for (ip = inodes; totalInodes; ip++, totalInodes--) {
1552             static int TraceBadLinkCounts = 0;
1553 #ifdef AFS_NAMEI_ENV
1554             if (VGLinkH->ih_ino == ip->inodeNumber) {
1555                 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1556                 VGLinkH_p1 = ip->u.param[0];
1557                 continue;       /* Deal with this last. */
1558             }
1559 #endif
1560             if (ip->linkCount != 0 && TraceBadLinkCounts) {
1561                 TraceBadLinkCounts--;   /* Limit reports, per volume */
1562                 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]);
1563             }
1564             while (ip->linkCount > 0) {
1565                 /* below used to assert, not break */
1566                 if (!Testing) {
1567                     if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1568                         Log("idec failed. inode %s errno %d\n",
1569                             PrintInode(NULL, ip->inodeNumber), errno);
1570                         break;
1571                     }
1572                 }
1573                 ip->linkCount--;
1574             }
1575             while (ip->linkCount < 0) {
1576                 /* these used to be asserts */
1577                 if (!Testing) {
1578                     if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1579                         Log("iinc failed. inode %s errno %d\n",
1580                             PrintInode(NULL, ip->inodeNumber), errno);
1581                         break;
1582                     }
1583                 }
1584                 ip->linkCount++;
1585             }
1586         }
1587 #ifdef AFS_NAMEI_ENV
1588         while (dec_VGLinkH > 0) {
1589             if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1590                 Log("idec failed on link table, errno = %d\n", errno);
1591             }
1592             dec_VGLinkH--;
1593         }
1594         while (dec_VGLinkH < 0) {
1595             if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1596                 Log("iinc failed on link table, errno = %d\n", errno);
1597             }
1598             dec_VGLinkH++;
1599         }
1600 #endif
1601     }
1602     free(inodes);
1603     /* Directory consistency checks on the rw volume */
1604     if (haveRWvolume)
1605         SalvageVolume(isp, VGLinkH);
1606     IH_RELEASE(VGLinkH);
1607
1608     if (canfork && !debug) {
1609         ShowLog = 0;
1610         Exit(0);
1611     }
1612 }
1613
1614 int
1615 QuickCheck(register struct InodeSummary *isp, int nVols)
1616 {
1617     /* Check headers BEFORE forking */
1618     register int i;
1619     IHandle_t *h;
1620
1621     for (i = 0; i < nVols; i++) {
1622         struct VolumeSummary *vs = isp[i].volSummary;
1623         VolumeDiskData volHeader;
1624         if (!vs) {
1625             /* Don't salvage just because phantom rw volume is there... */
1626             /* (If a read-only volume exists, read/write inodes must also exist) */
1627             if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1628                 continue;
1629             return 0;
1630         }
1631         IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1632         if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1633             == sizeof(volHeader)
1634             && volHeader.stamp.magic == VOLUMEINFOMAGIC
1635             && volHeader.dontSalvage == DONT_SALVAGE
1636             && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1637             if (volHeader.inUse != 0) {
1638                 volHeader.inUse = 0;
1639                 volHeader.inService = 1;
1640                 if (!Testing) {
1641                     if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1642                         != sizeof(volHeader)) {
1643                         IH_RELEASE(h);
1644                         return 0;
1645                     }
1646                 }
1647             }
1648             IH_RELEASE(h);
1649         } else {
1650             IH_RELEASE(h);
1651             return 0;
1652         }
1653     }
1654     return 1;
1655 }
1656
1657
1658 /* SalvageVolumeHeaderFile
1659  *
1660  * Salvage the top level V*.vol header file. Make sure the special files
1661  * exist and that there are no duplicates.
1662  *
1663  * Calls SalvageHeader for each possible type of volume special file.
1664  */
1665
1666 int
1667 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1668                         register struct ViceInodeInfo *inodes, int RW,
1669                         int check, int *deleteMe)
1670 {
1671     int headerFd = 0;
1672     int i;
1673     register struct ViceInodeInfo *ip;
1674     int allinodesobsolete = 1;
1675     struct VolumeDiskHeader diskHeader;
1676
1677     if (deleteMe)
1678         *deleteMe = 0;
1679     memset(&tempHeader, 0, sizeof(tempHeader));
1680     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1681     tempHeader.stamp.version = VOLUMEHEADERVERSION;
1682     tempHeader.id = isp->volumeId;
1683     tempHeader.parent = isp->RWvolumeId;
1684     /* Check for duplicates (inodes are sorted by type field) */
1685     for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1686         ip = &inodes[isp->index + i];
1687         if (ip->u.special.type == (ip + 1)->u.special.type) {
1688             if (!Showmode)
1689                 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1690             return -1;
1691         }
1692     }
1693     for (i = 0; i < isp->nSpecialInodes; i++) {
1694         ip = &inodes[isp->index + i];
1695         if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1696             if (check) {
1697                 Log("Rubbish header inode\n");
1698                 return -1;
1699             }
1700             Log("Rubbish header inode; deleted\n");
1701         } else if (!stuff[ip->u.special.type - 1].obsolete) {
1702             *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1703             if (!check && ip->u.special.type != VI_LINKTABLE)
1704                 ip->linkCount--;        /* Keep the inode around */
1705             allinodesobsolete = 0;
1706         }
1707     }
1708
1709     if (allinodesobsolete) {
1710         if (deleteMe)
1711             *deleteMe = 1;
1712         return -1;
1713     }
1714
1715     if (!check)
1716         VGLinkH_cnt++;          /* one for every header. */
1717
1718     if (!RW && !check && isp->volSummary) {
1719         ClearROInUseBit(isp->volSummary);
1720         return 0;
1721     }
1722
1723     for (i = 0; i < MAXINODETYPE; i++) {
1724         if (stuff[i].inodeType == VI_LINKTABLE) {
1725             /* Gross hack: SalvageHeader does a bcmp on the volume header.
1726              * And we may have recreated the link table earlier, so set the
1727              * RW header as well.
1728              */
1729             if (VALID_INO(VGLinkH->ih_ino)) {
1730                 *stuff[i].inode = VGLinkH->ih_ino;
1731             }
1732             continue;
1733         }
1734         if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1735             return -1;
1736     }
1737
1738     if (isp->volSummary == NULL) {
1739         char name[64];
1740         (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1741         if (check) {
1742             Log("No header file for volume %u\n", isp->volumeId);
1743             return -1;
1744         }
1745         if (!Showmode)
1746             Log("No header file for volume %u; %screating %s/%s\n",
1747                 isp->volumeId, (Testing ? "it would have been " : ""),
1748                 fileSysPathName, name);
1749         headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
1750         assert(headerFd != -1);
1751         isp->volSummary = (struct VolumeSummary *)
1752             malloc(sizeof(struct VolumeSummary));
1753         isp->volSummary->fileName = ToString(name);
1754     } else {
1755         char name[64];
1756         /* hack: these two fields are obsolete... */
1757         isp->volSummary->header.volumeAcl = 0;
1758         isp->volSummary->header.volumeMountTable = 0;
1759
1760         if (memcmp
1761             (&isp->volSummary->header, &tempHeader,
1762              sizeof(struct VolumeHeader))) {
1763             /* We often remove the name before calling us, so we make a fake one up */
1764             if (isp->volSummary->fileName) {
1765                 strcpy(name, isp->volSummary->fileName);
1766             } else {
1767                 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1768                 isp->volSummary->fileName = ToString(name);
1769             }
1770
1771             Log("Header file %s is damaged or no longer valid%s\n", name,
1772                 (check ? "" : "; repairing"));
1773             if (check)
1774                 return -1;
1775
1776             headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
1777             assert(headerFd != -1);
1778         }
1779     }
1780     if (headerFd) {
1781         memcpy(&isp->volSummary->header, &tempHeader,
1782                sizeof(struct VolumeHeader));
1783         if (Testing) {
1784             if (!Showmode)
1785                 Log("It would have written a new header file for volume %u\n",
1786                     isp->volumeId);
1787         } else {
1788             VolumeHeaderToDisk(&diskHeader, &tempHeader);
1789             if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1790                 != sizeof(struct VolumeDiskHeader)) {
1791                 Log("Couldn't rewrite volume header file!\n");
1792                 close(headerFd);
1793                 return -1;
1794             }
1795         }
1796         close(headerFd);
1797     }
1798     IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1799             isp->volSummary->header.volumeInfo);
1800     return 0;
1801 }
1802
1803 int
1804 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1805               int *deleteMe)
1806 {
1807     union {
1808         VolumeDiskData volumeInfo;
1809         struct versionStamp fileHeader;
1810     } header;
1811     IHandle_t *specH;
1812     int recreate = 0;
1813     afs_int32 code;
1814     FdHandle_t *fdP;
1815
1816     if (sp->obsolete)
1817         return 0;
1818 #ifndef AFS_NAMEI_ENV
1819     if (sp->inodeType == VI_LINKTABLE)
1820         return 0;
1821 #endif
1822     if (*(sp->inode) == 0) {
1823         if (check) {
1824             Log("Missing inode in volume header (%s)\n", sp->description);
1825             return -1;
1826         }
1827         if (!Showmode)
1828             Log("Missing inode in volume header (%s); %s\n", sp->description,
1829                 (Testing ? "it would have recreated it" : "recreating"));
1830         if (!Testing) {
1831             *(sp->inode) =
1832                 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1833                           INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1834             if (!VALID_INO(*(sp->inode)))
1835                 Abort
1836                     ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1837                      sp->description, errno);
1838         }
1839         recreate = 1;
1840     }
1841
1842     IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1843     fdP = IH_OPEN(specH);
1844     if (OKToZap && (fdP == NULL) && BadError(errno)) {
1845         /* bail out early and destroy the volume */
1846         if (!Showmode)
1847             Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1848         if (deleteMe)
1849             *deleteMe = 1;
1850         IH_RELEASE(specH);
1851         return -1;
1852     }
1853     if (fdP == NULL)
1854         Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1855               sp->description, errno);
1856
1857     if (!recreate
1858         && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1859             || header.fileHeader.magic != sp->stamp.magic)) {
1860         if (check) {
1861             Log("Part of the header (%s) is corrupted\n", sp->description);
1862             FDH_REALLYCLOSE(fdP);
1863             IH_RELEASE(specH);
1864             return -1;
1865         }
1866         Log("Part of the header (%s) is corrupted; recreating\n",
1867             sp->description);
1868         recreate = 1;
1869     }
1870     if (sp->inodeType == VI_VOLINFO
1871         && header.volumeInfo.destroyMe == DESTROY_ME) {
1872         if (deleteMe)
1873             *deleteMe = 1;
1874         FDH_REALLYCLOSE(fdP);
1875         IH_RELEASE(specH);
1876         return -1;
1877     }
1878     if (recreate && !Testing) {
1879         if (check)
1880             Abort
1881                 ("Internal error: recreating volume header (%s) in check mode\n",
1882                  sp->description);
1883         code = FDH_TRUNC(fdP, 0);
1884         if (code == -1)
1885             Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1886                   sp->description, errno);
1887
1888         /* The following code should be moved into vutil.c */
1889         if (sp->inodeType == VI_VOLINFO) {
1890             struct timeval tp;
1891             memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1892             header.volumeInfo.stamp = sp->stamp;
1893             header.volumeInfo.id = isp->volumeId;
1894             header.volumeInfo.parentId = isp->RWvolumeId;
1895             sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1896             Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1897                 isp->volumeId, isp->volumeId);
1898             header.volumeInfo.inService = 0;
1899             header.volumeInfo.blessed = 0;
1900             /* The + 1000 is a hack in case there are any files out in venus caches */
1901             header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1902             header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume);     /* XXXX */
1903             header.volumeInfo.needsCallback = 0;
1904             gettimeofday(&tp, 0);
1905             header.volumeInfo.creationDate = tp.tv_sec;
1906             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1907                 Abort
1908                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1909                      sp->description, errno);
1910             }
1911             code =
1912                 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1913                           sizeof(header.volumeInfo));
1914             if (code != sizeof(header.volumeInfo)) {
1915                 if (code < 0)
1916                     Abort
1917                         ("Unable to write volume header file (%s) (errno = %d)\n",
1918                          sp->description, errno);
1919                 Abort("Unable to write entire volume header file (%s)\n",
1920                       sp->description);
1921             }
1922         } else {
1923             if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1924                 Abort
1925                     ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1926                      sp->description, errno);
1927             }
1928             code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1929             if (code != sizeof(sp->stamp)) {
1930                 if (code < 0)
1931                     Abort
1932                         ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1933                          sp->description, errno);
1934                 Abort
1935                     ("Unable to write entire version stamp in volume header file (%s)\n",
1936                      sp->description);
1937             }
1938         }
1939     }
1940     FDH_REALLYCLOSE(fdP);
1941     IH_RELEASE(specH);
1942     if (sp->inodeType == VI_VOLINFO) {
1943         VolInfo = header.volumeInfo;
1944         if (check) {
1945             char update[25];
1946             if (VolInfo.updateDate) {
1947                 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1948                 if (!Showmode)
1949                     Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1950                         (Testing ? "it would have been " : ""), update);
1951             } else {
1952                 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1953                 if (!Showmode)
1954                     Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1955                         VolInfo.id, update);
1956             }
1957
1958         }
1959     }
1960
1961     return 0;
1962 }
1963
1964 int
1965 SalvageVnodes(register struct InodeSummary *rwIsp,
1966               register struct InodeSummary *thisIsp,
1967               register struct ViceInodeInfo *inodes, int check)
1968 {
1969     int ilarge, ismall, ioffset, RW, nInodes;
1970     ioffset = rwIsp->index + rwIsp->nSpecialInodes;     /* first inode */
1971     if (Showmode)
1972         return 0;
1973     RW = (rwIsp == thisIsp);
1974     nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1975     ismall =
1976         SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1977                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1978     if (check && ismall == -1)
1979         return -1;
1980     ilarge =
1981         SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1982                      &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1983     return (ilarge == 0 && ismall == 0 ? 0 : -1);
1984 }
1985
1986 int
1987 SalvageIndex(Inode ino, VnodeClass class, int RW,
1988              register struct ViceInodeInfo *ip, int nInodes,
1989              struct VolumeSummary *volSummary, int check)
1990 {
1991     VolumeId volumeNumber;
1992     char buf[SIZEOF_LARGEDISKVNODE];
1993     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1994     int err = 0;
1995     StreamHandle_t *file;
1996     struct VnodeClassInfo *vcp;
1997     afs_sfsize_t size;
1998     afs_fsize_t vnodeLength;
1999     int vnodeIndex, nVnodes;
2000     afs_ino_str_t stmp1, stmp2;
2001     IHandle_t *handle;
2002     FdHandle_t *fdP;
2003
2004     volumeNumber = volSummary->header.id;
2005     IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2006     fdP = IH_OPEN(handle);
2007     assert(fdP != NULL);
2008     file = FDH_FDOPEN(fdP, "r+");
2009     assert(file != NULL);
2010     vcp = &VnodeClassInfo[class];
2011     size = OS_SIZE(fdP->fd_fd);
2012     assert(size != -1);
2013     nVnodes = (size / vcp->diskSize) - 1;
2014     if (nVnodes > 0) {
2015         assert((nVnodes + 1) * vcp->diskSize == size);
2016         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2017     } else {
2018         nVnodes = 0;
2019     }
2020     for (vnodeIndex = 0;
2021          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2022          nVnodes--, vnodeIndex++) {
2023         if (vnode->type != vNull) {
2024             int vnodeChanged = 0;
2025             int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2026             /* Log programs that belong to root (potentially suid root);
2027              * don't bother for read-only or backup volumes */
2028 #ifdef  notdef                  /* This is done elsewhere */
2029             if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2030                 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);
2031 #endif
2032             if (VNDISK_GET_INO(vnode) == 0) {
2033                 if (RW) {
2034                     /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2035                     memset(vnode, 0, vcp->diskSize);
2036                     vnodeChanged = 1;
2037                 }
2038             } else {
2039                 if (vcp->magic != vnode->vnodeMagic) {
2040                     /* bad magic #, probably partially created vnode */
2041                     Log("Partially allocated vnode %d deleted.\n",
2042                         vnodeNumber);
2043                     memset(vnode, 0, vcp->diskSize);
2044                     vnodeChanged = 1;
2045                     goto vnodeDone;
2046                 }
2047                 /* ****** Should do a bit more salvage here:  e.g. make sure
2048                  * vnode type matches what it should be given the index */
2049                 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2050 /*                  if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2051  *                     Log("Inode %d: says it belongs to non-existing vnode %d\n",
2052  *                         ip->inodeNumber, ip->u.vnode.vnodeNumber);
2053  *                  }
2054  */
2055                     ip++;
2056                     nInodes--;
2057                 }
2058                 if (!RW) {
2059                     while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2060                         /* The following doesn't work, because the version number
2061                          * is not maintained correctly by the file server */
2062                         /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2063                          * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2064                          * break; */
2065                         if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2066                             break;
2067                         ip++;
2068                         nInodes--;
2069                     }
2070                 } else {
2071                     /* For RW volume, look for vnode with matching inode number;
2072                      * if no such match, take the first determined by our sort
2073                      * order */
2074                     register struct ViceInodeInfo *lip = ip;
2075                     register int lnInodes = nInodes;
2076                     while (lnInodes
2077                            && lip->u.vnode.vnodeNumber == vnodeNumber) {
2078                         if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2079                             ip = lip;
2080                             nInodes = lnInodes;
2081                             break;
2082                         }
2083                         lip++;
2084                         lnInodes--;
2085                     }
2086                 }
2087                 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2088                     /* "Matching" inode */
2089                     if (RW) {
2090                         Unique vu, iu;
2091                         FileVersion vd, id;
2092                         vu = vnode->uniquifier;
2093                         iu = ip->u.vnode.vnodeUniquifier;
2094                         vd = vnode->dataVersion;
2095                         id = ip->u.vnode.inodeDataVersion;
2096                         /*
2097                          * Because of the possibility of the uniquifier overflows (> 4M)
2098                          * we compare them modulo the low 22-bits; we shouldn't worry
2099                          * about mismatching since they shouldn't to many old 
2100                          * uniquifiers of the same vnode...
2101                          */
2102                         if (IUnique(vu) != IUnique(iu)) {
2103                             if (!Showmode) {
2104                                 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2105                             }
2106
2107                             vnode->uniquifier = iu;
2108 #ifdef  AFS_3DISPARES
2109                             vnode->dataVersion = (id >= vd ?
2110                                                   /* 90% of 2.1M */
2111                                                   ((id - vd) >
2112                                                    1887437 ? vd : id) :
2113                                                   /* 90% of 2.1M */
2114                                                   ((vd - id) >
2115                                                    1887437 ? id : vd));
2116 #else
2117 #if defined(AFS_SGI_EXMAG)
2118                             vnode->dataVersion = (id >= vd ?
2119                                                   /* 90% of 16M */
2120                                                   ((id - vd) >
2121                                                    15099494 ? vd : id) :
2122                                                   /* 90% of 16M */
2123                                                   ((vd - id) >
2124                                                    15099494 ? id : vd));
2125 #else
2126                             vnode->dataVersion = (id > vd ? id : vd);
2127 #endif /* AFS_SGI_EXMAG */
2128 #endif /* AFS_3DISPARES */
2129                             vnodeChanged = 1;
2130                         } else {
2131                             /* don't bother checking for vd > id any more, since
2132                              * partial file transfers always result in this state,
2133                              * and you can't do much else anyway (you've already
2134                              * found the best data you can) */
2135 #ifdef  AFS_3DISPARES
2136                             if (!vnodeIsDirectory(vnodeNumber)
2137                                 && ((vd < id && (id - vd) < 1887437)
2138                                     || ((vd > id && (vd - id) > 1887437)))) {
2139 #else
2140 #if defined(AFS_SGI_EXMAG)
2141                             if (!vnodeIsDirectory(vnodeNumber)
2142                                 && ((vd < id && (id - vd) < 15099494)
2143                                     || ((vd > id && (vd - id) > 15099494)))) {
2144 #else
2145                             if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2146 #endif /* AFS_SGI_EXMAG */
2147 #endif
2148                                 if (!Showmode)
2149                                     Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2150                                 vnode->dataVersion = id;
2151                                 vnodeChanged = 1;
2152                             }
2153                         }
2154                     }
2155                     if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2156                         if (check) {
2157                             if (!Showmode) {
2158                                 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);
2159                             }
2160                             VNDISK_SET_INO(vnode, ip->inodeNumber);
2161                             err = -1;
2162                             goto zooks;
2163                         }
2164                         if (!Showmode) {
2165                             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);
2166                         }
2167                         VNDISK_SET_INO(vnode, ip->inodeNumber);
2168                         vnodeChanged = 1;
2169                     }
2170                     VNDISK_GET_LEN(vnodeLength, vnode);
2171                     if (ip->byteCount != vnodeLength) {
2172                         if (check) {
2173                             if (!Showmode)
2174                                 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2175                             err = -1;
2176                             goto zooks;
2177                         }
2178                         if (!Showmode)
2179                             Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2180                         VNDISK_SET_LEN(vnode, ip->byteCount);
2181                         vnodeChanged = 1;
2182                     }
2183                     if (!check)
2184                         ip->linkCount--;        /* Keep the inode around */
2185                     ip++;
2186                     nInodes--;
2187                 } else {        /* no matching inode */
2188                     if (VNDISK_GET_INO(vnode) != 0
2189                         || vnode->type == vDirectory) {
2190                         /* No matching inode--get rid of the vnode */
2191                         if (check) {
2192                             if (VNDISK_GET_INO(vnode)) {
2193                                 if (!Showmode) {
2194                                     Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2195                                 }
2196                             } else {
2197                                 if (!Showmode)
2198                                     Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2199                             }
2200                             err = -1;
2201                             goto zooks;
2202                         }
2203                         if (VNDISK_GET_INO(vnode)) {
2204                             if (!Showmode) {
2205                                 time_t serverModifyTime = vnode->serverModifyTime;
2206                                 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));
2207                             }
2208                         } else {
2209                             if (!Showmode) {
2210                                 time_t serverModifyTime = vnode->serverModifyTime;
2211                                 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2212                             }
2213                         }
2214                         memset(vnode, 0, vcp->diskSize);
2215                         vnodeChanged = 1;
2216                     } else {
2217                         /* Should not reach here becuase we checked for 
2218                          * (inodeNumber == 0) above. And where we zero the vnode,
2219                          * we also goto vnodeDone.
2220                          */
2221                     }
2222                 }
2223                 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2224                     ip++;
2225                     nInodes--;
2226                 }
2227             }                   /* VNDISK_GET_INO(vnode) != 0 */
2228           vnodeDone:
2229             assert(!(vnodeChanged && check));
2230             if (vnodeChanged && !Testing) {
2231                 assert(IH_IWRITE
2232                        (handle, vnodeIndexOffset(vcp, vnodeNumber),
2233                         (char *)vnode, vcp->diskSize)
2234                        == vcp->diskSize);
2235                 VolumeChanged = 1;      /* For break call back */
2236             }
2237         }
2238     }
2239   zooks:
2240     STREAM_CLOSE(file);
2241     FDH_CLOSE(fdP);
2242     IH_RELEASE(handle);
2243     return err;
2244 }
2245
2246 struct VnodeEssence *
2247 CheckVnodeNumber(VnodeId vnodeNumber)
2248 {
2249     VnodeClass class;
2250     struct VnodeInfo *vip;
2251     int offset;
2252
2253     class = vnodeIdToClass(vnodeNumber);
2254     vip = &vnodeInfo[class];
2255     offset = vnodeIdToBitNumber(vnodeNumber);
2256     return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2257 }
2258
2259 void
2260 CopyOnWrite(register struct DirSummary *dir)
2261 {
2262     /* Copy the directory unconditionally if we are going to change it:
2263      * not just if was cloned.
2264      */
2265     struct VnodeDiskObject vnode;
2266     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2267     Inode oldinode, newinode;
2268     afs_sfsize_t code;
2269
2270     if (dir->copied || Testing)
2271         return;
2272     DFlush();                   /* Well justified paranoia... */
2273
2274     code =
2275         IH_IREAD(vnodeInfo[vLarge].handle,
2276                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2277                  sizeof(vnode));
2278     assert(code == sizeof(vnode));
2279     oldinode = VNDISK_GET_INO(&vnode);
2280     /* Increment the version number by a whole lot to avoid problems with
2281      * clients that were promised new version numbers--but the file server
2282      * crashed before the versions were written to disk.
2283      */
2284     newinode =
2285         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2286                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2287                   200);
2288     assert(VALID_INO(newinode));
2289     assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2290     vnode.cloned = 0;
2291     VNDISK_SET_INO(&vnode, newinode);
2292     code =
2293         IH_IWRITE(vnodeInfo[vLarge].handle,
2294                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2295                   sizeof(vnode));
2296     assert(code == sizeof(vnode));
2297
2298     SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2299                         fileSysDevice, newinode);
2300     /* Don't delete the original inode right away, because the directory is
2301      * still being scanned.
2302      */
2303     dir->copied = 1;
2304 }
2305
2306 /*
2307  * This function should either successfully create a new dir, or give up 
2308  * and leave things the way they were.  In particular, if it fails to write 
2309  * the new dir properly, it should return w/o changing the reference to the 
2310  * old dir.
2311  */
2312 void
2313 CopyAndSalvage(register struct DirSummary *dir)
2314 {
2315     struct VnodeDiskObject vnode;
2316     struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2317     Inode oldinode, newinode;
2318     DirHandle newdir;
2319     FdHandle_t *fdP;
2320     afs_int32 code;
2321     afs_sfsize_t lcode;
2322     afs_int32 parentUnique = 1;
2323     struct VnodeEssence *vnodeEssence;
2324     afs_fsize_t length;
2325
2326     if (Testing)
2327         return;
2328     Log("Salvaging directory %u...\n", dir->vnodeNumber);
2329     lcode =
2330         IH_IREAD(vnodeInfo[vLarge].handle,
2331                  vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2332                  sizeof(vnode));
2333     assert(lcode == sizeof(vnode));
2334     oldinode = VNDISK_GET_INO(&vnode);
2335     /* Increment the version number by a whole lot to avoid problems with
2336      * clients that were promised new version numbers--but the file server
2337      * crashed before the versions were written to disk.
2338      */
2339     newinode =
2340         IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2341                   dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2342                   200);
2343     assert(VALID_INO(newinode));
2344     SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2345
2346     /* Assign . and .. vnode numbers from dir and vnode.parent. 
2347      * The uniquifier for . is in the vnode.
2348      * The uniquifier for .. might be set to a bogus value of 1 and 
2349      * the salvager will later clean it up.
2350      */
2351     if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2352         parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2353     }
2354     code =
2355         DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2356                    vnode.uniquifier,
2357                    (vnode.parent ? vnode.parent : dir->vnodeNumber),
2358                    parentUnique);
2359     if (code == 0)
2360         code = DFlush();
2361     if (code) {
2362         /* didn't really build the new directory properly, let's just give up. */
2363         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2364         Log("Directory salvage returned code %d, continuing.\n", code);
2365         if (code) {
2366             Log("also failed to decrement link count on new inode");
2367         }
2368         assert(1 == 2);
2369     }
2370     Log("Checking the results of the directory salvage...\n");
2371     if (!DirOK(&newdir)) {
2372         Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2373         code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2374         assert(code == 0);
2375         assert(1 == 2);
2376     }
2377     vnode.cloned = 0;
2378     VNDISK_SET_INO(&vnode, newinode);
2379     length = Length(&newdir);
2380     VNDISK_SET_LEN(&vnode, length);
2381     lcode =
2382         IH_IWRITE(vnodeInfo[vLarge].handle,
2383                   vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2384                   sizeof(vnode));
2385     assert(lcode == sizeof(vnode));
2386 #if 0
2387 #ifdef AFS_NT40_ENV
2388     nt_sync(fileSysDevice);
2389 #else
2390     sync();                     /* this is slow, but hopefully rarely called.  We don't have
2391                                  * an open FD on the file itself to fsync.
2392                                  */
2393 #endif
2394 #else
2395     vnodeInfo[vLarge].handle->ih_synced = 1;
2396 #endif
2397     /* make sure old directory file is really closed */
2398     fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2399     FDH_REALLYCLOSE(fdP);
2400     
2401     code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2402     assert(code == 0);
2403     dir->dirHandle = newdir;
2404 }
2405
2406 int
2407 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2408            Unique unique)
2409 {
2410     struct VnodeEssence *vnodeEssence;
2411     afs_int32 dirOrphaned, todelete;
2412
2413     dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2414
2415     vnodeEssence = CheckVnodeNumber(vnodeNumber);
2416     if (vnodeEssence == NULL) {
2417         if (!Showmode) {
2418             Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2419         }
2420         if (!Testing) {
2421             CopyOnWrite(dir);
2422             assert(Delete(&dir->dirHandle, name) == 0);
2423         }
2424         return 0;
2425     }
2426 #ifdef AFS_AIX_ENV
2427 #ifndef AFS_NAMEI_ENV
2428     /* On AIX machines, don't allow entries to point to inode 0. That is a special 
2429      * mount inode for the partition. If this inode were deleted, it would crash
2430      * the machine.
2431      */
2432     if (vnodeEssence->InodeNumber == 0) {
2433         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"));
2434         if (!Testing) {
2435             CopyOnWrite(dir);
2436             assert(Delete(&dir->dirHandle, name) == 0);
2437         }
2438         return 0;
2439     }
2440 #endif
2441 #endif
2442
2443     if (!(vnodeNumber & 1) && !Showmode
2444         && !(vnodeEssence->count || vnodeEssence->unique
2445              || vnodeEssence->modeBits)) {
2446         Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2447             dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2448             vnodeNumber, unique,
2449             ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2450              ""));
2451         if (!unique) {
2452             if (!Testing) {
2453                 CopyOnWrite(dir);
2454                 assert(Delete(&dir->dirHandle, name) == 0);
2455             }
2456             return 0;
2457         }
2458     }
2459
2460     /* Check if the Uniquifiers match. If not, change the directory entry
2461      * so its unique matches the vnode unique. Delete if the unique is zero
2462      * or if the directory is orphaned.
2463      */
2464     if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2465         if (!vnodeEssence->unique
2466             && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2467             /* This is an orphaned directory. Don't delete the . or ..
2468              * entry. Otherwise, it will get created in the next 
2469              * salvage and deleted again here. So Just skip it.
2470              */
2471             return 0;
2472         }
2473
2474         todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2475
2476         if (!Showmode) {
2477             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")));
2478         }
2479         if (!Testing) {
2480             ViceFid fid;
2481             fid.Vnode = vnodeNumber;
2482             fid.Unique = vnodeEssence->unique;
2483             CopyOnWrite(dir);
2484             assert(Delete(&dir->dirHandle, name) == 0);
2485             if (!todelete)
2486                 assert(Create(&dir->dirHandle, name, &fid) == 0);
2487         }
2488         if (todelete)
2489             return 0;           /* no need to continue */
2490     }
2491
2492     if (strcmp(name, ".") == 0) {
2493         if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2494             ViceFid fid;
2495             if (!Showmode)
2496                 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2497             if (!Testing) {
2498                 CopyOnWrite(dir);
2499                 assert(Delete(&dir->dirHandle, ".") == 0);
2500                 fid.Vnode = dir->vnodeNumber;
2501                 fid.Unique = dir->unique;
2502                 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2503             }
2504
2505             vnodeNumber = fid.Vnode;    /* Get the new Essence */
2506             unique = fid.Unique;
2507             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2508         }
2509         dir->haveDot = 1;
2510     } else if (strcmp(name, "..") == 0) {
2511         ViceFid pa;
2512         if (dir->parent) {
2513             struct VnodeEssence *dotdot;
2514             pa.Vnode = dir->parent;
2515             dotdot = CheckVnodeNumber(pa.Vnode);
2516             assert(dotdot != NULL);     /* XXX Should not be assert */
2517             pa.Unique = dotdot->unique;
2518         } else {
2519             pa.Vnode = dir->vnodeNumber;
2520             pa.Unique = dir->unique;
2521         }
2522         if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2523             if (!Showmode)
2524                 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2525             if (!Testing) {
2526                 CopyOnWrite(dir);
2527                 assert(Delete(&dir->dirHandle, "..") == 0);
2528                 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2529             }
2530
2531             vnodeNumber = pa.Vnode;     /* Get the new Essence */
2532             unique = pa.Unique;
2533             vnodeEssence = CheckVnodeNumber(vnodeNumber);
2534         }
2535         dir->haveDotDot = 1;
2536     } else if (strncmp(name, ".__afs", 6) == 0) {
2537         if (!Showmode) {
2538             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);
2539         }
2540         if (!Testing) {
2541             CopyOnWrite(dir);
2542             assert(Delete(&dir->dirHandle, name) == 0);
2543         }
2544         vnodeEssence->claimed = 0;      /* Not claimed: Orphaned */
2545         vnodeEssence->todelete = 1;     /* Will later delete vnode and decr inode */
2546         return 0;
2547     } else {
2548         if (ShowSuid && (vnodeEssence->modeBits & 06000))
2549             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);
2550         if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
2551             && !(vnodeEssence->modeBits & 0111)) {
2552             int code, size;
2553             char buf[1025];
2554             IHandle_t *ihP;
2555             FdHandle_t *fdP;
2556
2557             IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2558                     vnodeEssence->InodeNumber);
2559             fdP = IH_OPEN(ihP);
2560             if (fdP == NULL) {
2561                 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
2562                 IH_RELEASE(ihP);
2563                 return 0;
2564             }
2565             size = FDH_SIZE(fdP);
2566             if (size < 0) {
2567                 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
2568                 FDH_REALLYCLOSE(fdP);
2569                 IH_RELEASE(ihP);
2570                 return 0;
2571             }
2572         
2573             if (size > 1024)
2574                 size = 1024;
2575             code = FDH_READ(fdP, buf, size);
2576             if (code == size) {
2577                 buf[size] = '\0';
2578                 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
2579                     Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
2580                         dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
2581                         Testing ? "would convert" : "converted");
2582                     vnodeEssence->modeBits |= 0111;
2583                     vnodeEssence->changed = 1;
2584                 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2585                     dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2586                     dir->name ? dir->name : "??", name, buf);
2587             } else {
2588                 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
2589                     dir->vname, vnodeNumber, size, code);
2590             }
2591             FDH_REALLYCLOSE(fdP);
2592             IH_RELEASE(ihP);
2593         }
2594         if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2595             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);
2596         if (vnodeIdToClass(vnodeNumber) == vLarge
2597             && vnodeEssence->name == NULL) {
2598             char *n;
2599             if ((n = (char *)malloc(strlen(name) + 1)))
2600                 strcpy(n, name);
2601             vnodeEssence->name = n;
2602         }
2603
2604         /* The directory entry points to the vnode. Check to see if the
2605          * vnode points back to the directory. If not, then let the 
2606          * directory claim it (else it might end up orphaned). Vnodes 
2607          * already claimed by another directory are deleted from this
2608          * directory: hardlinks to the same vnode are not allowed
2609          * from different directories.
2610          */
2611         if (vnodeEssence->parent != dir->vnodeNumber) {
2612             if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2613                 /* Vnode does not point back to this directory.
2614                  * Orphaned dirs cannot claim a file (it may belong to
2615                  * another non-orphaned dir).
2616                  */
2617                 if (!Showmode) {
2618                     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);
2619                 }
2620                 vnodeEssence->parent = dir->vnodeNumber;
2621                 vnodeEssence->changed = 1;
2622             } else {
2623                 /* Vnode was claimed by another directory */
2624                 if (!Showmode) {
2625                     if (dirOrphaned) {
2626                         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 " : ""));
2627                     } else if (vnodeNumber == 1) {
2628                         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 " : ""));
2629                     } else {
2630                         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 " : ""));
2631                     }
2632                 }
2633                 if (!Testing) {
2634                     CopyOnWrite(dir);
2635                     assert(Delete(&dir->dirHandle, name) == 0);
2636                 }
2637                 return 0;
2638             }
2639         }
2640         /* This directory claims the vnode */
2641         vnodeEssence->claimed = 1;
2642     }
2643     vnodeEssence->count--;
2644     return 0;
2645 }
2646
2647 void
2648 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2649 {
2650     register struct VnodeInfo *vip = &vnodeInfo[class];
2651     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2652     char buf[SIZEOF_LARGEDISKVNODE];
2653     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2654     int size;
2655     StreamHandle_t *file;
2656     int vnodeIndex;
2657     int nVnodes;
2658     FdHandle_t *fdP;
2659
2660     IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2661     fdP = IH_OPEN(vip->handle);
2662     assert(fdP != NULL);
2663     file = FDH_FDOPEN(fdP, "r+");
2664     assert(file != NULL);
2665     size = OS_SIZE(fdP->fd_fd);
2666     assert(size != -1);
2667     vip->nVnodes = (size / vcp->diskSize) - 1;
2668     if (vip->nVnodes > 0) {
2669         assert((vip->nVnodes + 1) * vcp->diskSize == size);
2670         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2671         assert((vip->vnodes = (struct VnodeEssence *)
2672                 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2673         if (class == vLarge) {
2674             assert((vip->inodes = (Inode *)
2675                     calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2676         } else {
2677             vip->inodes = NULL;
2678         }
2679     } else {
2680         vip->nVnodes = 0;
2681         vip->vnodes = NULL;
2682         vip->inodes = NULL;
2683     }
2684     vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2685     for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2686          nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2687          nVnodes--, vnodeIndex++) {
2688         if (vnode->type != vNull) {
2689             register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2690             afs_fsize_t vnodeLength;
2691             vip->nAllocatedVnodes++;
2692             vep->count = vnode->linkCount;
2693             VNDISK_GET_LEN(vnodeLength, vnode);
2694             vep->blockCount = nBlocks(vnodeLength);
2695             vip->volumeBlockCount += vep->blockCount;
2696             vep->parent = vnode->parent;
2697             vep->unique = vnode->uniquifier;
2698             if (*maxu < vnode->uniquifier)
2699                 *maxu = vnode->uniquifier;
2700             vep->modeBits = vnode->modeBits;
2701             vep->InodeNumber = VNDISK_GET_INO(vnode);
2702             vep->type = vnode->type;
2703             vep->author = vnode->author;
2704             vep->owner = vnode->owner;
2705             vep->group = vnode->group;
2706             if (vnode->type == vDirectory) {
2707                 assert(class == vLarge);
2708                 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2709             }
2710         }
2711     }
2712     STREAM_CLOSE(file);
2713     FDH_CLOSE(fdP);
2714 }
2715
2716 static char *
2717 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2718 {
2719     struct VnodeEssence *parentvp;
2720
2721     if (vnode == 1) {
2722         strcpy(path, ".");
2723         return path;
2724     }
2725     if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2726         && GetDirName(vp->parent, parentvp, path)) {
2727         strcat(path, "/");
2728         strcat(path, vp->name);
2729         return path;
2730     }
2731     return 0;
2732 }
2733
2734 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2735  * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2736  */
2737 static int
2738 IsVnodeOrphaned(VnodeId vnode)
2739 {
2740     struct VnodeEssence *vep;
2741
2742     if (vnode == 0)
2743         return (1);             /* Vnode zero does not exist */
2744     if (vnode == 1)
2745         return (0);             /* The root dir vnode is always claimed */
2746     vep = CheckVnodeNumber(vnode);      /* Get the vnode essence */
2747     if (!vep || !vep->claimed)
2748         return (1);             /* Vnode is not claimed - it is orphaned */
2749
2750     return (IsVnodeOrphaned(vep->parent));
2751 }
2752
2753 void
2754 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2755            IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2756            int *rootdirfound)
2757 {
2758     static struct DirSummary dir;
2759     static struct DirHandle dirHandle;
2760     struct VnodeEssence *parent;
2761     static char path[MAXPATHLEN];
2762     int dirok, code;
2763
2764     if (dirVnodeInfo->vnodes[i].salvaged)
2765         return;                 /* already salvaged */
2766
2767     dir.rwVid = rwVid;
2768     dirVnodeInfo->vnodes[i].salvaged = 1;
2769
2770     if (dirVnodeInfo->inodes[i] == 0)
2771         return;                 /* Not allocated to a directory */
2772
2773     if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2774         if (dirVnodeInfo->vnodes[i].parent) {
2775             Log("Bad parent, vnode 1; %s...\n",
2776                 (Testing ? "skipping" : "salvaging"));
2777             dirVnodeInfo->vnodes[i].parent = 0;
2778             dirVnodeInfo->vnodes[i].changed = 1;
2779         }
2780     } else {
2781         parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2782         if (parent && parent->salvaged == 0)
2783             SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2784                        vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2785                        rootdir, rootdirfound);
2786     }
2787
2788     dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2789     dir.unique = dirVnodeInfo->vnodes[i].unique;
2790     dir.copied = 0;
2791     dir.vname = name;
2792     dir.parent = dirVnodeInfo->vnodes[i].parent;
2793     dir.haveDot = dir.haveDotDot = 0;
2794     dir.ds_linkH = alinkH;
2795     SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2796                         dirVnodeInfo->inodes[i]);
2797
2798     dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2799     if (!dirok) {
2800         if (!RebuildDirs) {
2801             Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2802                 (Testing ? "skipping" : "salvaging"));
2803         }
2804         if (!Testing) {
2805             CopyAndSalvage(&dir);
2806             dirok = 1;
2807         }
2808     }
2809     dirHandle = dir.dirHandle;
2810
2811     dir.name =
2812         GetDirName(bitNumberToVnodeNumber(i, vLarge),
2813                    &dirVnodeInfo->vnodes[i], path);
2814
2815     if (dirok) {
2816         /* If enumeration failed for random reasons, we will probably delete
2817          * too much stuff, so we guard against this instead.
2818          */
2819         assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2820     }
2821
2822     /* Delete the old directory if it was copied in order to salvage.
2823      * CopyOnWrite has written the new inode # to the disk, but we still
2824      * have the old one in our local structure here.  Thus, we idec the
2825      * local dude.
2826      */
2827     DFlush();
2828     if (dir.copied && !Testing) {
2829         code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2830         assert(code == 0);
2831         dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2832     }
2833
2834     /* Remember rootdir DirSummary _after_ it has been judged */
2835     if (dir.vnodeNumber == 1 && dir.unique == 1) {
2836         memcpy(rootdir, &dir, sizeof(struct DirSummary));
2837         *rootdirfound = 1;
2838     }
2839
2840     return;
2841 }
2842
2843 int
2844 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2845 {
2846     /* This routine, for now, will only be called for read-write volumes */
2847     int i, j, code;
2848     int BlocksInVolume = 0, FilesInVolume = 0;
2849     register VnodeClass class;
2850     struct DirSummary rootdir, oldrootdir;
2851     struct VnodeInfo *dirVnodeInfo;
2852     struct VnodeDiskObject vnode;
2853     VolumeDiskData volHeader;
2854     VolumeId vid;
2855     int orphaned, rootdirfound = 0;
2856     Unique maxunique = 0;       /* the maxUniquifier from the vnodes */
2857     afs_int32 ofiles = 0, oblocks = 0;  /* Number of orphaned files/blocks */
2858     struct VnodeEssence *vep;
2859     afs_int32 v, pv;
2860     IHandle_t *h;
2861     afs_sfsize_t nBytes;
2862     ViceFid pa;
2863     VnodeId LFVnode, ThisVnode;
2864     Unique LFUnique, ThisUnique;
2865     char npath[128];
2866
2867     vid = rwIsp->volSummary->header.id;
2868     IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2869     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2870     assert(nBytes == sizeof(volHeader));
2871     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2872     assert(volHeader.destroyMe != DESTROY_ME);
2873     /* (should not have gotten this far with DESTROY_ME flag still set!) */
2874
2875     DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2876                        &maxunique);
2877     DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2878                        &maxunique);
2879
2880     dirVnodeInfo = &vnodeInfo[vLarge];
2881     for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2882         SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2883                    &rootdirfound);
2884     }
2885 #ifdef AFS_NT40_ENV
2886     nt_sync(fileSysDevice);
2887 #else
2888     sync();                             /* This used to be done lower level, for every dir */
2889 #endif
2890     if (Showmode) {
2891         IH_RELEASE(h);
2892         return 0;
2893     }
2894
2895     /* Parse each vnode looking for orphaned vnodes and
2896      * connect them to the tree as orphaned (if requested).
2897      */
2898     oldrootdir = rootdir;
2899     for (class = 0; class < nVNODECLASSES; class++) {
2900         for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2901             vep = &(vnodeInfo[class].vnodes[v]);
2902             ThisVnode = bitNumberToVnodeNumber(v, class);
2903             ThisUnique = vep->unique;
2904
2905             if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2906                 continue;       /* Ignore unused, claimed, and root vnodes */
2907
2908             /* This vnode is orphaned. If it is a directory vnode, then the '..'
2909              * entry in this vnode had incremented the parent link count (In
2910              * JudgeEntry()). We need to go to the parent and decrement that
2911              * link count. But if the parent's unique is zero, then the parent
2912              * link count was not incremented in JudgeEntry().
2913              */
2914             if (class == vLarge) {      /* directory vnode */
2915                 pv = vnodeIdToBitNumber(vep->parent);
2916                 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2917                     vnodeInfo[vLarge].vnodes[pv].count++;
2918             }
2919
2920             if (!rootdirfound)
2921                 continue;       /* If no rootdir, can't attach orphaned files */
2922
2923             /* Here we attach orphaned files and directories into the
2924              * root directory, LVVnode, making sure link counts stay correct.
2925              */
2926             if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2927                 LFVnode = rootdir.vnodeNumber;  /* Lost+Found vnode number */
2928                 LFUnique = rootdir.unique;      /* Lost+Found uniquifier */
2929
2930                 /* Update this orphaned vnode's info. Its parent info and 
2931                  * link count (do for orphaned directories and files).
2932                  */
2933                 vep->parent = LFVnode;  /* Parent is the root dir */
2934                 vep->unique = LFUnique;
2935                 vep->changed = 1;
2936                 vep->claimed = 1;
2937                 vep->count--;   /* Inc link count (root dir will pt to it) */
2938
2939                 /* If this orphaned vnode is a directory, change '..'. 
2940                  * The name of the orphaned dir/file is unknown, so we
2941                  * build a unique name. No need to CopyOnWrite the directory
2942                  * since it is not connected to tree in BK or RO volume and
2943                  * won't be visible there.
2944                  */
2945                 if (class == vLarge) {
2946                     ViceFid pa;
2947                     DirHandle dh;
2948
2949                     /* Remove and recreate the ".." entry in this orphaned directory */
2950                     SetSalvageDirHandle(&dh, vid, fileSysDevice,
2951                                         vnodeInfo[class].inodes[v]);
2952                     pa.Vnode = LFVnode;
2953                     pa.Unique = LFUnique;
2954                     assert(Delete(&dh, "..") == 0);
2955                     assert(Create(&dh, "..", &pa) == 0);
2956
2957                     /* The original parent's link count was decremented above.
2958                      * Here we increment the new parent's link count.
2959                      */
2960                     pv = vnodeIdToBitNumber(LFVnode);
2961                     vnodeInfo[vLarge].vnodes[pv].count--;
2962
2963                 }
2964
2965                 /* Go to the root dir and add this entry. The link count of the
2966                  * root dir was incremented when ".." was created. Try 10 times.
2967                  */
2968                 for (j = 0; j < 10; j++) {
2969                     pa.Vnode = ThisVnode;
2970                     pa.Unique = ThisUnique;
2971
2972                     (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2973                                        ((class ==
2974                                          vLarge) ? "__ORPHANDIR__" :
2975                                         "__ORPHANFILE__"), ThisVnode,
2976                                        ThisUnique);
2977
2978                     CopyOnWrite(&rootdir);
2979                     code = Create(&rootdir.dirHandle, npath, &pa);
2980                     if (!code)
2981                         break;
2982
2983                     ThisUnique += 50;   /* Try creating a different file */
2984                 }
2985                 assert(code == 0);
2986                 Log("Attaching orphaned %s to volume's root dir as %s\n",
2987                     ((class == vLarge) ? "directory" : "file"), npath);
2988             }
2989         }                       /* for each vnode in the class */
2990     }                           /* for each class of vnode */
2991
2992     /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2993     DFlush();
2994     if (!oldrootdir.copied && rootdir.copied) {
2995         code =
2996             IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2997                    oldrootdir.rwVid);
2998         assert(code == 0);
2999         /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3000     }
3001
3002     DFlush();                   /* Flush the changes */
3003     if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3004         Log("Cannot attach orphaned files and directories: Root directory not found\n");
3005         orphans = ORPH_IGNORE;
3006     }
3007
3008     /* Write out all changed vnodes. Orphaned files and directories
3009      * will get removed here also (if requested).
3010      */
3011     for (class = 0; class < nVNODECLASSES; class++) {
3012         int nVnodes = vnodeInfo[class].nVnodes;
3013         struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3014         struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3015         FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3016         BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3017         for (i = 0; i < nVnodes; i++) {
3018             register struct VnodeEssence *vnp = &vnodes[i];
3019             VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3020
3021             /* If the vnode is good but is unclaimed (not listed in
3022              * any directory entries), then it is orphaned.
3023              */
3024             orphaned = -1;
3025             if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3026                 vnp->claimed = 0;       /* Makes IsVnodeOrphaned calls faster */
3027                 vnp->changed = 1;
3028             }
3029
3030             if (vnp->changed || vnp->count) {
3031                 int oldCount;
3032                 int code;
3033                 nBytes =
3034                     IH_IREAD(vnodeInfo[class].handle,
3035                              vnodeIndexOffset(vcp, vnodeNumber),
3036                              (char *)&vnode, sizeof(vnode));
3037                 assert(nBytes == sizeof(vnode));
3038
3039                 vnode.parent = vnp->parent;
3040                 oldCount = vnode.linkCount;
3041                 vnode.linkCount = vnode.linkCount - vnp->count;
3042
3043                 if (orphaned == -1)
3044                     orphaned = IsVnodeOrphaned(vnodeNumber);
3045                 if (orphaned) {
3046                     if (!vnp->todelete) {
3047                         /* Orphans should have already been attached (if requested) */
3048                         assert(orphans != ORPH_ATTACH);
3049                         oblocks += vnp->blockCount;
3050                         ofiles++;
3051                     }
3052                     if (((orphans == ORPH_REMOVE) || vnp->todelete)
3053                         && !Testing) {
3054                         BlocksInVolume -= vnp->blockCount;
3055                         FilesInVolume--;
3056                         if (VNDISK_GET_INO(&vnode)) {
3057                             code =
3058                                 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3059                             assert(code == 0);
3060                         }
3061                         memset(&vnode, 0, sizeof(vnode));
3062                     }
3063                 } else if (vnp->count) {
3064                     if (!Showmode) {
3065                         Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3066                     }
3067                 } else {
3068                     vnode.modeBits = vnp->modeBits;
3069                 }
3070
3071                 vnode.dataVersion++;
3072                 if (!Testing) {
3073                     nBytes =
3074                         IH_IWRITE(vnodeInfo[class].handle,
3075                                   vnodeIndexOffset(vcp, vnodeNumber),
3076                                   (char *)&vnode, sizeof(vnode));
3077                     assert(nBytes == sizeof(vnode));
3078                 }
3079                 VolumeChanged = 1;
3080             }
3081         }
3082     }
3083     if (!Showmode && ofiles) {
3084         Log("%s %d orphaned files and directories (approx. %u KB)\n",
3085             (!Testing
3086              && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3087             oblocks);
3088     }
3089
3090     for (class = 0; class < nVNODECLASSES; class++) {
3091         register struct VnodeInfo *vip = &vnodeInfo[class];
3092         for (i = 0; i < vip->nVnodes; i++)
3093             if (vip->vnodes[i].name)
3094                 free(vip->vnodes[i].name);
3095         if (vip->vnodes)
3096             free(vip->vnodes);
3097         if (vip->inodes)
3098             free(vip->inodes);
3099     }
3100
3101     /* Set correct resource utilization statistics */
3102     volHeader.filecount = FilesInVolume;
3103     volHeader.diskused = BlocksInVolume;
3104
3105     /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3106     if (volHeader.uniquifier < (maxunique + 1)) {
3107         if (!Showmode)
3108             Log("Volume uniquifier is too low; fixed\n");
3109         /* Plus 2,000 in case there are workstations out there with
3110          * cached vnodes that have since been deleted
3111          */
3112         volHeader.uniquifier = (maxunique + 1 + 2000);
3113     }
3114
3115     /* Turn off the inUse bit; the volume's been salvaged! */
3116     volHeader.inUse = 0;        /* clear flag indicating inUse@last crash */
3117     volHeader.needsSalvaged = 0;        /* clear 'damaged' flag */
3118     volHeader.inService = 1;    /* allow service again */
3119     volHeader.needsCallback = (VolumeChanged != 0);
3120     volHeader.dontSalvage = DONT_SALVAGE;
3121     VolumeChanged = 0;
3122     if (!Testing) {
3123         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3124         assert(nBytes == sizeof(volHeader));
3125     }
3126     if (!Showmode) {
3127         Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3128             (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3129             FilesInVolume, BlocksInVolume);
3130     }
3131     IH_RELEASE(vnodeInfo[vSmall].handle);
3132     IH_RELEASE(vnodeInfo[vLarge].handle);
3133     IH_RELEASE(h);
3134     return 0;
3135 }
3136
3137 void
3138 ClearROInUseBit(struct VolumeSummary *summary)
3139 {
3140     IHandle_t *h = summary->volumeInfoHandle;
3141     afs_sfsize_t nBytes;
3142
3143     VolumeDiskData volHeader;
3144
3145     nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3146     assert(nBytes == sizeof(volHeader));
3147     assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3148     volHeader.inUse = 0;
3149     volHeader.needsSalvaged = 0;
3150     volHeader.inService = 1;
3151     volHeader.dontSalvage = DONT_SALVAGE;
3152     if (!Testing) {
3153         nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3154         assert(nBytes == sizeof(volHeader));
3155     }
3156 }
3157
3158 /* MaybeZapVolume
3159  * Possible delete the volume.
3160  *
3161  * deleteMe - Always do so, only a partial volume.
3162  */
3163 void
3164 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3165                int check)
3166 {
3167     if (readOnly(isp) || deleteMe) {
3168         if (isp->volSummary && isp->volSummary->fileName) {
3169             if (deleteMe) {
3170                 if (!Showmode)
3171                     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);
3172                 if (!Showmode)
3173                     Log("It will be deleted on this server (you may find it elsewhere)\n");
3174             } else {
3175                 if (!Showmode)
3176                     Log("Volume %u needs to be salvaged.  Since it is read-only, however,\n", isp->volumeId);
3177                 if (!Showmode)
3178                     Log("it will be deleted instead.  It should be recloned.\n");
3179             }
3180             if (!Testing)
3181                 unlink(isp->volSummary->fileName);
3182         }
3183     } else if (!check) {
3184         Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3185             isp->volumeId);
3186         Abort("Salvage of volume %u aborted\n", isp->volumeId);
3187     }
3188 }
3189
3190
3191 void
3192 AskOffline(VolumeId volumeId, char * partition)
3193 {
3194     afs_int32 code, i;
3195
3196     for (i = 0; i < 3; i++) {
3197         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3198
3199         if (code == SYNC_OK) {
3200             break;
3201         } else if (code == SYNC_DENIED) {
3202 #ifdef DEMAND_ATTACH_ENABLE
3203             Log("AskOffline:  file server denied offline request; a general salvage may be required.\n");
3204 #else
3205             Log("AskOffline:  file server denied offline request; a general salvage is required.\n");
3206 #endif
3207             Abort("Salvage aborted\n");
3208         } else if (code == SYNC_BAD_COMMAND) {
3209             Log("AskOffline:  fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3210                 FSYNC_VOL_OFF);
3211 #ifdef DEMAND_ATTACH_ENABLE
3212             Log("AskOffline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3213 #else
3214             Log("AskOffline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3215 #endif
3216             Abort("Salvage aborted\n");
3217         } else if (i < 2) {
3218             /* try it again */
3219             Log("AskOffline:  request for fileserver to take volume offline failed; trying again...\n");
3220             FSYNC_clientFinis();
3221             FSYNC_clientInit();
3222         }
3223     }
3224     if (code != SYNC_OK) {
3225         Log("AskOffline:  request for fileserver to take volume offline failed; salvage aborting.\n");
3226         Abort("Salvage aborted\n");
3227     }
3228 }
3229
3230 void
3231 AskOnline(VolumeId volumeId, char *partition)
3232 {
3233     afs_int32 code, i;
3234
3235     for (i = 0; i < 3; i++) {
3236         code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3237
3238         if (code == SYNC_OK) {
3239             break;
3240         } else if (code == SYNC_DENIED) {
3241             Log("AskOnline:  file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3242         } else if (code == SYNC_BAD_COMMAND) {
3243             Log("AskOnline:  fssync protocol mismatch (bad command word '%d')\n",
3244                 FSYNC_VOL_ON);
3245 #ifdef DEMAND_ATTACH_ENABLE
3246             Log("AskOnline:  please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3247 #else
3248             Log("AskOnline:  please make sure fileserver, volserver and salvager binaries are same version.\n");
3249 #endif
3250             break;
3251         } else if (i < 2) {
3252             /* try it again */
3253             Log("AskOnline:  request for fileserver to take volume offline failed; trying again...\n");
3254             FSYNC_clientFinis();
3255             FSYNC_clientInit();
3256         }
3257     }
3258 }
3259
3260 int
3261 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3262 {
3263     /* Volume parameter is passed in case iopen is upgraded in future to
3264      * require a volume Id to be passed
3265      */
3266     char buf[4096];
3267     IHandle_t *srcH, *destH;
3268     FdHandle_t *srcFdP, *destFdP;
3269     register int n = 0;
3270
3271     IH_INIT(srcH, device, rwvolume, inode1);
3272     srcFdP = IH_OPEN(srcH);
3273     assert(srcFdP != NULL);
3274     IH_INIT(destH, device, rwvolume, inode2);
3275     destFdP = IH_OPEN(destH);
3276     assert(n != -1);
3277     while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3278         assert(FDH_WRITE(destFdP, buf, n) == n);
3279     assert(n == 0);
3280     FDH_REALLYCLOSE(srcFdP);
3281     FDH_REALLYCLOSE(destFdP);
3282     IH_RELEASE(srcH);
3283     IH_RELEASE(destH);
3284     return 0;
3285 }
3286
3287 void
3288 PrintInodeList(void)
3289 {
3290     register struct ViceInodeInfo *ip;
3291     struct ViceInodeInfo *buf;
3292     struct afs_stat status;
3293     register nInodes;
3294
3295     assert(afs_fstat(inodeFd, &status) == 0);
3296     buf = (struct ViceInodeInfo *)malloc(status.st_size);
3297     assert(buf != NULL);
3298     nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3299     assert(read(inodeFd, buf, status.st_size) == status.st_size);
3300     for (ip = buf; nInodes--; ip++) {
3301         Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3302             PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3303             (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3304             ip->u.param[2], ip->u.param[3]);
3305     }
3306     free(buf);
3307 }
3308
3309 void
3310 PrintInodeSummary(void)
3311 {
3312     int i;
3313     struct InodeSummary *isp;
3314
3315     for (i = 0; i < nVolumesInInodeFile; i++) {
3316         isp = &inodeSummary[i];
3317         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);
3318     }
3319 }
3320
3321 void
3322 PrintVolumeSummary(void)
3323 {
3324     int i;
3325     struct VolumeSummary *vsp;
3326
3327     for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3328         Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3329     }
3330 }
3331
3332 int
3333 Fork(void)
3334 {
3335     int f;
3336 #ifdef AFS_NT40_ENV
3337     f = 0;
3338     assert(0);                  /* Fork is never executed in the NT code path */
3339 #else
3340     f = fork();
3341     assert(f >= 0);
3342 #ifdef AFS_DEMAND_ATTACH_FS
3343     if ((f == 0) && (programType == salvageServer)) {
3344         /* we are a salvageserver child */
3345 #ifdef FSSYNC_BUILD_CLIENT
3346         VChildProcReconnectFS_r();
3347 #endif
3348 #ifdef SALVSYNC_BUILD_CLIENT
3349         VReconnectSALV_r();
3350 #endif
3351     }
3352 #endif /* AFS_DEMAND_ATTACH_FS */
3353 #endif /* !AFS_NT40_ENV */
3354     return f;
3355 }
3356
3357 void
3358 Exit(code)
3359      int code;
3360 {
3361     if (ShowLog)
3362         showlog();
3363
3364 #ifdef AFS_DEMAND_ATTACH_FS
3365     if (programType == salvageServer) {
3366 #ifdef SALVSYNC_BUILD_CLIENT
3367         VDisconnectSALV();
3368 #endif
3369 #ifdef FSSYNC_BUILD_CLIENT
3370         VDisconnectFS();
3371 #endif
3372     }
3373 #endif /* AFS_DEMAND_ATTACH_FS */
3374
3375 #ifdef AFS_NT40_ENV
3376     if (main_thread != pthread_self())
3377         pthread_exit((void *)code);
3378     else
3379         exit(code);
3380 #else
3381     exit(code);
3382 #endif
3383 }
3384
3385 int
3386 Wait(char *prog)
3387 {
3388     int status;
3389     int pid;
3390     pid = wait(&status);
3391     assert(pid != -1);
3392     if (WCOREDUMP(status))
3393         Log("\"%s\" core dumped!\n", prog);
3394     if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3395         return -1;
3396     return pid;
3397 }
3398
3399 static char *
3400 TimeStamp(time_t clock, int precision)
3401 {
3402     struct tm *lt;
3403     static char timestamp[20];
3404     lt = localtime(&clock);
3405     if (precision)
3406         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
3407     else
3408         (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3409     return timestamp;
3410 }
3411
3412 void
3413 CheckLogFile(char * log_path)
3414 {
3415     char oldSlvgLog[AFSDIR_PATH_MAX];
3416
3417 #ifndef AFS_NT40_ENV
3418     if (useSyslog) {
3419         ShowLog = 0;
3420         return;
3421     }
3422 #endif
3423
3424     strcpy(oldSlvgLog, log_path);
3425     strcat(oldSlvgLog, ".old");
3426     if (!logFile) {
3427         renamefile(log_path, oldSlvgLog);
3428         logFile = afs_fopen(log_path, "a");
3429
3430         if (!logFile) {         /* still nothing, use stdout */
3431             logFile = stdout;
3432             ShowLog = 0;
3433         }
3434 #ifndef AFS_NAMEI_ENV
3435         AFS_DEBUG_IOPS_LOG(logFile);
3436 #endif
3437     }
3438 }
3439
3440 #ifndef AFS_NT40_ENV
3441 void
3442 TimeStampLogFile(char * log_path)
3443 {
3444     char stampSlvgLog[AFSDIR_PATH_MAX];
3445     struct tm *lt;
3446     time_t now;
3447
3448     now = time(0);
3449     lt = localtime(&now);
3450     (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3451                        "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3452                        log_path, lt->tm_year + 1900,
3453                        lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3454                        lt->tm_sec);
3455
3456     /* try to link the logfile to a timestamped filename */
3457     /* if it fails, oh well, nothing we can do */
3458     link(log_path, stampSlvgLog);
3459 }
3460 #endif
3461
3462 void
3463 showlog(void)
3464 {
3465     char line[256];
3466
3467 #ifndef AFS_NT40_ENV
3468     if (useSyslog) {
3469         printf("Can't show log since using syslog.\n");
3470         fflush(stdout);
3471         return;
3472     }
3473 #endif
3474
3475     rewind(logFile);
3476     fclose(logFile);
3477
3478     logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3479
3480     if (!logFile)
3481         printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3482     else {
3483         rewind(logFile);
3484         while (fgets(line, sizeof(line), logFile))
3485             printf("%s", line);
3486         fflush(stdout);
3487     }
3488 }
3489
3490 void
3491 Log(const char *format, ...)
3492 {
3493     struct timeval now;
3494     char tmp[1024];
3495     va_list args;
3496
3497     va_start(args, format);
3498     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3499     va_end(args);
3500 #ifndef AFS_NT40_ENV
3501     if (useSyslog) {
3502         syslog(LOG_INFO, "%s", tmp);
3503     } else
3504 #endif
3505         if (logFile) {
3506             gettimeofday(&now, 0);
3507             fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3508             fflush(logFile);
3509         }
3510 }
3511
3512 void
3513 Abort(const char *format, ...)
3514 {
3515     va_list args;
3516     char tmp[1024];
3517
3518     va_start(args, format);
3519     (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3520     va_end(args);
3521 #ifndef AFS_NT40_ENV
3522     if (useSyslog) {
3523         syslog(LOG_INFO, "%s", tmp);
3524     } else
3525 #endif
3526         if (logFile) {
3527             fprintf(logFile, "%s", tmp);
3528             fflush(logFile);
3529             if (ShowLog)
3530                 showlog();
3531         }
3532
3533     if (debug)
3534         abort();
3535     Exit(1);
3536 }
3537
3538 char *
3539 ToString(char *s)
3540 {
3541     register char *p;
3542     p = (char *)malloc(strlen(s) + 1);
3543     assert(p != NULL);
3544     strcpy(p, s);
3545     return p;
3546
3547 }
3548
3549 /* Remove the FORCESALVAGE file */
3550 void
3551 RemoveTheForce(char *path)
3552 {
3553     if (!Testing && ForceSalvage) {
3554         if (chdir(path) == 0)
3555             unlink("FORCESALVAGE");
3556     }
3557 }
3558
3559 #ifndef AFS_AIX32_ENV
3560 /*
3561  * UseTheForceLuke -    see if we can use the force
3562  */
3563 int
3564 UseTheForceLuke(char *path)
3565 {
3566     struct afs_stat force;
3567
3568     assert(chdir(path) != -1);
3569
3570     return (afs_stat("FORCESALVAGE", &force) == 0);
3571 }
3572 #else
3573 /*
3574  * UseTheForceLuke -    see if we can use the force
3575  *
3576  * NOTE:
3577  *      The VRMIX fsck will not muck with the filesystem it is supposedly
3578  *      fixing and create a "FORCESALVAGE" file (by design).  Instead, we
3579  *      muck directly with the root inode, which is within the normal
3580  *      domain of fsck.
3581  *      ListViceInodes() has a side effect of setting ForceSalvage if
3582  *      it detects a need, based on root inode examination.
3583  */
3584 int
3585 UseTheForceLuke(char *path)
3586 {
3587
3588     return 0;                   /* sorry OB1    */
3589 }
3590 #endif
3591
3592 #ifdef AFS_NT40_ENV
3593 /* NT support routines */
3594
3595 static char execpathname[MAX_PATH];
3596 int
3597 nt_SalvagePartition(char *partName, int jobn)
3598 {
3599     int pid;
3600     int n;
3601     childJob_t job;
3602     if (!*execpathname) {
3603         n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3604         if (!n || n == 1023)
3605             return -1;
3606     }
3607     job.cj_magic = SALVAGER_MAGIC;
3608     job.cj_number = jobn;
3609     (void)strcpy(job.cj_part, partName);
3610     pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3611     return pid;
3612 }
3613
3614 int
3615 nt_SetupPartitionSalvage(void *datap, int len)
3616 {
3617     childJob_t *jobp = (childJob_t *) datap;
3618     char logname[AFSDIR_PATH_MAX];
3619
3620     if (len != sizeof(childJob_t))
3621         return -1;
3622     if (jobp->cj_magic != SALVAGER_MAGIC)
3623         return -1;
3624     myjob = *jobp;
3625
3626     /* Open logFile */
3627     (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3628                   myjob.cj_number);
3629     logFile = afs_fopen(logname, "w");
3630     if (!logFile)
3631         logFile = stdout;
3632
3633     return 0;
3634 }
3635
3636
3637 #endif /* AFS_NT40_ENV */