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>
101 #include <WINNT/afsevent.h>
103 #include <sys/param.h>
104 #include <sys/file.h>
106 #include <sys/time.h>
107 #endif /* ITIMER_REAL */
109 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
110 #define WCOREDUMP(x) (x & 0200)
113 #include <afs/afsint.h>
114 #include <afs/assert.h>
115 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
116 #if defined(AFS_VFSINCL_ENV)
117 #include <sys/vnode.h>
119 #include <sys/fs/ufs_inode.h>
121 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
122 #include <ufs/ufs/dinode.h>
123 #include <ufs/ffs/fs.h>
125 #include <ufs/inode.h>
128 #else /* AFS_VFSINCL_ENV */
130 #include <ufs/inode.h>
131 #else /* AFS_OSF_ENV */
132 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
133 #include <sys/inode.h>
136 #endif /* AFS_VFSINCL_ENV */
137 #endif /* AFS_SGI_ENV */
140 #include <sys/lockf.h>
144 #include <checklist.h>
146 #if defined(AFS_SGI_ENV)
151 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
154 #include <sys/mnttab.h>
155 #include <sys/mntent.h>
160 #endif /* AFS_SGI_ENV */
161 #endif /* AFS_HPUX_ENV */
166 #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"
195 /*@+fcnmacros +macrofcndecl@*/
198 extern off64_t afs_lseek(int FD, off64_t O, int F);
199 #endif /*S_SPLINT_S */
200 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
201 #define afs_stat stat64
202 #define afs_fstat fstat64
203 #define afs_open open64
204 #define afs_fopen fopen64
205 #else /* !O_LARGEFILE */
207 extern off_t afs_lseek(int FD, off_t O, int F);
208 #endif /*S_SPLINT_S */
209 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
210 #define afs_stat stat
211 #define afs_fstat fstat
212 #define afs_open open
213 #define afs_fopen fopen
214 #endif /* !O_LARGEFILE */
215 /*@=fcnmacros =macrofcndecl@*/
218 extern void *calloc();
220 static char *TimeStamp(time_t clock, int precision);
223 int debug; /* -d flag */
224 int Testing = 0; /* -n flag */
225 int ListInodeOption; /* -i flag */
226 int ShowRootFiles; /* -r flag */
227 int RebuildDirs; /* -sal flag */
228 int Parallel = 4; /* -para X flag */
229 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
230 int forceR = 0; /* -b flag */
231 int ShowLog = 0; /* -showlog flag */
232 int ShowSuid = 0; /* -showsuid flag */
233 int ShowMounts = 0; /* -showmounts flag */
234 int orphans = ORPH_IGNORE; /* -orphans option */
238 int useSyslog = 0; /* -syslog flag */
239 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
242 #define MAXPARALLEL 32
244 int OKToZap; /* -o flag */
245 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
246 * in the volume header */
248 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
250 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
252 Device fileSysDevice; /* The device number of the current
253 * partition being salvaged */
257 char *fileSysPath; /* The path of the mounted partition currently
258 * being salvaged, i.e. the directory
259 * containing the volume headers */
261 char *fileSysPathName; /* NT needs this to make name pretty in log. */
262 IHandle_t *VGLinkH; /* Link handle for current volume group. */
263 int VGLinkH_cnt; /* # of references to lnk handle. */
264 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
266 char *fileSysDeviceName; /* The block device where the file system
267 * being salvaged was mounted */
268 char *filesysfulldev;
270 int VolumeChanged; /* Set by any routine which would change the volume in
271 * a way which would require callback is to be broken if the
272 * volume was put back on line by an active file server */
274 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
276 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
277 int inodeFd; /* File descriptor for inode file */
280 struct VnodeInfo vnodeInfo[nVNODECLASSES];
283 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
284 int nVolumes; /* Number of volumes (read-write and read-only)
285 * in volume summary */
287 extern char * tmpdir = 0;
291 /* Forward declarations */
292 /*@printflike@*/ void Log(const char *format, ...);
293 /*@printflike@*/ void Abort(const char *format, ...);
294 static int IsVnodeOrphaned(VnodeId vnode);
296 /* Uniquifier stored in the Inode */
301 return (u & 0x3fffff);
303 #if defined(AFS_SGI_EXMAG)
304 return (u & SGI_UNIQMASK);
307 #endif /* AFS_SGI_EXMAG */
312 BadError(register int aerror)
314 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
316 return 0; /* otherwise may be transient, e.g. EMFILE */
322 char *save_args[MAX_ARGS];
324 pthread_t main_thread;
328 /* Get the salvage lock if not already held. Hold until process exits. */
330 ObtainSalvageLock(void)
336 (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
337 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
338 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
340 "salvager: There appears to be another salvager running! Aborted.\n");
345 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
346 if (salvageLock < 0) {
348 "salvager: can't open salvage lock file %s, aborting\n",
349 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
352 #ifdef AFS_DARWIN_ENV
353 if (flock(salvageLock, LOCK_EX) == -1) {
355 if (lockf(salvageLock, F_LOCK, 0) == -1) {
358 "salvager: There appears to be another salvager running! Aborted.\n");
365 #ifdef AFS_SGI_XFS_IOPS_ENV
366 /* Check if the given partition is mounted. For XFS, the root inode is not a
367 * constant. So we check the hard way.
370 IsPartitionMounted(char *part)
373 struct mntent *mntent;
375 assert(mntfp = setmntent(MOUNTED, "r"));
376 while (mntent = getmntent(mntfp)) {
377 if (!strcmp(part, mntent->mnt_dir))
382 return mntent ? 1 : 1;
385 /* Check if the given inode is the root of the filesystem. */
386 #ifndef AFS_SGI_XFS_IOPS_ENV
388 IsRootInode(struct afs_stat *status)
391 * The root inode is not a fixed value in XFS partitions. So we need to
392 * see if the partition is in the list of mounted partitions. This only
393 * affects the SalvageFileSys path, so we check there.
395 return (status->st_ino == ROOTINODE);
400 #ifndef AFS_NAMEI_ENV
401 /* We don't want to salvage big files filesystems, since we can't put volumes on
405 CheckIfBigFilesFS(char *mountPoint, char *devName)
407 struct superblock fs;
410 if (strncmp(devName, "/dev/", 5)) {
411 (void)sprintf(name, "/dev/%s", devName);
413 (void)strcpy(name, devName);
416 if (ReadSuper(&fs, name) < 0) {
417 Log("Unable to read superblock. Not salvaging partition %s.\n",
421 if (IsBigFilesFileSystem(&fs)) {
422 Log("Partition %s is a big files filesystem, not salvaging.\n",
432 #define HDSTR "\\Device\\Harddisk"
433 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
435 SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
440 static int dowarn = 1;
442 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
444 if (strncmp(res, HDSTR, HDLEN)) {
447 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
448 res, HDSTR, p1->devName);
452 d1 = atoi(&res[HDLEN]);
454 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
456 if (strncmp(res, HDSTR, HDLEN)) {
459 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
460 res, HDSTR, p2->devName);
464 d2 = atoi(&res[HDLEN]);
469 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
472 /* This assumes that two partitions with the same device number divided by
473 * PartsPerDisk are on the same disk.
476 SalvageFileSysParallel(struct DiskPartition *partP)
479 struct DiskPartition *partP;
480 int pid; /* Pid for this job */
481 int jobnumb; /* Log file job number */
482 struct job *nextjob; /* Next partition on disk to salvage */
484 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
485 struct job *thisjob = 0;
486 static int numjobs = 0;
487 static int jobcount = 0;
493 char logFileName[256];
497 /* We have a partition to salvage. Copy it into thisjob */
498 thisjob = (struct job *)malloc(sizeof(struct job));
500 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
503 memset(thisjob, 0, sizeof(struct job));
504 thisjob->partP = partP;
505 thisjob->jobnumb = jobcount;
507 } else if (jobcount == 0) {
508 /* We are asking to wait for all jobs (partp == 0), yet we never
511 Log("No file system partitions named %s* found; not salvaged\n",
512 VICE_PARTITION_PREFIX);
516 if (debug || Parallel == 1) {
518 SalvageFileSys(thisjob->partP, 0);
525 /* Check to see if thisjob is for a disk that we are already
526 * salvaging. If it is, link it in as the next job to do. The
527 * jobs array has 1 entry per disk being salvages. numjobs is
528 * the total number of disks currently being salvaged. In
529 * order to keep thejobs array compact, when a disk is
530 * completed, the hightest element in the jobs array is moved
531 * down to now open slot.
533 for (j = 0; j < numjobs; j++) {
534 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
535 /* On same disk, add it to this list and return */
536 thisjob->nextjob = jobs[j]->nextjob;
537 jobs[j]->nextjob = thisjob;
544 /* Loop until we start thisjob or until all existing jobs are finished */
545 while (thisjob || (!partP && (numjobs > 0))) {
546 startjob = -1; /* No new job to start */
548 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
549 /* Either the max jobs are running or we have to wait for all
550 * the jobs to finish. In either case, we wait for at least one
551 * job to finish. When it's done, clean up after it.
553 pid = wait(&wstatus);
555 for (j = 0; j < numjobs; j++) { /* Find which job it is */
556 if (pid == jobs[j]->pid)
560 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
561 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
564 numjobs--; /* job no longer running */
565 oldjob = jobs[j]; /* remember */
566 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
567 free(oldjob); /* free the old job */
569 /* If there is another partition on the disk to salvage, then
570 * say we will start it (startjob). If not, then put thisjob there
571 * and say we will start it.
573 if (jobs[j]) { /* Another partitions to salvage */
574 startjob = j; /* Will start it */
575 } else { /* There is not another partition to salvage */
577 jobs[j] = thisjob; /* Add thisjob */
579 startjob = j; /* Will start it */
581 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
582 startjob = -1; /* Don't start it - already running */
586 /* We don't have to wait for a job to complete */
588 jobs[numjobs] = thisjob; /* Add this job */
590 startjob = numjobs; /* Will start it */
594 /* Start up a new salvage job on a partition in job slot "startjob" */
595 if (startjob != -1) {
597 Log("Starting salvage of file system partition %s\n",
598 jobs[startjob]->partP->name);
600 /* For NT, we not only fork, but re-exec the salvager. Pass in the
601 * commands and pass the child job number via the data path.
604 nt_SalvagePartition(jobs[startjob]->partP->name,
605 jobs[startjob]->jobnumb);
606 jobs[startjob]->pid = pid;
611 jobs[startjob]->pid = pid;
617 for (fd = 0; fd < 16; fd++)
624 openlog("salvager", LOG_PID, useSyslogFacility);
628 (void)afs_snprintf(logFileName, sizeof logFileName,
630 AFSDIR_SERVER_SLVGLOG_FILEPATH,
631 jobs[startjob]->jobnumb);
632 logFile = afs_fopen(logFileName, "w");
637 SalvageFileSys1(jobs[startjob]->partP, 0);
642 } /* while ( thisjob || (!partP && numjobs > 0) ) */
644 /* If waited for all jobs to complete, now collect log files and return */
646 if (!useSyslog) /* if syslogging - no need to collect */
649 for (i = 0; i < jobcount; i++) {
650 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
651 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
652 if ((passLog = afs_fopen(logFileName, "r"))) {
653 while (fgets(buf, sizeof(buf), passLog)) {
658 (void)unlink(logFileName);
667 SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
669 if (!canfork || debug || Fork() == 0) {
670 SalvageFileSys1(partP, singleVolumeNumber);
671 if (canfork && !debug) {
676 Wait("SalvageFileSys");
680 get_DevName(char *pbuffer, char *wpath)
682 char pbuf[128], *ptr;
683 strcpy(pbuf, pbuffer);
684 ptr = (char *)strrchr(pbuf, '/');
690 ptr = (char *)strrchr(pbuffer, '/');
692 strcpy(pbuffer, ptr + 1);
699 SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
702 char inodeListPath[256];
703 static char tmpDevName[100];
704 static char wpath[100];
705 struct VolumeSummary *vsp, *esp;
708 fileSysPartition = partP;
709 fileSysDevice = fileSysPartition->device;
710 fileSysPathName = VPartitionPath(fileSysPartition);
713 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
714 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
715 name = partP->devName;
717 fileSysPath = fileSysPathName;
718 strcpy(tmpDevName, partP->devName);
719 name = get_DevName(tmpDevName, wpath);
720 fileSysDeviceName = name;
721 filesysfulldev = wpath;
724 VLockPartition(partP->name);
725 if (singleVolumeNumber || ForceSalvage)
728 ForceSalvage = UseTheForceLuke(fileSysPath);
730 if (singleVolumeNumber) {
731 /* salvageserver already setup fssync conn for us */
732 if ((programType != salvageServer) && !VConnectFS()) {
733 Abort("Couldn't connect to file server\n");
735 AskOffline(singleVolumeNumber);
738 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
739 partP->name, name, (Testing ? "(READONLY mode)" : ""));
741 Log("***Forced salvage of all volumes on this partition***\n");
746 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
753 assert((dirp = opendir(fileSysPath)) != NULL);
754 while ((dp = readdir(dirp))) {
755 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
756 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
758 Log("Removing old salvager temp files %s\n", dp->d_name);
759 strcpy(npath, fileSysPath);
761 strcat(npath, dp->d_name);
767 tdir = (tmpdir ? tmpdir : fileSysPath);
769 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
770 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
772 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
775 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
776 unlink(inodeListPath);
780 /* Using nt_unlink here since we're really using the delete on close
781 * semantics of unlink. In most places in the salvager, we really do
782 * mean to unlink the file at that point. Those places have been
783 * modified to actually do that so that the NT crt can be used there.
786 _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
787 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
789 inodeFd = afs_open(inodeListPath, O_RDONLY);
790 unlink(inodeListPath);
793 Abort("Temporary file %s is missing...\n", inodeListPath);
794 if (ListInodeOption) {
798 /* enumerate volumes in the partition.
799 * figure out sets of read-only + rw volumes.
800 * salvage each set, read-only volumes first, then read-write.
801 * Fix up inodes on last volume in set (whether it is read-write
804 GetVolumeSummary(singleVolumeNumber);
806 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
807 i < nVolumesInInodeFile; i = j) {
808 VolumeId rwvid = inodeSummary[i].RWvolumeId;
810 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
812 VolumeId vid = inodeSummary[j].volumeId;
813 struct VolumeSummary *tsp;
814 /* Scan volume list (from partition root directory) looking for the
815 * current rw volume number in the volume list from the inode scan.
816 * If there is one here that is not in the inode volume list,
818 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
820 DeleteExtraVolumeHeaderFile(vsp);
822 /* Now match up the volume summary info from the root directory with the
823 * entry in the volume list obtained from scanning inodes */
824 inodeSummary[j].volSummary = NULL;
825 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
826 if (tsp->header.id == vid) {
827 inodeSummary[j].volSummary = tsp;
833 /* Salvage the group of volumes (several read-only + 1 read/write)
834 * starting with the current read-only volume we're looking at.
836 SalvageVolumeGroup(&inodeSummary[i], j - i);
839 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
840 for (; vsp < esp; vsp++) {
842 DeleteExtraVolumeHeaderFile(vsp);
845 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
846 RemoveTheForce(fileSysPath);
848 if (!Testing && singleVolumeNumber) {
849 AskOnline(singleVolumeNumber, fileSysPartition->name);
851 /* Step through the volumeSummary list and set all volumes on-line.
852 * The volumes were taken off-line in GetVolumeSummary.
854 for (j = 0; j < nVolumes; j++) {
855 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
859 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
860 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
863 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
867 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
870 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
872 unlink(vsp->fileName);
876 CompareInodes(const void *_p1, const void *_p2)
878 register const struct ViceInodeInfo *p1 = _p1;
879 register const struct ViceInodeInfo *p2 = _p2;
880 if (p1->u.vnode.vnodeNumber == INODESPECIAL
881 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
882 VolumeId p1rwid, p2rwid;
884 (p1->u.vnode.vnodeNumber ==
885 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
887 (p2->u.vnode.vnodeNumber ==
888 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
893 if (p1->u.vnode.vnodeNumber == INODESPECIAL
894 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
895 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
896 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
897 if (p1->u.vnode.volumeId == p1rwid)
899 if (p2->u.vnode.volumeId == p2rwid)
901 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
903 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
904 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
905 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
907 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
909 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
911 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
913 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
915 /* The following tests are reversed, so that the most desirable
916 * of several similar inodes comes first */
917 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
919 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
920 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
924 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
925 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
930 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
932 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
933 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
937 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
938 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
943 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
945 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
946 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
950 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
951 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
956 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
958 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
959 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
963 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
964 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
973 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
974 register struct InodeSummary *summary)
976 int volume = ip->u.vnode.volumeId;
977 int rwvolume = volume;
978 register n, nSpecial;
979 register Unique maxunique;
982 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
984 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
986 rwvolume = ip->u.special.parentId;
987 /* This isn't quite right, as there could (in error) be different
988 * parent inodes in different special vnodes */
990 if (maxunique < ip->u.vnode.vnodeUniquifier)
991 maxunique = ip->u.vnode.vnodeUniquifier;
995 summary->volumeId = volume;
996 summary->RWvolumeId = rwvolume;
997 summary->nInodes = n;
998 summary->nSpecialInodes = nSpecial;
999 summary->maxUniquifier = maxunique;
1003 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber, void *rock)
1005 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1006 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1007 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1012 * Collect list of inodes in file named by path. If a truly fatal error,
1013 * unlink the file and abort. For lessor errors, return -1. The file will
1014 * be unlinked by the caller.
1017 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1019 struct afs_stat status;
1021 struct ViceInodeInfo *ip;
1022 struct InodeSummary summary;
1023 char summaryFileName[50];
1026 char *dev = fileSysPath;
1027 char *wpath = fileSysPath;
1029 char *dev = fileSysDeviceName;
1030 char *wpath = filesysfulldev;
1032 char *part = fileSysPath;
1035 /* This file used to come from vfsck; cobble it up ourselves now... */
1037 ListViceInodes(dev, fileSysPath, path,
1038 singleVolumeNumber ? OnlyOneVolume : 0,
1039 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1041 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, path, dev);
1045 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1047 if (forceSal && !ForceSalvage) {
1048 Log("***Forced salvage of all volumes on this partition***\n");
1051 inodeFd = afs_open(path, O_RDWR);
1052 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1054 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1056 tdir = (tmpdir ? tmpdir : part);
1058 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1059 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1061 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1062 "%s/salvage.temp.%d", tdir, getpid());
1064 summaryFile = afs_fopen(summaryFileName, "a+");
1065 if (summaryFile == NULL) {
1068 Abort("Unable to create inode summary file\n");
1070 if (!canfork || debug || Fork() == 0) {
1072 unsigned long st_size=(unsigned long) status.st_size;
1073 nInodes = st_size / sizeof(struct ViceInodeInfo);
1075 fclose(summaryFile);
1077 unlink(summaryFileName);
1078 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1079 RemoveTheForce(fileSysPath);
1081 struct VolumeSummary *vsp;
1084 GetVolumeSummary(singleVolumeNumber);
1086 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1088 DeleteExtraVolumeHeaderFile(vsp);
1091 Log("%s vice inodes on %s; not salvaged\n",
1092 singleVolumeNumber ? "No applicable" : "No", dev);
1095 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1097 fclose(summaryFile);
1100 unlink(summaryFileName);
1102 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1105 if (read(inodeFd, ip, st_size) != st_size) {
1106 fclose(summaryFile);
1109 unlink(summaryFileName);
1110 Abort("Unable to read inode table; %s not salvaged\n", dev);
1112 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1113 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1114 || write(inodeFd, ip, st_size) != st_size) {
1115 fclose(summaryFile);
1118 unlink(summaryFileName);
1119 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1123 CountVolumeInodes(ip, nInodes, &summary);
1124 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1125 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1126 fclose(summaryFile);
1130 summary.index += (summary.nInodes);
1131 nInodes -= summary.nInodes;
1132 ip += summary.nInodes;
1134 /* Following fflush is not fclose, because if it was debug mode would not work */
1135 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1136 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1137 fclose(summaryFile);
1141 if (canfork && !debug) {
1146 if (Wait("Inode summary") == -1) {
1147 fclose(summaryFile);
1150 unlink(summaryFileName);
1151 Exit(1); /* salvage of this partition aborted */
1154 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1155 if (status.st_size != 0) {
1157 unsigned long st_status=(unsigned long)status.st_size;
1158 inodeSummary = (struct InodeSummary *)malloc(st_status);
1159 assert(inodeSummary != NULL);
1160 /* For GNU we need to do lseek to get the file pointer moved. */
1161 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1162 ret = read(fileno(summaryFile), inodeSummary, st_status);
1163 assert(ret == st_status);
1165 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1166 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1167 fclose(summaryFile);
1169 unlink(summaryFileName);
1173 /* Comparison routine for volume sort.
1174 This is setup so that a read-write volume comes immediately before
1175 any read-only clones of that volume */
1177 CompareVolumes(const void *_p1, const void *_p2)
1179 register const struct VolumeSummary *p1 = _p1;
1180 register const struct VolumeSummary *p2 = _p2;
1181 if (p1->header.parent != p2->header.parent)
1182 return p1->header.parent < p2->header.parent ? -1 : 1;
1183 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1185 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1187 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1191 GetVolumeSummary(VolumeId singleVolumeNumber)
1194 afs_int32 nvols = 0;
1195 struct VolumeSummary *vsp, vs;
1196 struct VolumeDiskHeader diskHeader;
1199 /* Get headers from volume directory */
1200 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1201 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1202 if (!singleVolumeNumber) {
1203 while ((dp = readdir(dirp))) {
1204 char *p = dp->d_name;
1205 p = strrchr(dp->d_name, '.');
1206 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1208 if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
1209 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1210 == sizeof(diskHeader)
1211 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1212 DiskToVolumeHeader(&vs.header, &diskHeader);
1220 dirp = opendir("."); /* No rewinddir for NT */
1227 (struct VolumeSummary *)malloc(nvols *
1228 sizeof(struct VolumeSummary));
1231 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1232 assert(volumeSummaryp != NULL);
1235 vsp = volumeSummaryp;
1236 while ((dp = readdir(dirp))) {
1237 char *p = dp->d_name;
1238 p = strrchr(dp->d_name, '.');
1239 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1242 if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
1243 || read(fd, &diskHeader, sizeof(diskHeader))
1244 != sizeof(diskHeader)
1245 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1250 if (!singleVolumeNumber) {
1252 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1257 char nameShouldBe[64];
1258 DiskToVolumeHeader(&vsp->header, &diskHeader);
1259 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1260 && vsp->header.parent != singleVolumeNumber) {
1261 Log("%u is a read-only volume; not salvaged\n",
1262 singleVolumeNumber);
1265 if (!singleVolumeNumber
1266 || (vsp->header.id == singleVolumeNumber
1267 || vsp->header.parent == singleVolumeNumber)) {
1268 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1269 VFORMAT, vsp->header.id);
1270 if (singleVolumeNumber)
1271 AskOffline(vsp->header.id);
1272 if (strcmp(nameShouldBe, dp->d_name)) {
1274 Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", dp->d_name, (Testing ? "it would have been " : ""));
1278 vsp->fileName = ToString(dp->d_name);
1288 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1292 /* Find the link table. This should be associated with the RW volume or, if
1293 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1296 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1297 struct ViceInodeInfo *allInodes)
1300 struct ViceInodeInfo *ip;
1302 for (i = 0; i < nVols; i++) {
1303 ip = allInodes + isp[i].index;
1304 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1305 if (ip[j].u.special.type == VI_LINKTABLE)
1306 return ip[j].inodeNumber;
1313 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1315 struct versionStamp version;
1318 if (!VALID_INO(ino))
1320 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1321 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1322 if (!VALID_INO(ino))
1324 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1325 isp->RWvolumeId, errno);
1326 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1327 fdP = IH_OPEN(VGLinkH);
1329 Abort("Can't open link table for volume %u (error = %d)\n",
1330 isp->RWvolumeId, errno);
1332 if (FDH_TRUNC(fdP, 0) < 0)
1333 Abort("Can't truncate link table for volume %u (error = %d)\n",
1334 isp->RWvolumeId, errno);
1336 version.magic = LINKTABLEMAGIC;
1337 version.version = LINKTABLEVERSION;
1339 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1341 Abort("Can't truncate link table for volume %u (error = %d)\n",
1342 isp->RWvolumeId, errno);
1344 FDH_REALLYCLOSE(fdP);
1346 /* If the volume summary exits (i.e., the V*.vol header file exists),
1347 * then set this inode there as well.
1349 if (isp->volSummary)
1350 isp->volSummary->header.linkTable = ino;
1359 SVGParms_t *parms = (SVGParms_t *) arg;
1360 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1365 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1368 pthread_attr_t tattr;
1372 /* Initialize per volume global variables, even if later code does so */
1376 memset(&VolInfo, 0, sizeof(VolInfo));
1378 parms.svgp_inodeSummaryp = isp;
1379 parms.svgp_count = nVols;
1380 code = pthread_attr_init(&tattr);
1382 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1386 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1388 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1391 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1393 Log("Failed to create thread to salvage volume group %u\n",
1397 (void)pthread_join(tid, NULL);
1399 #endif /* AFS_NT40_ENV */
1402 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1404 struct ViceInodeInfo *inodes, *allInodes, *ip;
1405 int i, totalInodes, size, salvageTo;
1409 int dec_VGLinkH = 0;
1411 FdHandle_t *fdP = NULL;
1414 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1415 && isp->nSpecialInodes > 0);
1416 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1417 if (!ForceSalvage && QuickCheck(isp, nVols))
1420 if (ShowMounts && !haveRWvolume)
1422 if (canfork && !debug && Fork() != 0) {
1423 (void)Wait("Salvage volume group");
1426 for (i = 0, totalInodes = 0; i < nVols; i++)
1427 totalInodes += isp[i].nInodes;
1428 size = totalInodes * sizeof(struct ViceInodeInfo);
1429 inodes = (struct ViceInodeInfo *)malloc(size);
1430 allInodes = inodes - isp->index; /* this would the base of all the inodes
1431 * for the partition, if all the inodes
1432 * had been read into memory */
1434 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1436 assert(read(inodeFd, inodes, size) == size);
1438 /* Don't try to salvage a read write volume if there isn't one on this
1440 salvageTo = haveRWvolume ? 0 : 1;
1442 #ifdef AFS_NAMEI_ENV
1443 ino = FindLinkHandle(isp, nVols, allInodes);
1444 if (VALID_INO(ino)) {
1445 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1446 fdP = IH_OPEN(VGLinkH);
1448 if (!VALID_INO(ino) || fdP == NULL) {
1449 Log("%s link table for volume %u.\n",
1450 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1452 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1454 CreateLinkTable(isp, ino);
1458 FDH_REALLYCLOSE(fdP);
1460 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1463 /* Salvage in reverse order--read/write volume last; this way any
1464 * Inodes not referenced by the time we salvage the read/write volume
1465 * can be picked up by the read/write volume */
1466 /* ACTUALLY, that's not done right now--the inodes just vanish */
1467 for (i = nVols - 1; i >= salvageTo; i--) {
1469 struct InodeSummary *lisp = &isp[i];
1470 #ifdef AFS_NAMEI_ENV
1471 /* If only the RO is present on this partition, the link table
1472 * shows up as a RW volume special file. Need to make sure the
1473 * salvager doesn't try to salvage the non-existent RW.
1475 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1476 /* If this only special inode is the link table, continue */
1477 if (inodes->u.special.type == VI_LINKTABLE) {
1484 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1485 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1486 /* Check inodes twice. The second time do things seriously. This
1487 * way the whole RO volume can be deleted, below, if anything goes wrong */
1488 for (check = 1; check >= 0; check--) {
1490 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1492 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1493 if (rw && deleteMe) {
1494 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1495 * volume won't be called */
1501 if (rw && check == 1)
1503 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1504 MaybeZapVolume(lisp, "Vnode index", 0, check);
1510 /* Fix actual inode counts */
1512 Log("totalInodes %d\n",totalInodes);
1513 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1514 static int TraceBadLinkCounts = 0;
1515 #ifdef AFS_NAMEI_ENV
1516 if (VGLinkH->ih_ino == ip->inodeNumber) {
1517 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1518 VGLinkH_p1 = ip->u.param[0];
1519 continue; /* Deal with this last. */
1522 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1523 TraceBadLinkCounts--; /* Limit reports, per volume */
1524 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]);
1526 while (ip->linkCount > 0) {
1527 /* below used to assert, not break */
1529 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1530 Log("idec failed. inode %s errno %d\n",
1531 PrintInode(NULL, ip->inodeNumber), errno);
1537 while (ip->linkCount < 0) {
1538 /* these used to be asserts */
1540 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1541 Log("iinc failed. inode %s errno %d\n",
1542 PrintInode(NULL, ip->inodeNumber), errno);
1549 #ifdef AFS_NAMEI_ENV
1550 while (dec_VGLinkH > 0) {
1551 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1552 Log("idec failed on link table, errno = %d\n", errno);
1556 while (dec_VGLinkH < 0) {
1557 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1558 Log("iinc failed on link table, errno = %d\n", errno);
1565 /* Directory consistency checks on the rw volume */
1567 SalvageVolume(isp, VGLinkH);
1568 IH_RELEASE(VGLinkH);
1570 if (canfork && !debug) {
1577 QuickCheck(register struct InodeSummary *isp, int nVols)
1579 /* Check headers BEFORE forking */
1583 for (i = 0; i < nVols; i++) {
1584 struct VolumeSummary *vs = isp[i].volSummary;
1585 VolumeDiskData volHeader;
1587 /* Don't salvage just because phantom rw volume is there... */
1588 /* (If a read-only volume exists, read/write inodes must also exist) */
1589 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1593 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1594 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1595 == sizeof(volHeader)
1596 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1597 && volHeader.dontSalvage == DONT_SALVAGE
1598 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1599 if (volHeader.inUse == 1) {
1600 volHeader.inUse = 0;
1601 volHeader.inService = 1;
1603 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1604 != sizeof(volHeader)) {
1620 /* SalvageVolumeHeaderFile
1622 * Salvage the top level V*.vol header file. Make sure the special files
1623 * exist and that there are no duplicates.
1625 * Calls SalvageHeader for each possible type of volume special file.
1629 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1630 register struct ViceInodeInfo *inodes, int RW,
1631 int check, int *deleteMe)
1635 register struct ViceInodeInfo *ip;
1636 int allinodesobsolete = 1;
1637 struct VolumeDiskHeader diskHeader;
1641 memset(&tempHeader, 0, sizeof(tempHeader));
1642 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1643 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1644 tempHeader.id = isp->volumeId;
1645 tempHeader.parent = isp->RWvolumeId;
1646 /* Check for duplicates (inodes are sorted by type field) */
1647 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1648 ip = &inodes[isp->index + i];
1649 if (ip->u.special.type == (ip + 1)->u.special.type) {
1651 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1655 for (i = 0; i < isp->nSpecialInodes; i++) {
1656 ip = &inodes[isp->index + i];
1657 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1659 Log("Rubbish header inode\n");
1662 Log("Rubbish header inode; deleted\n");
1663 } else if (!stuff[ip->u.special.type - 1].obsolete) {
1664 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1665 if (!check && ip->u.special.type != VI_LINKTABLE)
1666 ip->linkCount--; /* Keep the inode around */
1667 allinodesobsolete = 0;
1671 if (allinodesobsolete) {
1678 VGLinkH_cnt++; /* one for every header. */
1680 if (!RW && !check && isp->volSummary) {
1681 ClearROInUseBit(isp->volSummary);
1685 for (i = 0; i < MAXINODETYPE; i++) {
1686 if (stuff[i].inodeType == VI_LINKTABLE) {
1687 /* Gross hack: SalvageHeader does a bcmp on the volume header.
1688 * And we may have recreated the link table earlier, so set the
1689 * RW header as well.
1691 if (VALID_INO(VGLinkH->ih_ino)) {
1692 *stuff[i].inode = VGLinkH->ih_ino;
1696 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1700 if (isp->volSummary == NULL) {
1702 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1704 Log("No header file for volume %u\n", isp->volumeId);
1708 Log("No header file for volume %u; %screating %s/%s\n",
1709 isp->volumeId, (Testing ? "it would have been " : ""),
1710 fileSysPathName, name);
1711 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
1712 assert(headerFd != -1);
1713 isp->volSummary = (struct VolumeSummary *)
1714 malloc(sizeof(struct VolumeSummary));
1715 isp->volSummary->fileName = ToString(name);
1718 /* hack: these two fields are obsolete... */
1719 isp->volSummary->header.volumeAcl = 0;
1720 isp->volSummary->header.volumeMountTable = 0;
1723 (&isp->volSummary->header, &tempHeader,
1724 sizeof(struct VolumeHeader))) {
1725 /* We often remove the name before calling us, so we make a fake one up */
1726 if (isp->volSummary->fileName) {
1727 strcpy(name, isp->volSummary->fileName);
1729 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1730 isp->volSummary->fileName = ToString(name);
1733 Log("Header file %s is damaged or no longer valid%s\n", name,
1734 (check ? "" : "; repairing"));
1738 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
1739 assert(headerFd != -1);
1743 memcpy(&isp->volSummary->header, &tempHeader,
1744 sizeof(struct VolumeHeader));
1747 Log("It would have written a new header file for volume %u\n",
1750 VolumeHeaderToDisk(&diskHeader, &tempHeader);
1751 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1752 != sizeof(struct VolumeDiskHeader)) {
1753 Log("Couldn't rewrite volume header file!\n");
1760 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1761 isp->volSummary->header.volumeInfo);
1766 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1770 VolumeDiskData volumeInfo;
1771 struct versionStamp fileHeader;
1780 #ifndef AFS_NAMEI_ENV
1781 if (sp->inodeType == VI_LINKTABLE)
1784 if (*(sp->inode) == 0) {
1786 Log("Missing inode in volume header (%s)\n", sp->description);
1790 Log("Missing inode in volume header (%s); %s\n", sp->description,
1791 (Testing ? "it would have recreated it" : "recreating"));
1794 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1795 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1796 if (!VALID_INO(*(sp->inode)))
1798 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1799 sp->description, errno);
1804 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1805 fdP = IH_OPEN(specH);
1806 if (OKToZap && (fdP == NULL) && BadError(errno)) {
1807 /* bail out early and destroy the volume */
1809 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1816 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1817 sp->description, errno);
1820 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1821 || header.fileHeader.magic != sp->stamp.magic)) {
1823 Log("Part of the header (%s) is corrupted\n", sp->description);
1824 FDH_REALLYCLOSE(fdP);
1828 Log("Part of the header (%s) is corrupted; recreating\n",
1832 if (sp->inodeType == VI_VOLINFO
1833 && header.volumeInfo.destroyMe == DESTROY_ME) {
1836 FDH_REALLYCLOSE(fdP);
1840 if (recreate && !Testing) {
1843 ("Internal error: recreating volume header (%s) in check mode\n",
1845 code = FDH_TRUNC(fdP, 0);
1847 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1848 sp->description, errno);
1850 /* The following code should be moved into vutil.c */
1851 if (sp->inodeType == VI_VOLINFO) {
1853 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1854 header.volumeInfo.stamp = sp->stamp;
1855 header.volumeInfo.id = isp->volumeId;
1856 header.volumeInfo.parentId = isp->RWvolumeId;
1857 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1858 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1859 isp->volumeId, isp->volumeId);
1860 header.volumeInfo.inService = 0;
1861 header.volumeInfo.blessed = 0;
1862 /* The + 1000 is a hack in case there are any files out in venus caches */
1863 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1864 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
1865 header.volumeInfo.needsCallback = 0;
1866 gettimeofday(&tp, 0);
1867 header.volumeInfo.creationDate = tp.tv_sec;
1868 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1870 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1871 sp->description, errno);
1874 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1875 sizeof(header.volumeInfo));
1876 if (code != sizeof(header.volumeInfo)) {
1879 ("Unable to write volume header file (%s) (errno = %d)\n",
1880 sp->description, errno);
1881 Abort("Unable to write entire volume header file (%s)\n",
1885 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1887 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1888 sp->description, errno);
1890 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1891 if (code != sizeof(sp->stamp)) {
1894 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1895 sp->description, errno);
1897 ("Unable to write entire version stamp in volume header file (%s)\n",
1902 FDH_REALLYCLOSE(fdP);
1904 if (sp->inodeType == VI_VOLINFO) {
1905 VolInfo = header.volumeInfo;
1908 if (VolInfo.updateDate) {
1909 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1911 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1912 (Testing ? "it would have been " : ""), update);
1914 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1916 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1917 VolInfo.id, update);
1927 SalvageVnodes(register struct InodeSummary *rwIsp,
1928 register struct InodeSummary *thisIsp,
1929 register struct ViceInodeInfo *inodes, int check)
1931 int ilarge, ismall, ioffset, RW, nInodes;
1932 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
1935 RW = (rwIsp == thisIsp);
1936 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1938 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1939 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1940 if (check && ismall == -1)
1943 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1944 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1945 return (ilarge == 0 && ismall == 0 ? 0 : -1);
1949 SalvageIndex(Inode ino, VnodeClass class, int RW,
1950 register struct ViceInodeInfo *ip, int nInodes,
1951 struct VolumeSummary *volSummary, int check)
1953 VolumeId volumeNumber;
1954 char buf[SIZEOF_LARGEDISKVNODE];
1955 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1957 StreamHandle_t *file;
1958 struct VnodeClassInfo *vcp;
1960 afs_fsize_t vnodeLength;
1961 int vnodeIndex, nVnodes;
1962 afs_ino_str_t stmp1, stmp2;
1966 volumeNumber = volSummary->header.id;
1967 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
1968 fdP = IH_OPEN(handle);
1969 assert(fdP != NULL);
1970 file = FDH_FDOPEN(fdP, "r+");
1971 assert(file != NULL);
1972 vcp = &VnodeClassInfo[class];
1973 size = OS_SIZE(fdP->fd_fd);
1975 nVnodes = (size / vcp->diskSize) - 1;
1977 assert((nVnodes + 1) * vcp->diskSize == size);
1978 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
1982 for (vnodeIndex = 0;
1983 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
1984 nVnodes--, vnodeIndex++) {
1985 if (vnode->type != vNull) {
1986 int vnodeChanged = 0;
1987 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
1988 /* Log programs that belong to root (potentially suid root);
1989 * don't bother for read-only or backup volumes */
1990 #ifdef notdef /* This is done elsewhere */
1991 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
1992 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);
1994 if (VNDISK_GET_INO(vnode) == 0) {
1996 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
1997 memset(vnode, 0, vcp->diskSize);
2001 if (vcp->magic != vnode->vnodeMagic) {
2002 /* bad magic #, probably partially created vnode */
2003 Log("Partially allocated vnode %d deleted.\n",
2005 memset(vnode, 0, vcp->diskSize);
2009 /* ****** Should do a bit more salvage here: e.g. make sure
2010 * vnode type matches what it should be given the index */
2011 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2012 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2013 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2014 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2021 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2022 /* The following doesn't work, because the version number
2023 * is not maintained correctly by the file server */
2024 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2025 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2027 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2033 /* For RW volume, look for vnode with matching inode number;
2034 * if no such match, take the first determined by our sort
2036 register struct ViceInodeInfo *lip = ip;
2037 register int lnInodes = nInodes;
2039 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2040 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2049 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2050 /* "Matching" inode */
2054 vu = vnode->uniquifier;
2055 iu = ip->u.vnode.vnodeUniquifier;
2056 vd = vnode->dataVersion;
2057 id = ip->u.vnode.inodeDataVersion;
2059 * Because of the possibility of the uniquifier overflows (> 4M)
2060 * we compare them modulo the low 22-bits; we shouldn't worry
2061 * about mismatching since they shouldn't to many old
2062 * uniquifiers of the same vnode...
2064 if (IUnique(vu) != IUnique(iu)) {
2066 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2069 vnode->uniquifier = iu;
2070 #ifdef AFS_3DISPARES
2071 vnode->dataVersion = (id >= vd ?
2074 1887437 ? vd : id) :
2077 1887437 ? id : vd));
2079 #if defined(AFS_SGI_EXMAG)
2080 vnode->dataVersion = (id >= vd ?
2083 15099494 ? vd : id) :
2086 15099494 ? id : vd));
2088 vnode->dataVersion = (id > vd ? id : vd);
2089 #endif /* AFS_SGI_EXMAG */
2090 #endif /* AFS_3DISPARES */
2093 /* don't bother checking for vd > id any more, since
2094 * partial file transfers always result in this state,
2095 * and you can't do much else anyway (you've already
2096 * found the best data you can) */
2097 #ifdef AFS_3DISPARES
2098 if (!vnodeIsDirectory(vnodeNumber)
2099 && ((vd < id && (id - vd) < 1887437)
2100 || ((vd > id && (vd - id) > 1887437)))) {
2102 #if defined(AFS_SGI_EXMAG)
2103 if (!vnodeIsDirectory(vnodeNumber)
2104 && ((vd < id && (id - vd) < 15099494)
2105 || ((vd > id && (vd - id) > 15099494)))) {
2107 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2108 #endif /* AFS_SGI_EXMAG */
2111 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2112 vnode->dataVersion = id;
2117 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2120 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);
2122 VNDISK_SET_INO(vnode, ip->inodeNumber);
2127 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);
2129 VNDISK_SET_INO(vnode, ip->inodeNumber);
2132 VNDISK_GET_LEN(vnodeLength, vnode);
2133 if (ip->byteCount != vnodeLength) {
2136 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2141 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2142 VNDISK_SET_LEN(vnode, ip->byteCount);
2146 ip->linkCount--; /* Keep the inode around */
2149 } else { /* no matching inode */
2150 if (VNDISK_GET_INO(vnode) != 0
2151 || vnode->type == vDirectory) {
2152 /* No matching inode--get rid of the vnode */
2154 if (VNDISK_GET_INO(vnode)) {
2156 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2160 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2165 if (VNDISK_GET_INO(vnode)) {
2167 Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime((time_t *) & (vnode->serverModifyTime)));
2171 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime((time_t *) & (vnode->serverModifyTime)));
2173 memset(vnode, 0, vcp->diskSize);
2176 /* Should not reach here becuase we checked for
2177 * (inodeNumber == 0) above. And where we zero the vnode,
2178 * we also goto vnodeDone.
2182 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2186 } /* VNDISK_GET_INO(vnode) != 0 */
2188 assert(!(vnodeChanged && check));
2189 if (vnodeChanged && !Testing) {
2191 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2192 (char *)vnode, vcp->diskSize)
2194 VolumeChanged = 1; /* For break call back */
2205 struct VnodeEssence *
2206 CheckVnodeNumber(VnodeId vnodeNumber)
2209 struct VnodeInfo *vip;
2212 class = vnodeIdToClass(vnodeNumber);
2213 vip = &vnodeInfo[class];
2214 offset = vnodeIdToBitNumber(vnodeNumber);
2215 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2219 CopyOnWrite(register struct DirSummary *dir)
2221 /* Copy the directory unconditionally if we are going to change it:
2222 * not just if was cloned.
2224 struct VnodeDiskObject vnode;
2225 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2226 Inode oldinode, newinode;
2229 if (dir->copied || Testing)
2231 DFlush(); /* Well justified paranoia... */
2234 IH_IREAD(vnodeInfo[vLarge].handle,
2235 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2237 assert(code == sizeof(vnode));
2238 oldinode = VNDISK_GET_INO(&vnode);
2239 /* Increment the version number by a whole lot to avoid problems with
2240 * clients that were promised new version numbers--but the file server
2241 * crashed before the versions were written to disk.
2244 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2245 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2247 assert(VALID_INO(newinode));
2248 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2250 VNDISK_SET_INO(&vnode, newinode);
2252 IH_IWRITE(vnodeInfo[vLarge].handle,
2253 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2255 assert(code == sizeof(vnode));
2257 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2258 fileSysDevice, newinode);
2259 /* Don't delete the original inode right away, because the directory is
2260 * still being scanned.
2266 * This function should either successfully create a new dir, or give up
2267 * and leave things the way they were. In particular, if it fails to write
2268 * the new dir properly, it should return w/o changing the reference to the
2272 CopyAndSalvage(register struct DirSummary *dir)
2274 struct VnodeDiskObject vnode;
2275 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2276 Inode oldinode, newinode;
2278 register afs_int32 code;
2279 afs_int32 parentUnique = 1;
2280 struct VnodeEssence *vnodeEssence;
2284 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2286 IH_IREAD(vnodeInfo[vLarge].handle,
2287 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2289 assert(code == sizeof(vnode));
2290 oldinode = VNDISK_GET_INO(&vnode);
2291 /* Increment the version number by a whole lot to avoid problems with
2292 * clients that were promised new version numbers--but the file server
2293 * crashed before the versions were written to disk.
2296 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2297 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2299 assert(VALID_INO(newinode));
2300 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2302 /* Assign . and .. vnode numbers from dir and vnode.parent.
2303 * The uniquifier for . is in the vnode.
2304 * The uniquifier for .. might be set to a bogus value of 1 and
2305 * the salvager will later clean it up.
2307 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2308 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2311 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2313 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2318 /* didn't really build the new directory properly, let's just give up. */
2319 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2321 Log("Directory salvage returned code %d, continuing.\n", code);
2324 Log("Checking the results of the directory salvage...\n");
2325 if (!DirOK(&newdir)) {
2326 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2327 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2332 VNDISK_SET_INO(&vnode, newinode);
2333 VNDISK_SET_LEN(&vnode, Length(&newdir));
2335 IH_IWRITE(vnodeInfo[vLarge].handle,
2336 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2338 assert(code == sizeof(vnode));
2340 nt_sync(fileSysDevice);
2342 sync(); /* this is slow, but hopefully rarely called. We don't have
2343 * an open FD on the file itself to fsync.
2346 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2348 dir->dirHandle = newdir;
2352 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2355 struct VnodeEssence *vnodeEssence;
2356 afs_int32 dirOrphaned, todelete;
2358 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2360 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2361 if (vnodeEssence == NULL) {
2363 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2367 assert(Delete(&dir->dirHandle, name) == 0);
2372 #ifndef AFS_NAMEI_ENV
2373 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2374 * mount inode for the partition. If this inode were deleted, it would crash
2377 if (vnodeEssence->InodeNumber == 0) {
2378 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"));
2381 assert(Delete(&dir->dirHandle, name) == 0);
2388 if (!(vnodeNumber & 1) && !Showmode
2389 && !(vnodeEssence->count || vnodeEssence->unique
2390 || vnodeEssence->modeBits)) {
2391 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2392 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2393 vnodeNumber, unique,
2394 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2399 assert(Delete(&dir->dirHandle, name) == 0);
2405 /* Check if the Uniquifiers match. If not, change the directory entry
2406 * so its unique matches the vnode unique. Delete if the unique is zero
2407 * or if the directory is orphaned.
2409 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2410 if (!vnodeEssence->unique
2411 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2412 /* This is an orphaned directory. Don't delete the . or ..
2413 * entry. Otherwise, it will get created in the next
2414 * salvage and deleted again here. So Just skip it.
2419 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2422 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")));
2426 fid.Vnode = vnodeNumber;
2427 fid.Unique = vnodeEssence->unique;
2429 assert(Delete(&dir->dirHandle, name) == 0);
2431 assert(Create(&dir->dirHandle, name, &fid) == 0);
2434 return; /* no need to continue */
2437 if (strcmp(name, ".") == 0) {
2438 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2441 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2444 assert(Delete(&dir->dirHandle, ".") == 0);
2445 fid.Vnode = dir->vnodeNumber;
2446 fid.Unique = dir->unique;
2447 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2450 vnodeNumber = fid.Vnode; /* Get the new Essence */
2451 unique = fid.Unique;
2452 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2455 } else if (strcmp(name, "..") == 0) {
2458 struct VnodeEssence *dotdot;
2459 pa.Vnode = dir->parent;
2460 dotdot = CheckVnodeNumber(pa.Vnode);
2461 assert(dotdot != NULL); /* XXX Should not be assert */
2462 pa.Unique = dotdot->unique;
2464 pa.Vnode = dir->vnodeNumber;
2465 pa.Unique = dir->unique;
2467 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2469 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2472 assert(Delete(&dir->dirHandle, "..") == 0);
2473 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2476 vnodeNumber = pa.Vnode; /* Get the new Essence */
2478 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2480 dir->haveDotDot = 1;
2481 } else if (strncmp(name, ".__afs", 6) == 0) {
2483 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);
2487 assert(Delete(&dir->dirHandle, name) == 0);
2489 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2490 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2493 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2494 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);
2495 if (ShowMounts && (vnodeEssence->type == vSymlink)
2496 && !(vnodeEssence->modeBits & 0111)) {
2502 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2503 vnodeEssence->InodeNumber);
2505 assert(fdP != NULL);
2506 size = FDH_SIZE(fdP);
2508 memset(buf, 0, 1024);
2511 code = FDH_READ(fdP, buf, size);
2512 assert(code == size);
2513 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2514 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2515 dir->name ? dir->name : "??", name, buf);
2516 FDH_REALLYCLOSE(fdP);
2519 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2520 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);
2521 if (vnodeIdToClass(vnodeNumber) == vLarge
2522 && vnodeEssence->name == NULL) {
2524 if ((n = (char *)malloc(strlen(name) + 1)))
2526 vnodeEssence->name = n;
2529 /* The directory entry points to the vnode. Check to see if the
2530 * vnode points back to the directory. If not, then let the
2531 * directory claim it (else it might end up orphaned). Vnodes
2532 * already claimed by another directory are deleted from this
2533 * directory: hardlinks to the same vnode are not allowed
2534 * from different directories.
2536 if (vnodeEssence->parent != dir->vnodeNumber) {
2537 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2538 /* Vnode does not point back to this directory.
2539 * Orphaned dirs cannot claim a file (it may belong to
2540 * another non-orphaned dir).
2543 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);
2545 vnodeEssence->parent = dir->vnodeNumber;
2546 vnodeEssence->changed = 1;
2548 /* Vnode was claimed by another directory */
2551 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 " : ""));
2552 } else if (vnodeNumber == 1) {
2553 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 " : ""));
2555 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 " : ""));
2560 assert(Delete(&dir->dirHandle, name) == 0);
2565 /* This directory claims the vnode */
2566 vnodeEssence->claimed = 1;
2568 vnodeEssence->count--;
2572 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2574 register struct VnodeInfo *vip = &vnodeInfo[class];
2575 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2576 char buf[SIZEOF_LARGEDISKVNODE];
2577 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2579 StreamHandle_t *file;
2584 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2585 fdP = IH_OPEN(vip->handle);
2586 assert(fdP != NULL);
2587 file = FDH_FDOPEN(fdP, "r+");
2588 assert(file != NULL);
2589 size = OS_SIZE(fdP->fd_fd);
2591 vip->nVnodes = (size / vcp->diskSize) - 1;
2592 if (vip->nVnodes > 0) {
2593 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2594 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2595 assert((vip->vnodes = (struct VnodeEssence *)
2596 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2597 if (class == vLarge) {
2598 assert((vip->inodes = (Inode *)
2599 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2608 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2609 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2610 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2611 nVnodes--, vnodeIndex++) {
2612 if (vnode->type != vNull) {
2613 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2614 afs_fsize_t vnodeLength;
2615 vip->nAllocatedVnodes++;
2616 vep->count = vnode->linkCount;
2617 VNDISK_GET_LEN(vnodeLength, vnode);
2618 vep->blockCount = nBlocks(vnodeLength);
2619 vip->volumeBlockCount += vep->blockCount;
2620 vep->parent = vnode->parent;
2621 vep->unique = vnode->uniquifier;
2622 if (*maxu < vnode->uniquifier)
2623 *maxu = vnode->uniquifier;
2624 vep->modeBits = vnode->modeBits;
2625 vep->InodeNumber = VNDISK_GET_INO(vnode);
2626 vep->type = vnode->type;
2627 vep->author = vnode->author;
2628 vep->owner = vnode->owner;
2629 vep->group = vnode->group;
2630 if (vnode->type == vDirectory) {
2631 assert(class == vLarge);
2632 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2641 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2643 struct VnodeEssence *parentvp;
2649 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2650 && GetDirName(vp->parent, parentvp, path)) {
2652 strcat(path, vp->name);
2658 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2659 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2662 IsVnodeOrphaned(VnodeId vnode)
2664 struct VnodeEssence *vep;
2667 return (1); /* Vnode zero does not exist */
2669 return (0); /* The root dir vnode is always claimed */
2670 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2671 if (!vep || !vep->claimed)
2672 return (1); /* Vnode is not claimed - it is orphaned */
2674 return (IsVnodeOrphaned(vep->parent));
2678 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2679 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2682 static struct DirSummary dir;
2683 static struct DirHandle dirHandle;
2684 struct VnodeEssence *parent;
2685 static char path[MAXPATHLEN];
2688 if (dirVnodeInfo->vnodes[i].salvaged)
2689 return; /* already salvaged */
2692 dirVnodeInfo->vnodes[i].salvaged = 1;
2694 if (dirVnodeInfo->inodes[i] == 0)
2695 return; /* Not allocated to a directory */
2697 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2698 if (dirVnodeInfo->vnodes[i].parent) {
2699 Log("Bad parent, vnode 1; %s...\n",
2700 (Testing ? "skipping" : "salvaging"));
2701 dirVnodeInfo->vnodes[i].parent = 0;
2702 dirVnodeInfo->vnodes[i].changed = 1;
2705 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2706 if (parent && parent->salvaged == 0)
2707 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2708 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2709 rootdir, rootdirfound);
2712 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2713 dir.unique = dirVnodeInfo->vnodes[i].unique;
2716 dir.parent = dirVnodeInfo->vnodes[i].parent;
2717 dir.haveDot = dir.haveDotDot = 0;
2718 dir.ds_linkH = alinkH;
2719 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2720 dirVnodeInfo->inodes[i]);
2722 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2725 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2726 (Testing ? "skipping" : "salvaging"));
2729 CopyAndSalvage(&dir);
2733 dirHandle = dir.dirHandle;
2736 GetDirName(bitNumberToVnodeNumber(i, vLarge),
2737 &dirVnodeInfo->vnodes[i], path);
2740 /* If enumeration failed for random reasons, we will probably delete
2741 * too much stuff, so we guard against this instead.
2743 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2746 /* Delete the old directory if it was copied in order to salvage.
2747 * CopyOnWrite has written the new inode # to the disk, but we still
2748 * have the old one in our local structure here. Thus, we idec the
2752 if (dir.copied && !Testing) {
2753 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2755 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2758 /* Remember rootdir DirSummary _after_ it has been judged */
2759 if (dir.vnodeNumber == 1 && dir.unique == 1) {
2760 memcpy(rootdir, &dir, sizeof(struct DirSummary));
2768 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2770 /* This routine, for now, will only be called for read-write volumes */
2772 int BlocksInVolume = 0, FilesInVolume = 0;
2773 register VnodeClass class;
2774 struct DirSummary rootdir, oldrootdir;
2775 struct VnodeInfo *dirVnodeInfo;
2776 struct VnodeDiskObject vnode;
2777 VolumeDiskData volHeader;
2779 int orphaned, rootdirfound = 0;
2780 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
2781 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
2782 struct VnodeEssence *vep;
2787 VnodeId LFVnode, ThisVnode;
2788 Unique LFUnique, ThisUnique;
2791 vid = rwIsp->volSummary->header.id;
2792 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2793 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2794 assert(nBytes == sizeof(volHeader));
2795 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2796 assert(volHeader.destroyMe != DESTROY_ME);
2797 /* (should not have gotten this far with DESTROY_ME flag still set!) */
2799 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2801 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2804 dirVnodeInfo = &vnodeInfo[vLarge];
2805 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2806 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2814 /* Parse each vnode looking for orphaned vnodes and
2815 * connect them to the tree as orphaned (if requested).
2817 oldrootdir = rootdir;
2818 for (class = 0; class < nVNODECLASSES; class++) {
2819 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2820 vep = &(vnodeInfo[class].vnodes[v]);
2821 ThisVnode = bitNumberToVnodeNumber(v, class);
2822 ThisUnique = vep->unique;
2824 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2825 continue; /* Ignore unused, claimed, and root vnodes */
2827 /* This vnode is orphaned. If it is a directory vnode, then the '..'
2828 * entry in this vnode had incremented the parent link count (In
2829 * JudgeEntry()). We need to go to the parent and decrement that
2830 * link count. But if the parent's unique is zero, then the parent
2831 * link count was not incremented in JudgeEntry().
2833 if (class == vLarge) { /* directory vnode */
2834 pv = vnodeIdToBitNumber(vep->parent);
2835 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2836 vnodeInfo[vLarge].vnodes[pv].count++;
2840 continue; /* If no rootdir, can't attach orphaned files */
2842 /* Here we attach orphaned files and directories into the
2843 * root directory, LVVnode, making sure link counts stay correct.
2845 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2846 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
2847 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
2849 /* Update this orphaned vnode's info. Its parent info and
2850 * link count (do for orphaned directories and files).
2852 vep->parent = LFVnode; /* Parent is the root dir */
2853 vep->unique = LFUnique;
2856 vep->count--; /* Inc link count (root dir will pt to it) */
2858 /* If this orphaned vnode is a directory, change '..'.
2859 * The name of the orphaned dir/file is unknown, so we
2860 * build a unique name. No need to CopyOnWrite the directory
2861 * since it is not connected to tree in BK or RO volume and
2862 * won't be visible there.
2864 if (class == vLarge) {
2868 /* Remove and recreate the ".." entry in this orphaned directory */
2869 SetSalvageDirHandle(&dh, vid, fileSysDevice,
2870 vnodeInfo[class].inodes[v]);
2872 pa.Unique = LFUnique;
2873 assert(Delete(&dh, "..") == 0);
2874 assert(Create(&dh, "..", &pa) == 0);
2876 /* The original parent's link count was decremented above.
2877 * Here we increment the new parent's link count.
2879 pv = vnodeIdToBitNumber(LFVnode);
2880 vnodeInfo[vLarge].vnodes[pv].count--;
2884 /* Go to the root dir and add this entry. The link count of the
2885 * root dir was incremented when ".." was created. Try 10 times.
2887 for (j = 0; j < 10; j++) {
2888 pa.Vnode = ThisVnode;
2889 pa.Unique = ThisUnique;
2891 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2893 vLarge) ? "__ORPHANDIR__" :
2894 "__ORPHANFILE__"), ThisVnode,
2897 CopyOnWrite(&rootdir);
2898 code = Create(&rootdir.dirHandle, npath, &pa);
2902 ThisUnique += 50; /* Try creating a different file */
2905 Log("Attaching orphaned %s to volume's root dir as %s\n",
2906 ((class == vLarge) ? "directory" : "file"), npath);
2908 } /* for each vnode in the class */
2909 } /* for each class of vnode */
2911 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2913 if (!oldrootdir.copied && rootdir.copied) {
2915 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2918 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
2921 DFlush(); /* Flush the changes */
2922 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
2923 Log("Cannot attach orphaned files and directories: Root directory not found\n");
2924 orphans = ORPH_IGNORE;
2927 /* Write out all changed vnodes. Orphaned files and directories
2928 * will get removed here also (if requested).
2930 for (class = 0; class < nVNODECLASSES; class++) {
2931 int nVnodes = vnodeInfo[class].nVnodes;
2932 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2933 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
2934 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
2935 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
2936 for (i = 0; i < nVnodes; i++) {
2937 register struct VnodeEssence *vnp = &vnodes[i];
2938 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
2940 /* If the vnode is good but is unclaimed (not listed in
2941 * any directory entries), then it is orphaned.
2944 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
2945 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
2949 if (vnp->changed || vnp->count) {
2953 IH_IREAD(vnodeInfo[class].handle,
2954 vnodeIndexOffset(vcp, vnodeNumber),
2955 (char *)&vnode, sizeof(vnode));
2956 assert(nBytes == sizeof(vnode));
2958 vnode.parent = vnp->parent;
2959 oldCount = vnode.linkCount;
2960 vnode.linkCount = vnode.linkCount - vnp->count;
2963 orphaned = IsVnodeOrphaned(vnodeNumber);
2965 if (!vnp->todelete) {
2966 /* Orphans should have already been attached (if requested) */
2967 assert(orphans != ORPH_ATTACH);
2968 oblocks += vnp->blockCount;
2971 if (((orphans == ORPH_REMOVE) || vnp->todelete)
2973 BlocksInVolume -= vnp->blockCount;
2975 if (VNDISK_GET_INO(&vnode)) {
2977 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
2980 memset(&vnode, 0, sizeof(vnode));
2982 } else if (vnp->count) {
2984 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
2988 vnode.dataVersion++;
2991 IH_IWRITE(vnodeInfo[class].handle,
2992 vnodeIndexOffset(vcp, vnodeNumber),
2993 (char *)&vnode, sizeof(vnode));
2994 assert(nBytes == sizeof(vnode));
3000 if (!Showmode && ofiles) {
3001 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3003 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3007 for (class = 0; class < nVNODECLASSES; class++) {
3008 register struct VnodeInfo *vip = &vnodeInfo[class];
3009 for (i = 0; i < vip->nVnodes; i++)
3010 if (vip->vnodes[i].name)
3011 free(vip->vnodes[i].name);
3018 /* Set correct resource utilization statistics */
3019 volHeader.filecount = FilesInVolume;
3020 volHeader.diskused = BlocksInVolume;
3022 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3023 if (volHeader.uniquifier < (maxunique + 1)) {
3025 Log("Volume uniquifier is too low; fixed\n");
3026 /* Plus 2,000 in case there are workstations out there with
3027 * cached vnodes that have since been deleted
3029 volHeader.uniquifier = (maxunique + 1 + 2000);
3032 /* Turn off the inUse bit; the volume's been salvaged! */
3033 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3034 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3035 volHeader.inService = 1; /* allow service again */
3036 volHeader.needsCallback = (VolumeChanged != 0);
3037 volHeader.dontSalvage = DONT_SALVAGE;
3040 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3041 assert(nBytes == sizeof(volHeader));
3044 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3045 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3046 FilesInVolume, BlocksInVolume);
3048 IH_RELEASE(vnodeInfo[vSmall].handle);
3049 IH_RELEASE(vnodeInfo[vLarge].handle);
3055 ClearROInUseBit(struct VolumeSummary *summary)
3057 IHandle_t *h = summary->volumeInfoHandle;
3060 VolumeDiskData volHeader;
3062 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3063 assert(nBytes == sizeof(volHeader));
3064 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3065 volHeader.inUse = 0;
3066 volHeader.needsSalvaged = 0;
3067 volHeader.inService = 1;
3068 volHeader.dontSalvage = DONT_SALVAGE;
3070 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3071 assert(nBytes == sizeof(volHeader));
3076 * Possible delete the volume.
3078 * deleteMe - Always do so, only a partial volume.
3081 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3084 if (readOnly(isp) || deleteMe) {
3085 if (isp->volSummary && isp->volSummary->fileName) {
3088 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);
3090 Log("It will be deleted on this server (you may find it elsewhere)\n");
3093 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3095 Log("it will be deleted instead. It should be recloned.\n");
3098 unlink(isp->volSummary->fileName);
3100 } else if (!check) {
3101 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3103 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3109 AskOffline(VolumeId volumeId)
3113 for (i = 0; i < 3; i++) {
3114 code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3116 if (code == SYNC_OK) {
3118 } else if (code == SYNC_DENIED) {
3119 #ifdef DEMAND_ATTACH_ENABLE
3120 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3122 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3124 Abort("Salvage aborted\n");
3125 } else if (code == SYNC_BAD_COMMAND) {
3126 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3128 #ifdef DEMAND_ATTACH_ENABLE
3129 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3131 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3133 Abort("Salvage aborted\n");
3136 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3137 FSYNC_clientFinis();
3141 if (code != SYNC_OK) {
3142 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3143 Abort("Salvage aborted\n");
3148 AskOnline(VolumeId volumeId, char *partition)
3152 for (i = 0; i < 3; i++) {
3153 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3155 if (code == SYNC_OK) {
3157 } else if (code == SYNC_DENIED) {
3158 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3159 } else if (code == SYNC_BAD_COMMAND) {
3160 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3162 #ifdef DEMAND_ATTACH_ENABLE
3163 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3165 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3170 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3171 FSYNC_clientFinis();
3178 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3180 /* Volume parameter is passed in case iopen is upgraded in future to
3181 * require a volume Id to be passed
3184 IHandle_t *srcH, *destH;
3185 FdHandle_t *srcFdP, *destFdP;
3188 IH_INIT(srcH, device, rwvolume, inode1);
3189 srcFdP = IH_OPEN(srcH);
3190 assert(srcFdP != NULL);
3191 IH_INIT(destH, device, rwvolume, inode2);
3192 destFdP = IH_OPEN(destH);
3194 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3195 assert(FDH_WRITE(destFdP, buf, n) == n);
3197 FDH_REALLYCLOSE(srcFdP);
3198 FDH_REALLYCLOSE(destFdP);
3205 PrintInodeList(void)
3207 register struct ViceInodeInfo *ip;
3208 struct ViceInodeInfo *buf;
3209 struct afs_stat status;
3212 assert(afs_fstat(inodeFd, &status) == 0);
3213 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3214 assert(buf != NULL);
3215 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3216 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3217 for (ip = buf; nInodes--; ip++) {
3218 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3219 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3220 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3221 ip->u.param[2], ip->u.param[3]);
3227 PrintInodeSummary(void)
3230 struct InodeSummary *isp;
3232 for (i = 0; i < nVolumesInInodeFile; i++) {
3233 isp = &inodeSummary[i];
3234 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);
3239 PrintVolumeSummary(void)
3242 struct VolumeSummary *vsp;
3244 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3245 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3255 assert(0); /* Fork is never executed in the NT code path */
3270 if (main_thread != pthread_self())
3271 pthread_exit((void *)code);
3284 pid = wait(&status);
3286 if (WCOREDUMP(status))
3287 Log("\"%s\" core dumped!\n", prog);
3288 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3294 TimeStamp(time_t clock, int precision)
3297 static char timestamp[20];
3298 lt = localtime(&clock);
3300 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3302 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3307 CheckLogFile(char * log_path)
3309 char oldSlvgLog[AFSDIR_PATH_MAX];
3311 #ifndef AFS_NT40_ENV
3318 strcpy(oldSlvgLog, log_path);
3319 strcat(oldSlvgLog, ".old");
3321 renamefile(log_path, oldSlvgLog);
3322 logFile = afs_fopen(log_path, "a");
3324 if (!logFile) { /* still nothing, use stdout */
3328 #ifndef AFS_NAMEI_ENV
3329 AFS_DEBUG_IOPS_LOG(logFile);
3334 #ifndef AFS_NT40_ENV
3336 TimeStampLogFile(char * log_path)
3338 char stampSlvgLog[AFSDIR_PATH_MAX];
3343 lt = localtime(&now);
3344 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3345 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3346 log_path, lt->tm_year + 1900,
3347 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3350 /* try to link the logfile to a timestamped filename */
3351 /* if it fails, oh well, nothing we can do */
3352 link(log_path, stampSlvgLog);
3361 #ifndef AFS_NT40_ENV
3363 printf("Can't show log since using syslog.\n");
3372 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3375 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3378 while (fgets(line, sizeof(line), logFile))
3385 Log(const char *format, ...)
3391 va_start(args, format);
3392 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3394 #ifndef AFS_NT40_ENV
3396 syslog(LOG_INFO, "%s", tmp);
3400 gettimeofday(&now, 0);
3401 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3407 Abort(const char *format, ...)
3412 va_start(args, format);
3413 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3415 #ifndef AFS_NT40_ENV
3417 syslog(LOG_INFO, "%s", tmp);
3421 fprintf(logFile, "%s", tmp);
3436 p = (char *)malloc(strlen(s) + 1);
3443 /* Remove the FORCESALVAGE file */
3445 RemoveTheForce(char *path)
3447 if (!Testing && ForceSalvage) {
3448 if (chdir(path) == 0)
3449 unlink("FORCESALVAGE");
3453 #ifndef AFS_AIX32_ENV
3455 * UseTheForceLuke - see if we can use the force
3458 UseTheForceLuke(char *path)