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