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