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