2 * Copyright 2000, International Business Machines Corporation and others.
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
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
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.
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).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
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.
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.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
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
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
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.
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.
86 #include <afsconfig.h>
87 #include <afs/param.h>
91 #include <sys/param.h>
95 #endif /* ITIMER_REAL */
101 #include <sys/stat.h>
106 #include <WINNT/afsevent.h>
109 #define WCOREDUMP(x) ((x) & 0200)
112 #include <afs/afsint.h>
113 #include <afs/assert.h>
114 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
115 #if defined(AFS_VFSINCL_ENV)
116 #include <sys/vnode.h>
118 #include <sys/fs/ufs_inode.h>
120 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
121 #include <ufs/ufs/dinode.h>
122 #include <ufs/ffs/fs.h>
124 #include <ufs/inode.h>
127 #else /* AFS_VFSINCL_ENV */
129 #include <ufs/inode.h>
130 #else /* AFS_OSF_ENV */
131 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
132 #include <sys/inode.h>
135 #endif /* AFS_VFSINCL_ENV */
136 #endif /* AFS_SGI_ENV */
139 #include <sys/lockf.h>
143 #include <checklist.h>
145 #if defined(AFS_SGI_ENV)
150 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
153 #include <sys/mnttab.h>
154 #include <sys/mntent.h>
159 #endif /* AFS_SGI_ENV */
160 #endif /* AFS_HPUX_ENV */
165 #include <afs/osi_inode.h>
169 #include <afs/afsutil.h>
170 #include <afs/fileutil.h>
171 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
179 #include <afs/afssyscalls.h>
183 #include "partition.h"
184 #include "daemon_com.h"
186 #include "salvsync.h"
187 #include "viceinode.h"
189 #include "volinodes.h" /* header magic number, etc. stuff */
190 #include "vol-salvage.h"
191 #include "vol_internal.h"
193 #ifdef FSSYNC_BUILD_CLIENT
194 #include "vg_cache.h"
201 /*@+fcnmacros +macrofcndecl@*/
204 extern off64_t afs_lseek(int FD, off64_t O, int F);
205 #endif /*S_SPLINT_S */
206 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
207 #define afs_stat stat64
208 #define afs_fstat fstat64
209 #define afs_open open64
210 #define afs_fopen fopen64
211 #else /* !O_LARGEFILE */
213 extern off_t afs_lseek(int FD, off_t O, int F);
214 #endif /*S_SPLINT_S */
215 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
216 #define afs_stat stat
217 #define afs_fstat fstat
218 #define afs_open open
219 #define afs_fopen fopen
220 #endif /* !O_LARGEFILE */
221 /*@=fcnmacros =macrofcndecl@*/
224 extern void *calloc();
226 static char *TimeStamp(time_t clock, int precision);
229 int debug; /* -d flag */
230 extern int Testing; /* -n flag */
231 int ListInodeOption; /* -i flag */
232 int ShowRootFiles; /* -r flag */
233 int RebuildDirs; /* -sal flag */
234 int Parallel = 4; /* -para X flag */
235 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
236 int forceR = 0; /* -b flag */
237 int ShowLog = 0; /* -showlog flag */
238 int ShowSuid = 0; /* -showsuid flag */
239 int ShowMounts = 0; /* -showmounts flag */
240 int orphans = ORPH_IGNORE; /* -orphans option */
245 int useSyslog = 0; /* -syslog flag */
246 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
255 #define MAXPARALLEL 32
257 int OKToZap; /* -o flag */
258 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
259 * in the volume header */
261 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
263 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
265 Device fileSysDevice; /* The device number of the current
266 * partition being salvaged */
270 char *fileSysPath; /* The path of the mounted partition currently
271 * being salvaged, i.e. the directory
272 * containing the volume headers */
274 char *fileSysPathName; /* NT needs this to make name pretty in log. */
275 IHandle_t *VGLinkH; /* Link handle for current volume group. */
276 int VGLinkH_cnt; /* # of references to lnk handle. */
277 struct DiskPartition64 *fileSysPartition; /* Partition being salvaged */
279 char *fileSysDeviceName; /* The block device where the file system
280 * being salvaged was mounted */
281 char *filesysfulldev;
283 int VolumeChanged; /* Set by any routine which would change the volume in
284 * a way which would require callback is to be broken if the
285 * volume was put back on line by an active file server */
287 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
289 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
290 int inodeFd; /* File descriptor for inode file */
293 struct VnodeInfo vnodeInfo[nVNODECLASSES];
296 struct VolumeSummary *volumeSummaryp = NULL; /* Holds all the volumes in a part */
297 int nVolumes; /* Number of volumes (read-write and read-only)
298 * in volume summary */
304 /* Forward declarations */
305 /*@printflike@*/ void Log(const char *format, ...);
306 /*@printflike@*/ void Abort(const char *format, ...);
307 static int IsVnodeOrphaned(VnodeId vnode);
308 static int AskVolumeSummary(VolumeId singleVolumeNumber);
310 /* Uniquifier stored in the Inode */
315 return (u & 0x3fffff);
317 #if defined(AFS_SGI_EXMAG)
318 return (u & SGI_UNIQMASK);
321 #endif /* AFS_SGI_EXMAG */
326 BadError(register int aerror)
328 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
330 return 0; /* otherwise may be transient, e.g. EMFILE */
335 char *save_args[MAX_ARGS];
337 extern pthread_t main_thread;
338 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
342 * Get the salvage lock if not already held. Hold until process exits.
344 * @param[in] locktype READ_LOCK or WRITE_LOCK
347 _ObtainSalvageLock(int locktype)
349 struct VLockFile salvageLock;
354 VLockFileInit(&salvageLock, AFSDIR_SERVER_SLVGLOCK_FILEPATH);
356 code = VLockFileLock(&salvageLock, offset, locktype, nonblock);
359 "salvager: There appears to be another salvager running! "
364 "salvager: Error %d trying to acquire salvage lock! "
370 ObtainSalvageLock(void)
372 _ObtainSalvageLock(WRITE_LOCK);
375 ObtainSharedSalvageLock(void)
377 _ObtainSalvageLock(READ_LOCK);
381 #ifdef AFS_SGI_XFS_IOPS_ENV
382 /* Check if the given partition is mounted. For XFS, the root inode is not a
383 * constant. So we check the hard way.
386 IsPartitionMounted(char *part)
389 struct mntent *mntent;
391 assert(mntfp = setmntent(MOUNTED, "r"));
392 while (mntent = getmntent(mntfp)) {
393 if (!strcmp(part, mntent->mnt_dir))
398 return mntent ? 1 : 1;
401 /* Check if the given inode is the root of the filesystem. */
402 #ifndef AFS_SGI_XFS_IOPS_ENV
404 IsRootInode(struct afs_stat *status)
407 * The root inode is not a fixed value in XFS partitions. So we need to
408 * see if the partition is in the list of mounted partitions. This only
409 * affects the SalvageFileSys path, so we check there.
411 return (status->st_ino == ROOTINODE);
416 #ifndef AFS_NAMEI_ENV
417 /* We don't want to salvage big files filesystems, since we can't put volumes on
421 CheckIfBigFilesFS(char *mountPoint, char *devName)
423 struct superblock fs;
426 if (strncmp(devName, "/dev/", 5)) {
427 (void)sprintf(name, "/dev/%s", devName);
429 (void)strcpy(name, devName);
432 if (ReadSuper(&fs, name) < 0) {
433 Log("Unable to read superblock. Not salvaging partition %s.\n",
437 if (IsBigFilesFileSystem(&fs)) {
438 Log("Partition %s is a big files filesystem, not salvaging.\n",
448 #define HDSTR "\\Device\\Harddisk"
449 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
451 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
456 static int dowarn = 1;
458 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
460 if (strncmp(res, HDSTR, HDLEN)) {
463 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
464 res, HDSTR, p1->devName);
468 d1 = atoi(&res[HDLEN]);
470 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
472 if (strncmp(res, HDSTR, HDLEN)) {
475 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
476 res, HDSTR, p2->devName);
480 d2 = atoi(&res[HDLEN]);
485 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
488 /* This assumes that two partitions with the same device number divided by
489 * PartsPerDisk are on the same disk.
492 SalvageFileSysParallel(struct DiskPartition64 *partP)
495 struct DiskPartition64 *partP;
496 int pid; /* Pid for this job */
497 int jobnumb; /* Log file job number */
498 struct job *nextjob; /* Next partition on disk to salvage */
500 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
501 struct job *thisjob = 0;
502 static int numjobs = 0;
503 static int jobcount = 0;
509 char logFileName[256];
513 /* We have a partition to salvage. Copy it into thisjob */
514 thisjob = (struct job *)malloc(sizeof(struct job));
516 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
519 memset(thisjob, 0, sizeof(struct job));
520 thisjob->partP = partP;
521 thisjob->jobnumb = jobcount;
523 } else if (jobcount == 0) {
524 /* We are asking to wait for all jobs (partp == 0), yet we never
527 Log("No file system partitions named %s* found; not salvaged\n",
528 VICE_PARTITION_PREFIX);
532 if (debug || Parallel == 1) {
534 SalvageFileSys(thisjob->partP, 0);
541 /* Check to see if thisjob is for a disk that we are already
542 * salvaging. If it is, link it in as the next job to do. The
543 * jobs array has 1 entry per disk being salvages. numjobs is
544 * the total number of disks currently being salvaged. In
545 * order to keep thejobs array compact, when a disk is
546 * completed, the hightest element in the jobs array is moved
547 * down to now open slot.
549 for (j = 0; j < numjobs; j++) {
550 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
551 /* On same disk, add it to this list and return */
552 thisjob->nextjob = jobs[j]->nextjob;
553 jobs[j]->nextjob = thisjob;
560 /* Loop until we start thisjob or until all existing jobs are finished */
561 while (thisjob || (!partP && (numjobs > 0))) {
562 startjob = -1; /* No new job to start */
564 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
565 /* Either the max jobs are running or we have to wait for all
566 * the jobs to finish. In either case, we wait for at least one
567 * job to finish. When it's done, clean up after it.
569 pid = wait(&wstatus);
571 for (j = 0; j < numjobs; j++) { /* Find which job it is */
572 if (pid == jobs[j]->pid)
576 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
577 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
580 numjobs--; /* job no longer running */
581 oldjob = jobs[j]; /* remember */
582 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
583 free(oldjob); /* free the old job */
585 /* If there is another partition on the disk to salvage, then
586 * say we will start it (startjob). If not, then put thisjob there
587 * and say we will start it.
589 if (jobs[j]) { /* Another partitions to salvage */
590 startjob = j; /* Will start it */
591 } else { /* There is not another partition to salvage */
593 jobs[j] = thisjob; /* Add thisjob */
595 startjob = j; /* Will start it */
597 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
598 startjob = -1; /* Don't start it - already running */
602 /* We don't have to wait for a job to complete */
604 jobs[numjobs] = thisjob; /* Add this job */
606 startjob = numjobs; /* Will start it */
610 /* Start up a new salvage job on a partition in job slot "startjob" */
611 if (startjob != -1) {
613 Log("Starting salvage of file system partition %s\n",
614 jobs[startjob]->partP->name);
616 /* For NT, we not only fork, but re-exec the salvager. Pass in the
617 * commands and pass the child job number via the data path.
620 nt_SalvagePartition(jobs[startjob]->partP->name,
621 jobs[startjob]->jobnumb);
622 jobs[startjob]->pid = pid;
627 jobs[startjob]->pid = pid;
633 for (fd = 0; fd < 16; fd++)
640 openlog("salvager", LOG_PID, useSyslogFacility);
644 (void)afs_snprintf(logFileName, sizeof logFileName,
646 AFSDIR_SERVER_SLVGLOG_FILEPATH,
647 jobs[startjob]->jobnumb);
648 logFile = afs_fopen(logFileName, "w");
653 SalvageFileSys1(jobs[startjob]->partP, 0);
658 } /* while ( thisjob || (!partP && numjobs > 0) ) */
660 /* If waited for all jobs to complete, now collect log files and return */
662 if (!useSyslog) /* if syslogging - no need to collect */
665 for (i = 0; i < jobcount; i++) {
666 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
667 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
668 if ((passLog = afs_fopen(logFileName, "r"))) {
669 while (fgets(buf, sizeof(buf), passLog)) {
674 (void)unlink(logFileName);
683 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
685 if (!canfork || debug || Fork() == 0) {
686 SalvageFileSys1(partP, singleVolumeNumber);
687 if (canfork && !debug) {
692 Wait("SalvageFileSys");
696 get_DevName(char *pbuffer, char *wpath)
698 char pbuf[128], *ptr;
699 strcpy(pbuf, pbuffer);
700 ptr = (char *)strrchr(pbuf, '/');
706 ptr = (char *)strrchr(pbuffer, '/');
708 strcpy(pbuffer, ptr + 1);
715 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
718 char inodeListPath[256];
720 static char tmpDevName[100];
721 static char wpath[100];
722 struct VolumeSummary *vsp, *esp;
726 fileSysPartition = partP;
727 fileSysDevice = fileSysPartition->device;
728 fileSysPathName = VPartitionPath(fileSysPartition);
731 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
732 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
733 name = partP->devName;
735 fileSysPath = fileSysPathName;
736 strcpy(tmpDevName, partP->devName);
737 name = get_DevName(tmpDevName, wpath);
738 fileSysDeviceName = name;
739 filesysfulldev = wpath;
742 VLockPartition(partP->name);
743 if (singleVolumeNumber || ForceSalvage)
746 ForceSalvage = UseTheForceLuke(fileSysPath);
748 if (singleVolumeNumber) {
749 /* salvageserver already setup fssync conn for us */
750 if ((programType != salvageServer) && !VConnectFS()) {
751 Abort("Couldn't connect to file server\n");
753 AskOffline(singleVolumeNumber, partP->name);
756 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
757 partP->name, name, (Testing ? "(READONLY mode)" : ""));
759 Log("***Forced salvage of all volumes on this partition***\n");
764 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
771 assert((dirp = opendir(fileSysPath)) != NULL);
772 while ((dp = readdir(dirp))) {
773 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
774 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
776 Log("Removing old salvager temp files %s\n", dp->d_name);
777 strcpy(npath, fileSysPath);
779 strcat(npath, dp->d_name);
785 tdir = (tmpdir ? tmpdir : fileSysPath);
787 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
788 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
790 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
794 inodeFile = fopen(inodeListPath, "w+b");
796 Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
799 /* Using nt_unlink here since we're really using the delete on close
800 * semantics of unlink. In most places in the salvager, we really do
801 * mean to unlink the file at that point. Those places have been
802 * modified to actually do that so that the NT crt can be used there.
804 code = nt_unlink(inodeListPath);
806 code = unlink(inodeListPath);
809 Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
812 if (GetInodeSummary(inodeFile, singleVolumeNumber) < 0) {
816 inodeFd = fileno(inodeFile);
818 Abort("Temporary file %s is missing...\n", inodeListPath);
819 afs_lseek(inodeFd, 0L, SEEK_SET);
820 if (ListInodeOption) {
824 /* enumerate volumes in the partition.
825 * figure out sets of read-only + rw volumes.
826 * salvage each set, read-only volumes first, then read-write.
827 * Fix up inodes on last volume in set (whether it is read-write
830 GetVolumeSummary(singleVolumeNumber);
832 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
833 i < nVolumesInInodeFile; i = j) {
834 VolumeId rwvid = inodeSummary[i].RWvolumeId;
836 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
838 VolumeId vid = inodeSummary[j].volumeId;
839 struct VolumeSummary *tsp;
840 /* Scan volume list (from partition root directory) looking for the
841 * current rw volume number in the volume list from the inode scan.
842 * If there is one here that is not in the inode volume list,
844 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
846 DeleteExtraVolumeHeaderFile(vsp);
848 /* Now match up the volume summary info from the root directory with the
849 * entry in the volume list obtained from scanning inodes */
850 inodeSummary[j].volSummary = NULL;
851 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
852 if (tsp->header.id == vid) {
853 inodeSummary[j].volSummary = tsp;
859 /* Salvage the group of volumes (several read-only + 1 read/write)
860 * starting with the current read-only volume we're looking at.
862 SalvageVolumeGroup(&inodeSummary[i], j - i);
865 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
866 for (; vsp < esp; vsp++) {
868 DeleteExtraVolumeHeaderFile(vsp);
871 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
872 RemoveTheForce(fileSysPath);
874 if (!Testing && singleVolumeNumber) {
875 AskOnline(singleVolumeNumber, fileSysPartition->name);
877 /* Step through the volumeSummary list and set all volumes on-line.
878 * The volumes were taken off-line in GetVolumeSummary.
880 for (j = 0; j < nVolumes; j++) {
881 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
885 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
886 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
889 fclose(inodeFile); /* SalvageVolumeGroup was the last which needed it. */
893 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
896 sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
899 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
902 code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
904 Log("Error %ld destroying volume disk header for volume %lu\n",
905 afs_printable_int32_ld(code),
906 afs_printable_uint32_lu(vsp->header.id));
909 /* make sure we actually delete the fileName file; ENOENT
910 * is fine, since VDestroyVolumeDiskHeader probably already
912 if (unlink(path) && errno != ENOENT) {
913 Log("Unable to unlink %s (errno = %d)\n", path, errno);
920 CompareInodes(const void *_p1, const void *_p2)
922 register const struct ViceInodeInfo *p1 = _p1;
923 register const struct ViceInodeInfo *p2 = _p2;
924 if (p1->u.vnode.vnodeNumber == INODESPECIAL
925 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
926 VolumeId p1rwid, p2rwid;
928 (p1->u.vnode.vnodeNumber ==
929 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
931 (p2->u.vnode.vnodeNumber ==
932 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
937 if (p1->u.vnode.vnodeNumber == INODESPECIAL
938 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
939 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
940 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
941 if (p1->u.vnode.volumeId == p1rwid)
943 if (p2->u.vnode.volumeId == p2rwid)
945 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
947 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
948 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
949 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
951 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
953 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
955 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
957 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
959 /* The following tests are reversed, so that the most desirable
960 * of several similar inodes comes first */
961 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
963 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
964 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
968 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
969 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
974 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
976 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
977 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
981 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
982 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
987 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
989 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
990 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
994 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
995 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1000 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1001 #ifdef AFS_3DISPARES
1002 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1003 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1006 #ifdef AFS_SGI_EXMAG
1007 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1008 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1017 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1018 register struct InodeSummary *summary)
1020 VolumeId volume = ip->u.vnode.volumeId;
1021 VolumeId rwvolume = volume;
1022 register int n, nSpecial;
1023 register Unique maxunique;
1026 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1028 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1030 rwvolume = ip->u.special.parentId;
1031 /* This isn't quite right, as there could (in error) be different
1032 * parent inodes in different special vnodes */
1034 if (maxunique < ip->u.vnode.vnodeUniquifier)
1035 maxunique = ip->u.vnode.vnodeUniquifier;
1039 summary->volumeId = volume;
1040 summary->RWvolumeId = rwvolume;
1041 summary->nInodes = n;
1042 summary->nSpecialInodes = nSpecial;
1043 summary->maxUniquifier = maxunique;
1047 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1049 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1050 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1051 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1056 * Collect list of inodes in file named by path. If a truly fatal error,
1057 * unlink the file and abort. For lessor errors, return -1. The file will
1058 * be unlinked by the caller.
1061 GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
1063 struct afs_stat status;
1066 struct ViceInodeInfo *ip;
1067 struct InodeSummary summary;
1068 char summaryFileName[50];
1071 char *dev = fileSysPath;
1072 char *wpath = fileSysPath;
1074 char *dev = fileSysDeviceName;
1075 char *wpath = filesysfulldev;
1077 char *part = fileSysPath;
1080 /* This file used to come from vfsck; cobble it up ourselves now... */
1082 ListViceInodes(dev, fileSysPath, inodeFile,
1083 singleVolumeNumber ? OnlyOneVolume : 0,
1084 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1086 Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1089 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1091 if (forceSal && !ForceSalvage) {
1092 Log("***Forced salvage of all volumes on this partition***\n");
1095 fseek(inodeFile, 0L, SEEK_SET);
1096 inodeFd = fileno(inodeFile);
1097 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1098 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1100 tdir = (tmpdir ? tmpdir : part);
1102 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1103 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1105 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1106 "%s/salvage.temp.%d", tdir, getpid());
1108 summaryFile = afs_fopen(summaryFileName, "a+");
1109 if (summaryFile == NULL) {
1110 Abort("Unable to create inode summary file\n");
1114 /* Using nt_unlink here since we're really using the delete on close
1115 * semantics of unlink. In most places in the salvager, we really do
1116 * mean to unlink the file at that point. Those places have been
1117 * modified to actually do that so that the NT crt can be used there.
1119 code = nt_unlink(summaryFileName);
1121 code = unlink(summaryFileName);
1124 Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1127 if (!canfork || debug || Fork() == 0) {
1129 unsigned long st_size=(unsigned long) status.st_size;
1130 nInodes = st_size / sizeof(struct ViceInodeInfo);
1132 fclose(summaryFile);
1133 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1134 RemoveTheForce(fileSysPath);
1136 struct VolumeSummary *vsp;
1139 GetVolumeSummary(singleVolumeNumber);
1141 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1143 DeleteExtraVolumeHeaderFile(vsp);
1146 Log("%s vice inodes on %s; not salvaged\n",
1147 singleVolumeNumber ? "No applicable" : "No", dev);
1150 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1152 fclose(summaryFile);
1154 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1157 if (read(inodeFd, ip, st_size) != st_size) {
1158 fclose(summaryFile);
1159 Abort("Unable to read inode table; %s not salvaged\n", dev);
1161 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1162 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1163 || write(inodeFd, ip, st_size) != st_size) {
1164 fclose(summaryFile);
1165 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1169 CountVolumeInodes(ip, nInodes, &summary);
1170 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1171 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1172 fclose(summaryFile);
1175 summary.index += (summary.nInodes);
1176 nInodes -= summary.nInodes;
1177 ip += summary.nInodes;
1179 /* Following fflush is not fclose, because if it was debug mode would not work */
1180 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1181 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1182 fclose(summaryFile);
1185 if (canfork && !debug) {
1190 if (Wait("Inode summary") == -1) {
1191 fclose(summaryFile);
1192 Exit(1); /* salvage of this partition aborted */
1195 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1196 if (status.st_size != 0) {
1198 unsigned long st_status=(unsigned long)status.st_size;
1199 inodeSummary = (struct InodeSummary *)malloc(st_status);
1200 assert(inodeSummary != NULL);
1201 /* For GNU we need to do lseek to get the file pointer moved. */
1202 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1203 ret = read(fileno(summaryFile), inodeSummary, st_status);
1204 assert(ret == st_status);
1206 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1207 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1208 fclose(summaryFile);
1212 /* Comparison routine for volume sort.
1213 This is setup so that a read-write volume comes immediately before
1214 any read-only clones of that volume */
1216 CompareVolumes(const void *_p1, const void *_p2)
1218 register const struct VolumeSummary *p1 = _p1;
1219 register const struct VolumeSummary *p2 = _p2;
1220 if (p1->header.parent != p2->header.parent)
1221 return p1->header.parent < p2->header.parent ? -1 : 1;
1222 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1224 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1226 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1230 * Gleans volumeSummary information by asking the fileserver
1232 * @param[in] singleVolumeNumber the volume we're salvaging. 0 if we're
1233 * salvaging a whole partition
1235 * @return whether we obtained the volume summary information or not
1236 * @retval 0 success; we obtained the volume summary information
1237 * @retval nonzero we did not get the volume summary information; either the
1238 * fileserver responded with an error, or we are not supposed to
1239 * ask the fileserver for the information (e.g. we are salvaging
1240 * the entire partition or we are not the salvageserver)
1242 * @note for non-DAFS, always returns 1
1245 AskVolumeSummary(VolumeId singleVolumeNumber)
1248 #ifdef FSSYNC_BUILD_CLIENT
1249 if (programType == salvageServer) {
1250 if (singleVolumeNumber) {
1251 FSSYNC_VGQry_response_t q_res;
1253 struct VolumeSummary *vsp;
1255 struct VolumeDiskHeader diskHdr;
1257 memset(&res, 0, sizeof(res));
1259 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1262 * We must wait for the partition to finish scanning before
1263 * can continue, since we will not know if we got the entire
1264 * VG membership unless the partition is fully scanned.
1265 * We could, in theory, just scan the partition ourselves if
1266 * the VG cache is not ready, but we would be doing the exact
1267 * same scan the fileserver is doing; it will almost always
1268 * be faster to wait for the fileserver. The only exceptions
1269 * are if the partition does not take very long to scan, and
1270 * in that case it's fast either way, so who cares?
1272 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1273 Log("waiting for fileserver to finish scanning partition %s...\n",
1274 fileSysPartition->name);
1276 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1277 /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1278 * just so small partitions don't need to wait over 10
1279 * seconds every time, and large partitions are generally
1280 * polled only once every ten seconds. */
1281 sleep((i > 10) ? (i = 10) : i);
1283 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1287 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1288 /* This can happen if there's no header for the volume
1289 * we're salvaging, or no headers exist for the VG (if
1290 * we're salvaging an RW). Act as if we got a response
1291 * with no VG members. The headers may be created during
1292 * salvaging, if there are inodes in this VG. */
1294 memset(&q_res, 0, sizeof(q_res));
1295 q_res.rw = singleVolumeNumber;
1299 Log("fileserver refused VGCQuery request for volume %lu on "
1300 "partition %s, code %ld reason %ld\n",
1301 afs_printable_uint32_lu(singleVolumeNumber),
1302 fileSysPartition->name,
1303 afs_printable_int32_ld(code),
1304 afs_printable_int32_ld(res.hdr.reason));
1308 if (q_res.rw != singleVolumeNumber) {
1309 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1310 afs_printable_uint32_lu(singleVolumeNumber),
1311 afs_printable_uint32_lu(q_res.rw));
1312 #ifdef SALVSYNC_BUILD_CLIENT
1313 if (SALVSYNC_LinkVolume(q_res.rw,
1315 fileSysPartition->name,
1317 Log("schedule request failed\n");
1319 #endif /* SALVSYNC_BUILD_CLIENT */
1320 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1323 volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
1324 assert(volumeSummaryp != NULL);
1327 vsp = volumeSummaryp;
1329 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1330 char name[VMAXPATHLEN];
1332 if (!q_res.children[i]) {
1336 if (q_res.children[i] != singleVolumeNumber) {
1337 AskOffline(q_res.children[i], fileSysPartition->name);
1339 code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
1341 Log("Cannot read header for %lu; trying to salvage group anyway\n",
1342 afs_printable_uint32_lu(q_res.children[i]));
1347 DiskToVolumeHeader(&vsp->header, &diskHdr);
1348 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1349 vsp->fileName = ToString(name);
1354 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1359 Log("Cannot get volume summary from fileserver; falling back to scanning "
1360 "entire partition\n");
1363 #endif /* FSSYNC_BUILD_CLIENT */
1368 * count how many volume headers are found by VWalkVolumeHeaders.
1370 * @param[in] dp the disk partition (unused)
1371 * @param[in] name full path to the .vol header (unused)
1372 * @param[in] hdr the header data (unused)
1373 * @param[in] last whether this is the last try or not (unused)
1374 * @param[in] rock actually an afs_int32*; the running count of how many
1375 * volumes we have found
1380 CountHeader(struct DiskPartition64 *dp, const char *name,
1381 struct VolumeDiskHeader *hdr, int last, void *rock)
1383 afs_int32 *nvols = (afs_int32 *)rock;
1389 * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1392 struct SalvageScanParams {
1393 VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1394 * vol id of the VG we're salvaging */
1395 struct VolumeSummary *vsp; /**< ptr to the current volume summary object
1396 * we're filling in */
1397 afs_int32 nVolumes; /**< # of vols we've encountered */
1398 afs_int32 totalVolumes; /**< max # of vols we should encounter (the
1399 * # of vols we've alloc'd memory for) */
1403 * records volume summary info found from VWalkVolumeHeaders.
1405 * Found volumes are also taken offline if they are in the specific volume
1406 * group we are looking for.
1408 * @param[in] dp the disk partition
1409 * @param[in] name full path to the .vol header
1410 * @param[in] hdr the header data
1411 * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1412 * @param[in] rock actually a struct SalvageScanParams*, containing the
1413 * information needed to record the volume summary data
1415 * @return operation status
1417 * @retval 1 volume header is mis-named and should be deleted
1420 RecordHeader(struct DiskPartition64 *dp, const char *name,
1421 struct VolumeDiskHeader *hdr, int last, void *rock)
1423 char nameShouldBe[64];
1424 struct SalvageScanParams *params;
1425 struct VolumeSummary summary;
1426 VolumeId singleVolumeNumber;
1428 params = (struct SalvageScanParams *)rock;
1430 singleVolumeNumber = params->singleVolumeNumber;
1432 DiskToVolumeHeader(&summary.header, hdr);
1434 if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1435 && summary.header.parent != singleVolumeNumber) {
1437 if (programType == salvageServer) {
1438 #ifdef SALVSYNC_BUILD_CLIENT
1439 Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1440 summary.header.id, summary.header.parent);
1441 if (SALVSYNC_LinkVolume(summary.header.parent,
1445 Log("schedule request failed\n");
1448 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1451 Log("%u is a read-only volume; not salvaged\n",
1452 singleVolumeNumber);
1457 if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1458 || summary.header.parent == singleVolumeNumber) {
1460 /* check if the header file is incorrectly named */
1462 const char *base = strrchr(name, '/');
1469 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1470 VFORMAT, afs_printable_uint32_lu(summary.header.id));
1473 if (strcmp(nameShouldBe, base)) {
1474 /* .vol file has wrong name; retry/delete */
1478 if (!badname || last) {
1479 /* only offline the volume if the header is good, or if this is
1480 * the last try looking at it; avoid AskOffline'ing the same vol
1483 if (singleVolumeNumber
1484 && summary.header.id != singleVolumeNumber) {
1485 /* don't offline singleVolumeNumber; we already did that
1488 AskOffline(summary.header.id, fileSysPartition->name);
1492 if (last && !Showmode) {
1493 Log("Volume header file %s is incorrectly named (should be %s "
1494 "not %s); %sdeleted (it will be recreated later, if "
1495 "necessary)\n", name, nameShouldBe, base,
1496 (Testing ? "it would have been " : ""));
1501 summary.fileName = ToString(base);
1504 if (params->nVolumes > params->totalVolumes) {
1505 /* We found more volumes than we found on the first partition walk;
1506 * apparently something created a volume while we were
1507 * partition-salvaging, or we found more than 20 vols when salvaging a
1508 * particular volume. Abort if we detect this, since other programs
1509 * supposed to not touch the partition while it is partition-salvaging,
1510 * and we shouldn't find more than 20 vols in a VG.
1512 Abort("Found %ld vol headers, but should have found at most %ld! "
1513 "Make sure the volserver/fileserver are not running at the "
1514 "same time as a partition salvage\n",
1515 afs_printable_int32_ld(params->nVolumes),
1516 afs_printable_int32_ld(params->totalVolumes));
1519 memcpy(params->vsp, &summary, sizeof(summary));
1527 * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1529 * If the header could not be read in at all, the header is always unlinked.
1530 * If instead RecordHeader said the header was bad (that is, the header file
1531 * is mis-named), we only unlink if we are doing a partition salvage, as
1532 * opposed to salvaging a specific volume group.
1534 * @param[in] dp the disk partition
1535 * @param[in] name full path to the .vol header
1536 * @param[in] hdr header data, or NULL if the header could not be read
1537 * @param[in] rock actually a struct SalvageScanParams*, with some information
1541 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1542 struct VolumeDiskHeader *hdr, void *rock)
1544 struct SalvageScanParams *params;
1547 params = (struct SalvageScanParams *)rock;
1550 /* no header; header is too bogus to read in at all */
1552 Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1558 } else if (!params->singleVolumeNumber) {
1559 /* We were able to read in a header, but RecordHeader said something
1560 * was wrong with it. We only unlink those if we are doing a partition
1567 if (dounlink && unlink(name)) {
1568 Log("Error %d while trying to unlink %s\n", errno, name);
1573 GetVolumeSummary(VolumeId singleVolumeNumber)
1575 afs_int32 nvols = 0;
1576 struct SalvageScanParams params;
1579 if (AskVolumeSummary(singleVolumeNumber) == 0) {
1580 /* we successfully got the vol information from the fileserver; no
1581 * need to scan the partition */
1585 if (!singleVolumeNumber) {
1586 /* Count how many volumes we have in /vicepX */
1587 code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
1590 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1595 nvols = VOL_VG_MAX_VOLS;
1598 volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
1599 assert(volumeSummaryp != NULL);
1601 params.singleVolumeNumber = singleVolumeNumber;
1602 params.vsp = volumeSummaryp;
1603 params.nVolumes = 0;
1604 params.totalVolumes = nvols;
1606 /* walk the partition directory of volume headers and record the info
1607 * about them; unlinking invalid headers */
1608 code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
1609 UnlinkHeader, ¶ms);
1611 Abort("Failed to get volume header summary\n");
1613 nVolumes = params.nVolumes;
1615 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1619 /* Find the link table. This should be associated with the RW volume or, if
1620 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1623 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1624 struct ViceInodeInfo *allInodes)
1627 struct ViceInodeInfo *ip;
1629 for (i = 0; i < nVols; i++) {
1630 ip = allInodes + isp[i].index;
1631 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1632 if (ip[j].u.special.type == VI_LINKTABLE)
1633 return ip[j].inodeNumber;
1640 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1642 struct versionStamp version;
1645 if (!VALID_INO(ino))
1647 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1648 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1649 if (!VALID_INO(ino))
1651 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1652 isp->RWvolumeId, errno);
1653 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1654 fdP = IH_OPEN(VGLinkH);
1656 Abort("Can't open link table for volume %u (error = %d)\n",
1657 isp->RWvolumeId, errno);
1659 if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1660 Abort("Can't truncate link table for volume %u (error = %d)\n",
1661 isp->RWvolumeId, errno);
1663 version.magic = LINKTABLEMAGIC;
1664 version.version = LINKTABLEVERSION;
1666 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1668 Abort("Can't truncate link table for volume %u (error = %d)\n",
1669 isp->RWvolumeId, errno);
1671 FDH_REALLYCLOSE(fdP);
1673 /* If the volume summary exits (i.e., the V*.vol header file exists),
1674 * then set this inode there as well.
1676 if (isp->volSummary)
1677 isp->volSummary->header.linkTable = ino;
1686 SVGParms_t *parms = (SVGParms_t *) arg;
1687 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1692 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1695 pthread_attr_t tattr;
1699 /* Initialize per volume global variables, even if later code does so */
1703 memset(&VolInfo, 0, sizeof(VolInfo));
1705 parms.svgp_inodeSummaryp = isp;
1706 parms.svgp_count = nVols;
1707 code = pthread_attr_init(&tattr);
1709 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1713 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1715 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1718 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1720 Log("Failed to create thread to salvage volume group %u\n",
1724 (void)pthread_join(tid, NULL);
1726 #endif /* AFS_NT40_ENV */
1729 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1731 struct ViceInodeInfo *inodes, *allInodes, *ip;
1732 int i, totalInodes, size, salvageTo;
1736 int dec_VGLinkH = 0;
1738 FdHandle_t *fdP = NULL;
1741 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1742 && isp->nSpecialInodes > 0);
1743 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1744 if (!ForceSalvage && QuickCheck(isp, nVols))
1747 if (ShowMounts && !haveRWvolume)
1749 if (canfork && !debug && Fork() != 0) {
1750 (void)Wait("Salvage volume group");
1753 for (i = 0, totalInodes = 0; i < nVols; i++)
1754 totalInodes += isp[i].nInodes;
1755 size = totalInodes * sizeof(struct ViceInodeInfo);
1756 inodes = (struct ViceInodeInfo *)malloc(size);
1757 allInodes = inodes - isp->index; /* this would the base of all the inodes
1758 * for the partition, if all the inodes
1759 * had been read into memory */
1761 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1763 assert(read(inodeFd, inodes, size) == size);
1765 /* Don't try to salvage a read write volume if there isn't one on this
1767 salvageTo = haveRWvolume ? 0 : 1;
1769 #ifdef AFS_NAMEI_ENV
1770 ino = FindLinkHandle(isp, nVols, allInodes);
1771 if (VALID_INO(ino)) {
1772 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1773 fdP = IH_OPEN(VGLinkH);
1775 if (!VALID_INO(ino) || fdP == NULL) {
1776 Log("%s link table for volume %u.\n",
1777 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1779 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1782 struct ViceInodeInfo *ip;
1783 CreateLinkTable(isp, ino);
1784 fdP = IH_OPEN(VGLinkH);
1785 /* Sync fake 1 link counts to the link table, now that it exists */
1787 for (i = 0; i < nVols; i++) {
1788 ip = allInodes + isp[i].index;
1789 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1791 nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1793 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1801 FDH_REALLYCLOSE(fdP);
1803 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1806 /* Salvage in reverse order--read/write volume last; this way any
1807 * Inodes not referenced by the time we salvage the read/write volume
1808 * can be picked up by the read/write volume */
1809 /* ACTUALLY, that's not done right now--the inodes just vanish */
1810 for (i = nVols - 1; i >= salvageTo; i--) {
1812 struct InodeSummary *lisp = &isp[i];
1813 #ifdef AFS_NAMEI_ENV
1814 /* If only the RO is present on this partition, the link table
1815 * shows up as a RW volume special file. Need to make sure the
1816 * salvager doesn't try to salvage the non-existent RW.
1818 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1819 /* If this only special inode is the link table, continue */
1820 if (inodes->u.special.type == VI_LINKTABLE) {
1827 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1828 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1829 /* Check inodes twice. The second time do things seriously. This
1830 * way the whole RO volume can be deleted, below, if anything goes wrong */
1831 for (check = 1; check >= 0; check--) {
1833 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1835 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1836 if (rw && deleteMe) {
1837 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1838 * volume won't be called */
1844 if (rw && check == 1)
1846 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1847 MaybeZapVolume(lisp, "Vnode index", 0, check);
1853 /* Fix actual inode counts */
1855 Log("totalInodes %d\n",totalInodes);
1856 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1857 static int TraceBadLinkCounts = 0;
1858 #ifdef AFS_NAMEI_ENV
1859 if (VGLinkH->ih_ino == ip->inodeNumber) {
1860 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1861 VGLinkH_p1 = ip->u.param[0];
1862 continue; /* Deal with this last. */
1865 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1866 TraceBadLinkCounts--; /* Limit reports, per volume */
1867 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(NULL, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
1869 while (ip->linkCount > 0) {
1870 /* below used to assert, not break */
1872 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1873 Log("idec failed. inode %s errno %d\n",
1874 PrintInode(NULL, ip->inodeNumber), errno);
1880 while (ip->linkCount < 0) {
1881 /* these used to be asserts */
1883 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1884 Log("iinc failed. inode %s errno %d\n",
1885 PrintInode(NULL, ip->inodeNumber), errno);
1892 #ifdef AFS_NAMEI_ENV
1893 while (dec_VGLinkH > 0) {
1894 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1895 Log("idec failed on link table, errno = %d\n", errno);
1899 while (dec_VGLinkH < 0) {
1900 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1901 Log("iinc failed on link table, errno = %d\n", errno);
1908 /* Directory consistency checks on the rw volume */
1910 SalvageVolume(isp, VGLinkH);
1911 IH_RELEASE(VGLinkH);
1913 if (canfork && !debug) {
1920 QuickCheck(register struct InodeSummary *isp, int nVols)
1922 /* Check headers BEFORE forking */
1926 for (i = 0; i < nVols; i++) {
1927 struct VolumeSummary *vs = isp[i].volSummary;
1928 VolumeDiskData volHeader;
1930 /* Don't salvage just because phantom rw volume is there... */
1931 /* (If a read-only volume exists, read/write inodes must also exist) */
1932 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1936 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1937 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1938 == sizeof(volHeader)
1939 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1940 && volHeader.dontSalvage == DONT_SALVAGE
1941 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1942 if (volHeader.inUse != 0) {
1943 volHeader.inUse = 0;
1944 volHeader.inService = 1;
1946 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1947 != sizeof(volHeader)) {
1963 /* SalvageVolumeHeaderFile
1965 * Salvage the top level V*.vol header file. Make sure the special files
1966 * exist and that there are no duplicates.
1968 * Calls SalvageHeader for each possible type of volume special file.
1972 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1973 register struct ViceInodeInfo *inodes, int RW,
1974 int check, int *deleteMe)
1977 register struct ViceInodeInfo *ip;
1978 int allinodesobsolete = 1;
1979 struct VolumeDiskHeader diskHeader;
1980 afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
1983 /* keeps track of special inodes that are probably 'good'; they are
1984 * referenced in the vol header, and are included in the given inodes
1989 } goodspecial[MAXINODETYPE];
1994 memset(goodspecial, 0, sizeof(goodspecial));
1996 skip = malloc(isp->nSpecialInodes * sizeof(*skip));
1998 memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
2000 Log("cannot allocate memory for inode skip array when salvaging "
2001 "volume %lu; not performing duplicate special inode recovery\n",
2002 afs_printable_uint32_lu(isp->volumeId));
2003 /* still try to perform the salvage; the skip array only does anything
2004 * if we detect duplicate special inodes */
2008 * First, look at the special inodes and see if any are referenced by
2009 * the existing volume header. If we find duplicate special inodes, we
2010 * can use this information to use the referenced inode (it's more
2011 * likely to be the 'good' one), and throw away the duplicates.
2013 if (isp->volSummary && skip) {
2014 /* use tempHeader, so we can use the stuff[] array to easily index
2015 * into the isp->volSummary special inodes */
2016 memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2018 for (i = 0; i < isp->nSpecialInodes; i++) {
2019 ip = &inodes[isp->index + i];
2020 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2021 /* will get taken care of in a later loop */
2024 if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2025 goodspecial[ip->u.special.type-1].valid = 1;
2026 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2031 memset(&tempHeader, 0, sizeof(tempHeader));
2032 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2033 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2034 tempHeader.id = isp->volumeId;
2035 tempHeader.parent = isp->RWvolumeId;
2037 /* Check for duplicates (inodes are sorted by type field) */
2038 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2039 ip = &inodes[isp->index + i];
2040 if (ip->u.special.type == (ip + 1)->u.special.type) {
2041 afs_ino_str_t stmp1, stmp2;
2043 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2044 /* Will be caught in the loop below */
2048 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2049 ip->u.special.type, isp->volumeId,
2050 PrintInode(stmp1, ip->inodeNumber),
2051 PrintInode(stmp2, (ip+1)->inodeNumber));
2053 if (skip && goodspecial[ip->u.special.type-1].valid) {
2054 Inode gi = goodspecial[ip->u.special.type-1].inode;
2057 Log("using special inode referenced by vol header (%s)\n",
2058 PrintInode(stmp1, gi));
2061 /* the volume header references some special inode of
2062 * this type in the inodes array; are we it? */
2063 if (ip->inodeNumber != gi) {
2065 } else if ((ip+1)->inodeNumber != gi) {
2066 /* in case this is the last iteration; we need to
2067 * make sure we check ip+1, too */
2072 Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2080 for (i = 0; i < isp->nSpecialInodes; i++) {
2081 ip = &inodes[isp->index + i];
2082 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2084 Log("Rubbish header inode %s of type %d\n",
2085 PrintInode(NULL, ip->inodeNumber),
2086 ip->u.special.type);
2092 Log("Rubbish header inode %s of type %d; deleted\n",
2093 PrintInode(NULL, ip->inodeNumber),
2094 ip->u.special.type);
2095 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2096 if (skip && skip[i]) {
2097 if (orphans == ORPH_REMOVE) {
2098 Log("Removing orphan special inode %s of type %d\n",
2099 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2102 Log("Ignoring orphan special inode %s of type %d\n",
2103 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2104 /* fall through to the ip->linkCount--; line below */
2107 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2108 allinodesobsolete = 0;
2110 if (!check && ip->u.special.type != VI_LINKTABLE)
2111 ip->linkCount--; /* Keep the inode around */
2119 if (allinodesobsolete) {
2126 VGLinkH_cnt++; /* one for every header. */
2128 if (!RW && !check && isp->volSummary) {
2129 ClearROInUseBit(isp->volSummary);
2133 for (i = 0; i < MAXINODETYPE; i++) {
2134 if (stuff[i].inodeType == VI_LINKTABLE) {
2135 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2136 * And we may have recreated the link table earlier, so set the
2137 * RW header as well.
2139 if (VALID_INO(VGLinkH->ih_ino)) {
2140 *stuff[i].inode = VGLinkH->ih_ino;
2144 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2148 if (isp->volSummary == NULL) {
2150 char headerName[64];
2151 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2152 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2154 Log("No header file for volume %u\n", isp->volumeId);
2158 Log("No header file for volume %u; %screating %s\n",
2159 isp->volumeId, (Testing ? "it would have been " : ""),
2161 isp->volSummary = (struct VolumeSummary *)
2162 malloc(sizeof(struct VolumeSummary));
2163 isp->volSummary->fileName = ToString(headerName);
2165 writefunc = VCreateVolumeDiskHeader;
2168 char headerName[64];
2169 /* hack: these two fields are obsolete... */
2170 isp->volSummary->header.volumeAcl = 0;
2171 isp->volSummary->header.volumeMountTable = 0;
2174 (&isp->volSummary->header, &tempHeader,
2175 sizeof(struct VolumeHeader))) {
2176 /* We often remove the name before calling us, so we make a fake one up */
2177 if (isp->volSummary->fileName) {
2178 strcpy(headerName, isp->volSummary->fileName);
2180 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2181 isp->volSummary->fileName = ToString(headerName);
2183 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2185 Log("Header file %s is damaged or no longer valid%s\n", path,
2186 (check ? "" : "; repairing"));
2190 writefunc = VWriteVolumeDiskHeader;
2194 memcpy(&isp->volSummary->header, &tempHeader,
2195 sizeof(struct VolumeHeader));
2198 Log("It would have written a new header file for volume %u\n",
2202 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2203 code = (*writefunc)(&diskHeader, fileSysPartition);
2205 Log("Error %ld writing volume header file for volume %lu\n",
2206 afs_printable_int32_ld(code),
2207 afs_printable_uint32_lu(diskHeader.id));
2212 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2213 isp->volSummary->header.volumeInfo);
2218 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2222 VolumeDiskData volumeInfo;
2223 struct versionStamp fileHeader;
2232 #ifndef AFS_NAMEI_ENV
2233 if (sp->inodeType == VI_LINKTABLE)
2236 if (*(sp->inode) == 0) {
2238 Log("Missing inode in volume header (%s)\n", sp->description);
2242 Log("Missing inode in volume header (%s); %s\n", sp->description,
2243 (Testing ? "it would have recreated it" : "recreating"));
2246 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2247 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2248 if (!VALID_INO(*(sp->inode)))
2250 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2251 sp->description, errno);
2256 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2257 fdP = IH_OPEN(specH);
2258 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2259 /* bail out early and destroy the volume */
2261 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2268 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2269 sp->description, errno);
2272 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2273 || header.fileHeader.magic != sp->stamp.magic)) {
2275 Log("Part of the header (%s) is corrupted\n", sp->description);
2276 FDH_REALLYCLOSE(fdP);
2280 Log("Part of the header (%s) is corrupted; recreating\n",
2284 if (sp->inodeType == VI_VOLINFO
2285 && header.volumeInfo.destroyMe == DESTROY_ME) {
2288 FDH_REALLYCLOSE(fdP);
2292 if (recreate && !Testing) {
2295 ("Internal error: recreating volume header (%s) in check mode\n",
2297 code = FDH_TRUNC(fdP, 0);
2299 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2300 sp->description, errno);
2302 /* The following code should be moved into vutil.c */
2303 if (sp->inodeType == VI_VOLINFO) {
2305 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2306 header.volumeInfo.stamp = sp->stamp;
2307 header.volumeInfo.id = isp->volumeId;
2308 header.volumeInfo.parentId = isp->RWvolumeId;
2309 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2310 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2311 isp->volumeId, isp->volumeId);
2312 header.volumeInfo.inService = 0;
2313 header.volumeInfo.blessed = 0;
2314 /* The + 1000 is a hack in case there are any files out in venus caches */
2315 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2316 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2317 header.volumeInfo.needsCallback = 0;
2318 gettimeofday(&tp, 0);
2319 header.volumeInfo.creationDate = tp.tv_sec;
2320 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2322 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2323 sp->description, errno);
2326 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2327 sizeof(header.volumeInfo));
2328 if (code != sizeof(header.volumeInfo)) {
2331 ("Unable to write volume header file (%s) (errno = %d)\n",
2332 sp->description, errno);
2333 Abort("Unable to write entire volume header file (%s)\n",
2337 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2339 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2340 sp->description, errno);
2342 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2343 if (code != sizeof(sp->stamp)) {
2346 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2347 sp->description, errno);
2349 ("Unable to write entire version stamp in volume header file (%s)\n",
2354 FDH_REALLYCLOSE(fdP);
2356 if (sp->inodeType == VI_VOLINFO) {
2357 VolInfo = header.volumeInfo;
2360 if (VolInfo.updateDate) {
2361 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2363 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2364 (Testing ? "it would have been " : ""), update);
2366 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2368 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2369 VolInfo.id, update);
2379 SalvageVnodes(register struct InodeSummary *rwIsp,
2380 register struct InodeSummary *thisIsp,
2381 register struct ViceInodeInfo *inodes, int check)
2383 int ilarge, ismall, ioffset, RW, nInodes;
2384 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2387 RW = (rwIsp == thisIsp);
2388 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2390 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2391 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2392 if (check && ismall == -1)
2395 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2396 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2397 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2401 SalvageIndex(Inode ino, VnodeClass class, int RW,
2402 register struct ViceInodeInfo *ip, int nInodes,
2403 struct VolumeSummary *volSummary, int check)
2405 VolumeId volumeNumber;
2406 char buf[SIZEOF_LARGEDISKVNODE];
2407 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2409 StreamHandle_t *file;
2410 struct VnodeClassInfo *vcp;
2412 afs_fsize_t vnodeLength;
2413 int vnodeIndex, nVnodes;
2414 afs_ino_str_t stmp1, stmp2;
2418 volumeNumber = volSummary->header.id;
2419 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2420 fdP = IH_OPEN(handle);
2421 assert(fdP != NULL);
2422 file = FDH_FDOPEN(fdP, "r+");
2423 assert(file != NULL);
2424 vcp = &VnodeClassInfo[class];
2425 size = OS_SIZE(fdP->fd_fd);
2427 nVnodes = (size / vcp->diskSize) - 1;
2429 assert((nVnodes + 1) * vcp->diskSize == size);
2430 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2434 for (vnodeIndex = 0;
2435 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2436 nVnodes--, vnodeIndex++) {
2437 if (vnode->type != vNull) {
2438 int vnodeChanged = 0;
2439 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2440 /* Log programs that belong to root (potentially suid root);
2441 * don't bother for read-only or backup volumes */
2442 #ifdef notdef /* This is done elsewhere */
2443 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2444 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n", VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author, vnode->owner, vnode->modeBits);
2446 if (VNDISK_GET_INO(vnode) == 0) {
2448 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2449 memset(vnode, 0, vcp->diskSize);
2453 if (vcp->magic != vnode->vnodeMagic) {
2454 /* bad magic #, probably partially created vnode */
2455 Log("Partially allocated vnode %d deleted.\n",
2457 memset(vnode, 0, vcp->diskSize);
2461 /* ****** Should do a bit more salvage here: e.g. make sure
2462 * vnode type matches what it should be given the index */
2463 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2464 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2465 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2466 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2473 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2474 /* The following doesn't work, because the version number
2475 * is not maintained correctly by the file server */
2476 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2477 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2479 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2485 /* For RW volume, look for vnode with matching inode number;
2486 * if no such match, take the first determined by our sort
2488 register struct ViceInodeInfo *lip = ip;
2489 register int lnInodes = nInodes;
2491 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2492 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2501 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2502 /* "Matching" inode */
2506 vu = vnode->uniquifier;
2507 iu = ip->u.vnode.vnodeUniquifier;
2508 vd = vnode->dataVersion;
2509 id = ip->u.vnode.inodeDataVersion;
2511 * Because of the possibility of the uniquifier overflows (> 4M)
2512 * we compare them modulo the low 22-bits; we shouldn't worry
2513 * about mismatching since they shouldn't to many old
2514 * uniquifiers of the same vnode...
2516 if (IUnique(vu) != IUnique(iu)) {
2518 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2521 vnode->uniquifier = iu;
2522 #ifdef AFS_3DISPARES
2523 vnode->dataVersion = (id >= vd ?
2526 1887437 ? vd : id) :
2529 1887437 ? id : vd));
2531 #if defined(AFS_SGI_EXMAG)
2532 vnode->dataVersion = (id >= vd ?
2535 15099494 ? vd : id) :
2538 15099494 ? id : vd));
2540 vnode->dataVersion = (id > vd ? id : vd);
2541 #endif /* AFS_SGI_EXMAG */
2542 #endif /* AFS_3DISPARES */
2545 /* don't bother checking for vd > id any more, since
2546 * partial file transfers always result in this state,
2547 * and you can't do much else anyway (you've already
2548 * found the best data you can) */
2549 #ifdef AFS_3DISPARES
2550 if (!vnodeIsDirectory(vnodeNumber)
2551 && ((vd < id && (id - vd) < 1887437)
2552 || ((vd > id && (vd - id) > 1887437)))) {
2554 #if defined(AFS_SGI_EXMAG)
2555 if (!vnodeIsDirectory(vnodeNumber)
2556 && ((vd < id && (id - vd) < 15099494)
2557 || ((vd > id && (vd - id) > 15099494)))) {
2559 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2560 #endif /* AFS_SGI_EXMAG */
2563 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2564 vnode->dataVersion = id;
2569 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2572 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2574 VNDISK_SET_INO(vnode, ip->inodeNumber);
2579 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2581 VNDISK_SET_INO(vnode, ip->inodeNumber);
2584 VNDISK_GET_LEN(vnodeLength, vnode);
2585 if (ip->byteCount != vnodeLength) {
2588 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2593 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2594 VNDISK_SET_LEN(vnode, ip->byteCount);
2598 ip->linkCount--; /* Keep the inode around */
2601 } else { /* no matching inode */
2602 if (VNDISK_GET_INO(vnode) != 0
2603 || vnode->type == vDirectory) {
2604 /* No matching inode--get rid of the vnode */
2606 if (VNDISK_GET_INO(vnode)) {
2608 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2612 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2617 if (VNDISK_GET_INO(vnode)) {
2619 time_t serverModifyTime = vnode->serverModifyTime;
2620 Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime(&serverModifyTime));
2624 time_t serverModifyTime = vnode->serverModifyTime;
2625 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2628 memset(vnode, 0, vcp->diskSize);
2631 /* Should not reach here becuase we checked for
2632 * (inodeNumber == 0) above. And where we zero the vnode,
2633 * we also goto vnodeDone.
2637 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2641 } /* VNDISK_GET_INO(vnode) != 0 */
2643 assert(!(vnodeChanged && check));
2644 if (vnodeChanged && !Testing) {
2646 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2647 (char *)vnode, vcp->diskSize)
2649 VolumeChanged = 1; /* For break call back */
2660 struct VnodeEssence *
2661 CheckVnodeNumber(VnodeId vnodeNumber)
2664 struct VnodeInfo *vip;
2667 class = vnodeIdToClass(vnodeNumber);
2668 vip = &vnodeInfo[class];
2669 offset = vnodeIdToBitNumber(vnodeNumber);
2670 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2674 CopyOnWrite(register struct DirSummary *dir)
2676 /* Copy the directory unconditionally if we are going to change it:
2677 * not just if was cloned.
2679 struct VnodeDiskObject vnode;
2680 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2681 Inode oldinode, newinode;
2684 if (dir->copied || Testing)
2686 DFlush(); /* Well justified paranoia... */
2689 IH_IREAD(vnodeInfo[vLarge].handle,
2690 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2692 assert(code == sizeof(vnode));
2693 oldinode = VNDISK_GET_INO(&vnode);
2694 /* Increment the version number by a whole lot to avoid problems with
2695 * clients that were promised new version numbers--but the file server
2696 * crashed before the versions were written to disk.
2699 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2700 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2702 assert(VALID_INO(newinode));
2703 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2705 VNDISK_SET_INO(&vnode, newinode);
2707 IH_IWRITE(vnodeInfo[vLarge].handle,
2708 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2710 assert(code == sizeof(vnode));
2712 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2713 fileSysDevice, newinode);
2714 /* Don't delete the original inode right away, because the directory is
2715 * still being scanned.
2721 * This function should either successfully create a new dir, or give up
2722 * and leave things the way they were. In particular, if it fails to write
2723 * the new dir properly, it should return w/o changing the reference to the
2727 CopyAndSalvage(register struct DirSummary *dir)
2729 struct VnodeDiskObject vnode;
2730 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2731 Inode oldinode, newinode;
2736 afs_int32 parentUnique = 1;
2737 struct VnodeEssence *vnodeEssence;
2742 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2744 IH_IREAD(vnodeInfo[vLarge].handle,
2745 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2747 assert(lcode == sizeof(vnode));
2748 oldinode = VNDISK_GET_INO(&vnode);
2749 /* Increment the version number by a whole lot to avoid problems with
2750 * clients that were promised new version numbers--but the file server
2751 * crashed before the versions were written to disk.
2754 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2755 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2757 assert(VALID_INO(newinode));
2758 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2760 /* Assign . and .. vnode numbers from dir and vnode.parent.
2761 * The uniquifier for . is in the vnode.
2762 * The uniquifier for .. might be set to a bogus value of 1 and
2763 * the salvager will later clean it up.
2765 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2766 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2769 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2771 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2776 /* didn't really build the new directory properly, let's just give up. */
2777 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2778 Log("Directory salvage returned code %d, continuing.\n", code);
2780 Log("also failed to decrement link count on new inode");
2784 Log("Checking the results of the directory salvage...\n");
2785 if (!DirOK(&newdir)) {
2786 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2787 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2792 VNDISK_SET_INO(&vnode, newinode);
2793 length = Length(&newdir);
2794 VNDISK_SET_LEN(&vnode, length);
2796 IH_IWRITE(vnodeInfo[vLarge].handle,
2797 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2799 assert(lcode == sizeof(vnode));
2802 nt_sync(fileSysDevice);
2804 sync(); /* this is slow, but hopefully rarely called. We don't have
2805 * an open FD on the file itself to fsync.
2809 vnodeInfo[vLarge].handle->ih_synced = 1;
2811 /* make sure old directory file is really closed */
2812 fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2813 FDH_REALLYCLOSE(fdP);
2815 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2817 dir->dirHandle = newdir;
2821 JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
2824 struct DirSummary *dir = (struct DirSummary *)dirVal;
2825 struct VnodeEssence *vnodeEssence;
2826 afs_int32 dirOrphaned, todelete;
2828 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2830 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2831 if (vnodeEssence == NULL) {
2833 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2837 assert(Delete(&dir->dirHandle, name) == 0);
2842 #ifndef AFS_NAMEI_ENV
2843 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2844 * mount inode for the partition. If this inode were deleted, it would crash
2847 if (vnodeEssence->InodeNumber == 0) {
2848 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
2851 assert(Delete(&dir->dirHandle, name) == 0);
2858 if (!(vnodeNumber & 1) && !Showmode
2859 && !(vnodeEssence->count || vnodeEssence->unique
2860 || vnodeEssence->modeBits)) {
2861 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2862 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2863 vnodeNumber, unique,
2864 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2869 assert(Delete(&dir->dirHandle, name) == 0);
2875 /* Check if the Uniquifiers match. If not, change the directory entry
2876 * so its unique matches the vnode unique. Delete if the unique is zero
2877 * or if the directory is orphaned.
2879 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2880 if (!vnodeEssence->unique
2881 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2882 /* This is an orphaned directory. Don't delete the . or ..
2883 * entry. Otherwise, it will get created in the next
2884 * salvage and deleted again here. So Just skip it.
2889 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2892 Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
2896 fid.Vnode = vnodeNumber;
2897 fid.Unique = vnodeEssence->unique;
2899 assert(Delete(&dir->dirHandle, name) == 0);
2901 assert(Create(&dir->dirHandle, name, &fid) == 0);
2904 return 0; /* no need to continue */
2907 if (strcmp(name, ".") == 0) {
2908 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2911 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2914 assert(Delete(&dir->dirHandle, ".") == 0);
2915 fid.Vnode = dir->vnodeNumber;
2916 fid.Unique = dir->unique;
2917 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2920 vnodeNumber = fid.Vnode; /* Get the new Essence */
2921 unique = fid.Unique;
2922 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2925 } else if (strcmp(name, "..") == 0) {
2928 struct VnodeEssence *dotdot;
2929 pa.Vnode = dir->parent;
2930 dotdot = CheckVnodeNumber(pa.Vnode);
2931 assert(dotdot != NULL); /* XXX Should not be assert */
2932 pa.Unique = dotdot->unique;
2934 pa.Vnode = dir->vnodeNumber;
2935 pa.Unique = dir->unique;
2937 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2939 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2942 assert(Delete(&dir->dirHandle, "..") == 0);
2943 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2946 vnodeNumber = pa.Vnode; /* Get the new Essence */
2948 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2950 dir->haveDotDot = 1;
2951 } else if (strncmp(name, ".__afs", 6) == 0) {
2953 Log("dir vnode %u: special old unlink-while-referenced file %s %s deleted (vnode %u)\n", dir->vnodeNumber, name, (Testing ? "would have been" : "is"), vnodeNumber);
2957 assert(Delete(&dir->dirHandle, name) == 0);
2959 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2960 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2963 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2964 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2965 if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
2966 && !(vnodeEssence->modeBits & 0111)) {
2972 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2973 vnodeEssence->InodeNumber);
2976 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
2980 size = FDH_SIZE(fdP);
2982 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
2983 FDH_REALLYCLOSE(fdP);
2990 code = FDH_READ(fdP, buf, size);
2993 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
2994 Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
2995 dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
2996 Testing ? "would convert" : "converted");
2997 vnodeEssence->modeBits |= 0111;
2998 vnodeEssence->changed = 1;
2999 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3000 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3001 dir->name ? dir->name : "??", name, buf);
3003 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3004 dir->vname, vnodeNumber, size, code);
3006 FDH_REALLYCLOSE(fdP);
3009 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3010 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3011 if (vnodeIdToClass(vnodeNumber) == vLarge
3012 && vnodeEssence->name == NULL) {
3014 if ((n = (char *)malloc(strlen(name) + 1)))
3016 vnodeEssence->name = n;
3019 /* The directory entry points to the vnode. Check to see if the
3020 * vnode points back to the directory. If not, then let the
3021 * directory claim it (else it might end up orphaned). Vnodes
3022 * already claimed by another directory are deleted from this
3023 * directory: hardlinks to the same vnode are not allowed
3024 * from different directories.
3026 if (vnodeEssence->parent != dir->vnodeNumber) {
3027 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3028 /* Vnode does not point back to this directory.
3029 * Orphaned dirs cannot claim a file (it may belong to
3030 * another non-orphaned dir).
3033 Log("dir vnode %u: %s/%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
3035 vnodeEssence->parent = dir->vnodeNumber;
3036 vnodeEssence->changed = 1;
3038 /* Vnode was claimed by another directory */
3041 Log("dir vnode %u: %s/%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3042 } else if (vnodeNumber == 1) {
3043 Log("dir vnode %d: %s/%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
3045 Log("dir vnode %u: %s/%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3050 assert(Delete(&dir->dirHandle, name) == 0);
3055 /* This directory claims the vnode */
3056 vnodeEssence->claimed = 1;
3058 vnodeEssence->count--;
3063 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3065 register struct VnodeInfo *vip = &vnodeInfo[class];
3066 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3067 char buf[SIZEOF_LARGEDISKVNODE];
3068 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3070 StreamHandle_t *file;
3075 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3076 fdP = IH_OPEN(vip->handle);
3077 assert(fdP != NULL);
3078 file = FDH_FDOPEN(fdP, "r+");
3079 assert(file != NULL);
3080 size = OS_SIZE(fdP->fd_fd);
3082 vip->nVnodes = (size / vcp->diskSize) - 1;
3083 if (vip->nVnodes > 0) {
3084 assert((vip->nVnodes + 1) * vcp->diskSize == size);
3085 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3086 assert((vip->vnodes = (struct VnodeEssence *)
3087 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3088 if (class == vLarge) {
3089 assert((vip->inodes = (Inode *)
3090 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3099 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3100 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3101 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3102 nVnodes--, vnodeIndex++) {
3103 if (vnode->type != vNull) {
3104 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3105 afs_fsize_t vnodeLength;
3106 vip->nAllocatedVnodes++;
3107 vep->count = vnode->linkCount;
3108 VNDISK_GET_LEN(vnodeLength, vnode);
3109 vep->blockCount = nBlocks(vnodeLength);
3110 vip->volumeBlockCount += vep->blockCount;
3111 vep->parent = vnode->parent;
3112 vep->unique = vnode->uniquifier;
3113 if (*maxu < vnode->uniquifier)
3114 *maxu = vnode->uniquifier;
3115 vep->modeBits = vnode->modeBits;
3116 vep->InodeNumber = VNDISK_GET_INO(vnode);
3117 vep->type = vnode->type;
3118 vep->author = vnode->author;
3119 vep->owner = vnode->owner;
3120 vep->group = vnode->group;
3121 if (vnode->type == vDirectory) {
3122 if (class != vLarge) {
3123 VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3124 vip->nAllocatedVnodes--;
3125 memset(vnode, 0, sizeof(vnode));
3126 IH_IWRITE(vnodeInfo[vSmall].handle,
3127 vnodeIndexOffset(vcp, vnodeNumber),
3128 (char *)&vnode, sizeof(vnode));
3131 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3140 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3142 struct VnodeEssence *parentvp;
3148 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3149 && GetDirName(vp->parent, parentvp, path)) {
3151 strcat(path, vp->name);
3157 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3158 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3161 IsVnodeOrphaned(VnodeId vnode)
3163 struct VnodeEssence *vep;
3166 return (1); /* Vnode zero does not exist */
3168 return (0); /* The root dir vnode is always claimed */
3169 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3170 if (!vep || !vep->claimed)
3171 return (1); /* Vnode is not claimed - it is orphaned */
3173 return (IsVnodeOrphaned(vep->parent));
3177 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3178 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3181 static struct DirSummary dir;
3182 static struct DirHandle dirHandle;
3183 struct VnodeEssence *parent;
3184 static char path[MAXPATHLEN];
3187 if (dirVnodeInfo->vnodes[i].salvaged)
3188 return; /* already salvaged */
3191 dirVnodeInfo->vnodes[i].salvaged = 1;
3193 if (dirVnodeInfo->inodes[i] == 0)
3194 return; /* Not allocated to a directory */
3196 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3197 if (dirVnodeInfo->vnodes[i].parent) {
3198 Log("Bad parent, vnode 1; %s...\n",
3199 (Testing ? "skipping" : "salvaging"));
3200 dirVnodeInfo->vnodes[i].parent = 0;
3201 dirVnodeInfo->vnodes[i].changed = 1;
3204 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3205 if (parent && parent->salvaged == 0)
3206 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3207 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3208 rootdir, rootdirfound);
3211 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3212 dir.unique = dirVnodeInfo->vnodes[i].unique;
3215 dir.parent = dirVnodeInfo->vnodes[i].parent;
3216 dir.haveDot = dir.haveDotDot = 0;
3217 dir.ds_linkH = alinkH;
3218 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3219 dirVnodeInfo->inodes[i]);
3221 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3224 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3225 (Testing ? "skipping" : "salvaging"));
3228 CopyAndSalvage(&dir);
3232 dirHandle = dir.dirHandle;
3235 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3236 &dirVnodeInfo->vnodes[i], path);
3239 /* If enumeration failed for random reasons, we will probably delete
3240 * too much stuff, so we guard against this instead.
3242 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3245 /* Delete the old directory if it was copied in order to salvage.
3246 * CopyOnWrite has written the new inode # to the disk, but we still
3247 * have the old one in our local structure here. Thus, we idec the
3251 if (dir.copied && !Testing) {
3252 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3254 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3257 /* Remember rootdir DirSummary _after_ it has been judged */
3258 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3259 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3267 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3269 /* This routine, for now, will only be called for read-write volumes */
3271 int BlocksInVolume = 0, FilesInVolume = 0;
3272 register VnodeClass class;
3273 struct DirSummary rootdir, oldrootdir;
3274 struct VnodeInfo *dirVnodeInfo;
3275 struct VnodeDiskObject vnode;
3276 VolumeDiskData volHeader;
3278 int orphaned, rootdirfound = 0;
3279 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3280 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3281 struct VnodeEssence *vep;
3284 afs_sfsize_t nBytes;
3286 VnodeId LFVnode, ThisVnode;
3287 Unique LFUnique, ThisUnique;
3290 vid = rwIsp->volSummary->header.id;
3291 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3292 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3293 assert(nBytes == sizeof(volHeader));
3294 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3295 assert(volHeader.destroyMe != DESTROY_ME);
3296 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3298 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3300 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3303 dirVnodeInfo = &vnodeInfo[vLarge];
3304 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3305 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3309 nt_sync(fileSysDevice);
3311 sync(); /* This used to be done lower level, for every dir */
3318 /* Parse each vnode looking for orphaned vnodes and
3319 * connect them to the tree as orphaned (if requested).
3321 oldrootdir = rootdir;
3322 for (class = 0; class < nVNODECLASSES; class++) {
3323 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3324 vep = &(vnodeInfo[class].vnodes[v]);
3325 ThisVnode = bitNumberToVnodeNumber(v, class);
3326 ThisUnique = vep->unique;
3328 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3329 continue; /* Ignore unused, claimed, and root vnodes */
3331 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3332 * entry in this vnode had incremented the parent link count (In
3333 * JudgeEntry()). We need to go to the parent and decrement that
3334 * link count. But if the parent's unique is zero, then the parent
3335 * link count was not incremented in JudgeEntry().
3337 if (class == vLarge) { /* directory vnode */
3338 pv = vnodeIdToBitNumber(vep->parent);
3339 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3340 vnodeInfo[vLarge].vnodes[pv].count++;
3344 continue; /* If no rootdir, can't attach orphaned files */
3346 /* Here we attach orphaned files and directories into the
3347 * root directory, LVVnode, making sure link counts stay correct.
3349 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3350 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3351 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3353 /* Update this orphaned vnode's info. Its parent info and
3354 * link count (do for orphaned directories and files).
3356 vep->parent = LFVnode; /* Parent is the root dir */
3357 vep->unique = LFUnique;
3360 vep->count--; /* Inc link count (root dir will pt to it) */
3362 /* If this orphaned vnode is a directory, change '..'.
3363 * The name of the orphaned dir/file is unknown, so we
3364 * build a unique name. No need to CopyOnWrite the directory
3365 * since it is not connected to tree in BK or RO volume and
3366 * won't be visible there.
3368 if (class == vLarge) {
3372 /* Remove and recreate the ".." entry in this orphaned directory */
3373 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3374 vnodeInfo[class].inodes[v]);
3376 pa.Unique = LFUnique;
3377 assert(Delete(&dh, "..") == 0);
3378 assert(Create(&dh, "..", &pa) == 0);
3380 /* The original parent's link count was decremented above.
3381 * Here we increment the new parent's link count.
3383 pv = vnodeIdToBitNumber(LFVnode);
3384 vnodeInfo[vLarge].vnodes[pv].count--;
3388 /* Go to the root dir and add this entry. The link count of the
3389 * root dir was incremented when ".." was created. Try 10 times.
3391 for (j = 0; j < 10; j++) {
3392 pa.Vnode = ThisVnode;
3393 pa.Unique = ThisUnique;
3395 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3397 vLarge) ? "__ORPHANDIR__" :
3398 "__ORPHANFILE__"), ThisVnode,
3401 CopyOnWrite(&rootdir);
3402 code = Create(&rootdir.dirHandle, npath, &pa);
3406 ThisUnique += 50; /* Try creating a different file */
3409 Log("Attaching orphaned %s to volume's root dir as %s\n",
3410 ((class == vLarge) ? "directory" : "file"), npath);
3412 } /* for each vnode in the class */
3413 } /* for each class of vnode */
3415 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3417 if (!oldrootdir.copied && rootdir.copied) {
3419 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3422 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3425 DFlush(); /* Flush the changes */
3426 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3427 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3428 orphans = ORPH_IGNORE;
3431 /* Write out all changed vnodes. Orphaned files and directories
3432 * will get removed here also (if requested).
3434 for (class = 0; class < nVNODECLASSES; class++) {
3435 int nVnodes = vnodeInfo[class].nVnodes;
3436 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3437 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3438 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3439 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3440 for (i = 0; i < nVnodes; i++) {
3441 register struct VnodeEssence *vnp = &vnodes[i];
3442 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3444 /* If the vnode is good but is unclaimed (not listed in
3445 * any directory entries), then it is orphaned.
3448 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3449 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3453 if (vnp->changed || vnp->count) {
3457 IH_IREAD(vnodeInfo[class].handle,
3458 vnodeIndexOffset(vcp, vnodeNumber),
3459 (char *)&vnode, sizeof(vnode));
3460 assert(nBytes == sizeof(vnode));
3462 vnode.parent = vnp->parent;
3463 oldCount = vnode.linkCount;
3464 vnode.linkCount = vnode.linkCount - vnp->count;
3467 orphaned = IsVnodeOrphaned(vnodeNumber);
3469 if (!vnp->todelete) {
3470 /* Orphans should have already been attached (if requested) */
3471 assert(orphans != ORPH_ATTACH);
3472 oblocks += vnp->blockCount;
3475 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3477 BlocksInVolume -= vnp->blockCount;
3479 if (VNDISK_GET_INO(&vnode)) {
3481 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3484 memset(&vnode, 0, sizeof(vnode));
3486 } else if (vnp->count) {
3488 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3491 vnode.modeBits = vnp->modeBits;
3494 vnode.dataVersion++;
3497 IH_IWRITE(vnodeInfo[class].handle,
3498 vnodeIndexOffset(vcp, vnodeNumber),
3499 (char *)&vnode, sizeof(vnode));
3500 assert(nBytes == sizeof(vnode));
3506 if (!Showmode && ofiles) {
3507 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3509 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3513 for (class = 0; class < nVNODECLASSES; class++) {
3514 register struct VnodeInfo *vip = &vnodeInfo[class];
3515 for (i = 0; i < vip->nVnodes; i++)
3516 if (vip->vnodes[i].name)
3517 free(vip->vnodes[i].name);
3524 /* Set correct resource utilization statistics */
3525 volHeader.filecount = FilesInVolume;
3526 volHeader.diskused = BlocksInVolume;
3528 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3529 if (volHeader.uniquifier < (maxunique + 1)) {
3531 Log("Volume uniquifier is too low; fixed\n");
3532 /* Plus 2,000 in case there are workstations out there with
3533 * cached vnodes that have since been deleted
3535 volHeader.uniquifier = (maxunique + 1 + 2000);
3538 /* Turn off the inUse bit; the volume's been salvaged! */
3539 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3540 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3541 volHeader.inService = 1; /* allow service again */
3542 volHeader.needsCallback = (VolumeChanged != 0);
3543 volHeader.dontSalvage = DONT_SALVAGE;
3546 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3547 assert(nBytes == sizeof(volHeader));
3550 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3551 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3552 FilesInVolume, BlocksInVolume);
3554 IH_RELEASE(vnodeInfo[vSmall].handle);
3555 IH_RELEASE(vnodeInfo[vLarge].handle);
3561 ClearROInUseBit(struct VolumeSummary *summary)
3563 IHandle_t *h = summary->volumeInfoHandle;
3564 afs_sfsize_t nBytes;
3566 VolumeDiskData volHeader;
3568 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3569 assert(nBytes == sizeof(volHeader));
3570 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3571 volHeader.inUse = 0;
3572 volHeader.needsSalvaged = 0;
3573 volHeader.inService = 1;
3574 volHeader.dontSalvage = DONT_SALVAGE;
3576 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3577 assert(nBytes == sizeof(volHeader));
3582 * Possible delete the volume.
3584 * deleteMe - Always do so, only a partial volume.
3587 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3590 if (readOnly(isp) || deleteMe) {
3591 if (isp->volSummary && isp->volSummary->fileName) {
3594 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);
3596 Log("It will be deleted on this server (you may find it elsewhere)\n");
3599 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3601 Log("it will be deleted instead. It should be recloned.\n");
3606 sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
3608 code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
3610 Log("Error %ld destroying volume disk header for volume %lu\n",
3611 afs_printable_int32_ld(code),
3612 afs_printable_uint32_lu(isp->volumeId));
3615 /* make sure we actually delete the fileName file; ENOENT
3616 * is fine, since VDestroyVolumeDiskHeader probably already
3618 if (unlink(path) && errno != ENOENT) {
3619 Log("Unable to unlink %s (errno = %d)\n", path, errno);
3623 } else if (!check) {
3624 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3626 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3632 AskOffline(VolumeId volumeId, char * partition)
3637 memset(&res, 0, sizeof(res));
3639 for (i = 0; i < 3; i++) {
3640 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
3642 if (code == SYNC_OK) {
3644 } else if (code == SYNC_DENIED) {
3645 #ifdef DEMAND_ATTACH_ENABLE
3646 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3648 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3650 Abort("Salvage aborted\n");
3651 } else if (code == SYNC_BAD_COMMAND) {
3652 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3654 #ifdef DEMAND_ATTACH_ENABLE
3655 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3657 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3659 Abort("Salvage aborted\n");
3662 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3663 FSYNC_clientFinis();
3667 if (code != SYNC_OK) {
3668 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3669 Abort("Salvage aborted\n");
3672 #ifdef AFS_DEMAND_ATTACH_FS
3673 /* set inUse = programType in the volume header. We do this in case
3674 * the fileserver restarts/crashes while we are salvaging.
3675 * Otherwise, the fileserver could attach the volume again on
3676 * startup while we are salvaging, which would be very bad, or
3677 * schedule another salvage while we are salvaging, which would be
3681 struct VolumeHeader header;
3682 struct VolumeDiskHeader diskHeader;
3683 struct VolumeDiskData volHeader;
3685 code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
3690 DiskToVolumeHeader(&header, &diskHeader);
3692 IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
3693 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
3694 volHeader.stamp.magic != VOLUMEINFOMAGIC) {
3700 volHeader.inUse = programType;
3702 /* If we can't re-write the header, bail out and error. We don't
3703 * assert when reading the header, since it's possible the
3704 * header isn't really there (when there's no data associated
3705 * with the volume; we just delete the vol header file in that
3706 * case). But if it's there enough that we can read it, but
3707 * somehow we cannot write to it to signify we're salvaging it,
3708 * we've got a big problem and we cannot continue. */
3709 assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
3713 #endif /* AFS_DEMAND_ATTACH_FS */
3717 AskOnline(VolumeId volumeId, char *partition)
3721 for (i = 0; i < 3; i++) {
3722 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3724 if (code == SYNC_OK) {
3726 } else if (code == SYNC_DENIED) {
3727 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3728 } else if (code == SYNC_BAD_COMMAND) {
3729 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3731 #ifdef DEMAND_ATTACH_ENABLE
3732 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3734 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3739 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3740 FSYNC_clientFinis();
3747 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3749 /* Volume parameter is passed in case iopen is upgraded in future to
3750 * require a volume Id to be passed
3753 IHandle_t *srcH, *destH;
3754 FdHandle_t *srcFdP, *destFdP;
3757 IH_INIT(srcH, device, rwvolume, inode1);
3758 srcFdP = IH_OPEN(srcH);
3759 assert(srcFdP != NULL);
3760 IH_INIT(destH, device, rwvolume, inode2);
3761 destFdP = IH_OPEN(destH);
3763 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3764 assert(FDH_WRITE(destFdP, buf, n) == n);
3766 FDH_REALLYCLOSE(srcFdP);
3767 FDH_REALLYCLOSE(destFdP);
3774 PrintInodeList(void)
3776 register struct ViceInodeInfo *ip;
3777 struct ViceInodeInfo *buf;
3778 struct afs_stat status;
3779 register int nInodes;
3781 assert(afs_fstat(inodeFd, &status) == 0);
3782 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3783 assert(buf != NULL);
3784 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3785 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3786 for (ip = buf; nInodes--; ip++) {
3787 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3788 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3789 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3790 ip->u.param[2], ip->u.param[3]);
3796 PrintInodeSummary(void)
3799 struct InodeSummary *isp;
3801 for (i = 0; i < nVolumesInInodeFile; i++) {
3802 isp = &inodeSummary[i];
3803 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n", isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes, isp->nSpecialInodes, isp->maxUniquifier);
3808 PrintVolumeSummary(void)
3811 struct VolumeSummary *vsp;
3813 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3814 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3824 assert(0); /* Fork is never executed in the NT code path */
3828 #ifdef AFS_DEMAND_ATTACH_FS
3829 if ((f == 0) && (programType == salvageServer)) {
3830 /* we are a salvageserver child */
3831 #ifdef FSSYNC_BUILD_CLIENT
3832 VChildProcReconnectFS_r();
3834 #ifdef SALVSYNC_BUILD_CLIENT
3838 #endif /* AFS_DEMAND_ATTACH_FS */
3839 #endif /* !AFS_NT40_ENV */
3849 #ifdef AFS_DEMAND_ATTACH_FS
3850 if (programType == salvageServer) {
3851 #ifdef SALVSYNC_BUILD_CLIENT
3854 #ifdef FSSYNC_BUILD_CLIENT
3858 #endif /* AFS_DEMAND_ATTACH_FS */
3861 if (main_thread != pthread_self())
3862 pthread_exit((void *)code);
3875 pid = wait(&status);
3877 if (WCOREDUMP(status))
3878 Log("\"%s\" core dumped!\n", prog);
3879 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3885 TimeStamp(time_t clock, int precision)
3888 static char timestamp[20];
3889 lt = localtime(&clock);
3891 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
3893 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3898 CheckLogFile(char * log_path)
3900 char oldSlvgLog[AFSDIR_PATH_MAX];
3902 #ifndef AFS_NT40_ENV
3909 strcpy(oldSlvgLog, log_path);
3910 strcat(oldSlvgLog, ".old");
3912 renamefile(log_path, oldSlvgLog);
3913 logFile = afs_fopen(log_path, "a");
3915 if (!logFile) { /* still nothing, use stdout */
3919 #ifndef AFS_NAMEI_ENV
3920 AFS_DEBUG_IOPS_LOG(logFile);
3925 #ifndef AFS_NT40_ENV
3927 TimeStampLogFile(char * log_path)
3929 char stampSlvgLog[AFSDIR_PATH_MAX];
3934 lt = localtime(&now);
3935 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3936 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3937 log_path, lt->tm_year + 1900,
3938 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3941 /* try to link the logfile to a timestamped filename */
3942 /* if it fails, oh well, nothing we can do */
3943 link(log_path, stampSlvgLog);
3952 #ifndef AFS_NT40_ENV
3954 printf("Can't show log since using syslog.\n");
3965 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3968 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3971 while (fgets(line, sizeof(line), logFile))
3978 Log(const char *format, ...)
3984 va_start(args, format);
3985 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3987 #ifndef AFS_NT40_ENV
3989 syslog(LOG_INFO, "%s", tmp);
3993 gettimeofday(&now, 0);
3994 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
4000 Abort(const char *format, ...)
4005 va_start(args, format);
4006 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4008 #ifndef AFS_NT40_ENV
4010 syslog(LOG_INFO, "%s", tmp);
4014 fprintf(logFile, "%s", tmp);
4026 ToString(const char *s)
4029 p = (char *)malloc(strlen(s) + 1);
4035 /* Remove the FORCESALVAGE file */
4037 RemoveTheForce(char *path)
4040 struct afs_stat force; /* so we can use afs_stat to find it */
4041 strcpy(target,path);
4042 strcat(target,"/FORCESALVAGE");
4043 if (!Testing && ForceSalvage) {
4044 if (afs_stat(target,&force) == 0) unlink(target);
4048 #ifndef AFS_AIX32_ENV
4050 * UseTheForceLuke - see if we can use the force
4053 UseTheForceLuke(char *path)
4055 struct afs_stat force;
4057 strcpy(target,path);
4058 strcat(target,"/FORCESALVAGE");
4060 return (afs_stat(target, &force) == 0);
4064 * UseTheForceLuke - see if we can use the force
4067 * The VRMIX fsck will not muck with the filesystem it is supposedly
4068 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
4069 * muck directly with the root inode, which is within the normal
4071 * ListViceInodes() has a side effect of setting ForceSalvage if
4072 * it detects a need, based on root inode examination.
4075 UseTheForceLuke(char *path)
4078 return 0; /* sorry OB1 */
4083 /* NT support routines */
4085 static char execpathname[MAX_PATH];
4087 nt_SalvagePartition(char *partName, int jobn)
4092 if (!*execpathname) {
4093 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
4094 if (!n || n == 1023)
4097 job.cj_magic = SALVAGER_MAGIC;
4098 job.cj_number = jobn;
4099 (void)strcpy(job.cj_part, partName);
4100 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
4105 nt_SetupPartitionSalvage(void *datap, int len)
4107 childJob_t *jobp = (childJob_t *) datap;
4108 char logname[AFSDIR_PATH_MAX];
4110 if (len != sizeof(childJob_t))
4112 if (jobp->cj_magic != SALVAGER_MAGIC)
4117 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
4119 logFile = afs_fopen(logname, "w");
4127 #endif /* AFS_NT40_ENV */