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