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>
93 #include <sys/param.h>
97 #endif /* ITIMER_REAL */
103 #include <sys/stat.h>
108 #include <WINNT/afsevent.h>
110 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
111 #define WCOREDUMP(x) (x & 0200)
114 #include <afs/afsint.h>
115 #include <afs/assert.h>
116 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
117 #if defined(AFS_VFSINCL_ENV)
118 #include <sys/vnode.h>
120 #include <sys/fs/ufs_inode.h>
122 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
123 #include <ufs/ufs/dinode.h>
124 #include <ufs/ffs/fs.h>
126 #include <ufs/inode.h>
129 #else /* AFS_VFSINCL_ENV */
131 #include <ufs/inode.h>
132 #else /* AFS_OSF_ENV */
133 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
134 #include <sys/inode.h>
137 #endif /* AFS_VFSINCL_ENV */
138 #endif /* AFS_SGI_ENV */
141 #include <sys/lockf.h>
145 #include <checklist.h>
147 #if defined(AFS_SGI_ENV)
152 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
155 #include <sys/mnttab.h>
156 #include <sys/mntent.h>
161 #endif /* AFS_SGI_ENV */
162 #endif /* AFS_HPUX_ENV */
167 #include <afs/osi_inode.h>
170 #include <afs/afsutil.h>
171 #include <afs/fileutil.h>
172 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
180 #include <afs/afssyscalls.h>
184 #include "partition.h"
185 #include "daemon_com.h"
187 #include "salvsync.h"
188 #include "viceinode.h"
190 #include "volinodes.h" /* header magic number, etc. stuff */
191 #include "vol-salvage.h"
196 /*@+fcnmacros +macrofcndecl@*/
199 extern off64_t afs_lseek(int FD, off64_t O, int F);
200 #endif /*S_SPLINT_S */
201 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
202 #define afs_stat stat64
203 #define afs_fstat fstat64
204 #define afs_open open64
205 #define afs_fopen fopen64
206 #else /* !O_LARGEFILE */
208 extern off_t afs_lseek(int FD, off_t O, int F);
209 #endif /*S_SPLINT_S */
210 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
211 #define afs_stat stat
212 #define afs_fstat fstat
213 #define afs_open open
214 #define afs_fopen fopen
215 #endif /* !O_LARGEFILE */
216 /*@=fcnmacros =macrofcndecl@*/
219 extern void *calloc();
221 static char *TimeStamp(time_t clock, int precision);
224 int debug; /* -d flag */
225 extern int Testing; /* -n flag */
226 int ListInodeOption; /* -i flag */
227 int ShowRootFiles; /* -r flag */
228 int RebuildDirs; /* -sal flag */
229 int Parallel = 4; /* -para X flag */
230 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
231 int forceR = 0; /* -b flag */
232 int ShowLog = 0; /* -showlog flag */
233 int ShowSuid = 0; /* -showsuid flag */
234 int ShowMounts = 0; /* -showmounts flag */
235 int orphans = ORPH_IGNORE; /* -orphans option */
239 int useSyslog = 0; /* -syslog flag */
240 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
243 #define MAXPARALLEL 32
245 int OKToZap; /* -o flag */
246 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
247 * in the volume header */
249 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
251 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
253 Device fileSysDevice; /* The device number of the current
254 * partition being salvaged */
258 char *fileSysPath; /* The path of the mounted partition currently
259 * being salvaged, i.e. the directory
260 * containing the volume headers */
262 char *fileSysPathName; /* NT needs this to make name pretty in log. */
263 IHandle_t *VGLinkH; /* Link handle for current volume group. */
264 int VGLinkH_cnt; /* # of references to lnk handle. */
265 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
267 char *fileSysDeviceName; /* The block device where the file system
268 * being salvaged was mounted */
269 char *filesysfulldev;
271 int VolumeChanged; /* Set by any routine which would change the volume in
272 * a way which would require callback is to be broken if the
273 * volume was put back on line by an active file server */
275 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
277 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
278 int inodeFd; /* File descriptor for inode file */
281 struct VnodeInfo vnodeInfo[nVNODECLASSES];
284 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
285 int nVolumes; /* Number of volumes (read-write and read-only)
286 * in volume summary */
292 /* Forward declarations */
293 /*@printflike@*/ void Log(const char *format, ...);
294 /*@printflike@*/ void Abort(const char *format, ...);
295 static int IsVnodeOrphaned(VnodeId vnode);
297 /* Uniquifier stored in the Inode */
302 return (u & 0x3fffff);
304 #if defined(AFS_SGI_EXMAG)
305 return (u & SGI_UNIQMASK);
308 #endif /* AFS_SGI_EXMAG */
313 BadError(register int aerror)
315 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
317 return 0; /* otherwise may be transient, e.g. EMFILE */
322 char *save_args[MAX_ARGS];
324 extern pthread_t main_thread;
325 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
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, int 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, sizeof(version) + sizeof(short)) < 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);
1455 struct ViceInodeInfo *ip;
1456 CreateLinkTable(isp, ino);
1457 fdP = IH_OPEN(VGLinkH);
1458 /* Sync fake 1 link counts to the link table, now that it exists */
1460 for (i = 0; i < nVols; i++) {
1461 ip = allInodes + isp[i].index;
1462 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1464 nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1466 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1474 FDH_REALLYCLOSE(fdP);
1476 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1479 /* Salvage in reverse order--read/write volume last; this way any
1480 * Inodes not referenced by the time we salvage the read/write volume
1481 * can be picked up by the read/write volume */
1482 /* ACTUALLY, that's not done right now--the inodes just vanish */
1483 for (i = nVols - 1; i >= salvageTo; i--) {
1485 struct InodeSummary *lisp = &isp[i];
1486 #ifdef AFS_NAMEI_ENV
1487 /* If only the RO is present on this partition, the link table
1488 * shows up as a RW volume special file. Need to make sure the
1489 * salvager doesn't try to salvage the non-existent RW.
1491 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1492 /* If this only special inode is the link table, continue */
1493 if (inodes->u.special.type == VI_LINKTABLE) {
1500 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1501 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1502 /* Check inodes twice. The second time do things seriously. This
1503 * way the whole RO volume can be deleted, below, if anything goes wrong */
1504 for (check = 1; check >= 0; check--) {
1506 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1508 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1509 if (rw && deleteMe) {
1510 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1511 * volume won't be called */
1517 if (rw && check == 1)
1519 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1520 MaybeZapVolume(lisp, "Vnode index", 0, check);
1526 /* Fix actual inode counts */
1528 Log("totalInodes %d\n",totalInodes);
1529 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1530 static int TraceBadLinkCounts = 0;
1531 #ifdef AFS_NAMEI_ENV
1532 if (VGLinkH->ih_ino == ip->inodeNumber) {
1533 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1534 VGLinkH_p1 = ip->u.param[0];
1535 continue; /* Deal with this last. */
1538 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1539 TraceBadLinkCounts--; /* Limit reports, per volume */
1540 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]);
1542 while (ip->linkCount > 0) {
1543 /* below used to assert, not break */
1545 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1546 Log("idec failed. inode %s errno %d\n",
1547 PrintInode(NULL, ip->inodeNumber), errno);
1553 while (ip->linkCount < 0) {
1554 /* these used to be asserts */
1556 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1557 Log("iinc failed. inode %s errno %d\n",
1558 PrintInode(NULL, ip->inodeNumber), errno);
1565 #ifdef AFS_NAMEI_ENV
1566 while (dec_VGLinkH > 0) {
1567 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1568 Log("idec failed on link table, errno = %d\n", errno);
1572 while (dec_VGLinkH < 0) {
1573 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1574 Log("iinc failed on link table, errno = %d\n", errno);
1581 /* Directory consistency checks on the rw volume */
1583 SalvageVolume(isp, VGLinkH);
1584 IH_RELEASE(VGLinkH);
1586 if (canfork && !debug) {
1593 QuickCheck(register struct InodeSummary *isp, int nVols)
1595 /* Check headers BEFORE forking */
1599 for (i = 0; i < nVols; i++) {
1600 struct VolumeSummary *vs = isp[i].volSummary;
1601 VolumeDiskData volHeader;
1603 /* Don't salvage just because phantom rw volume is there... */
1604 /* (If a read-only volume exists, read/write inodes must also exist) */
1605 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1609 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1610 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1611 == sizeof(volHeader)
1612 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1613 && volHeader.dontSalvage == DONT_SALVAGE
1614 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1615 if (volHeader.inUse == 1) {
1616 volHeader.inUse = 0;
1617 volHeader.inService = 1;
1619 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1620 != sizeof(volHeader)) {
1636 /* SalvageVolumeHeaderFile
1638 * Salvage the top level V*.vol header file. Make sure the special files
1639 * exist and that there are no duplicates.
1641 * Calls SalvageHeader for each possible type of volume special file.
1645 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1646 register struct ViceInodeInfo *inodes, int RW,
1647 int check, int *deleteMe)
1651 register struct ViceInodeInfo *ip;
1652 int allinodesobsolete = 1;
1653 struct VolumeDiskHeader diskHeader;
1657 memset(&tempHeader, 0, sizeof(tempHeader));
1658 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1659 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1660 tempHeader.id = isp->volumeId;
1661 tempHeader.parent = isp->RWvolumeId;
1662 /* Check for duplicates (inodes are sorted by type field) */
1663 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1664 ip = &inodes[isp->index + i];
1665 if (ip->u.special.type == (ip + 1)->u.special.type) {
1667 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1671 for (i = 0; i < isp->nSpecialInodes; i++) {
1672 ip = &inodes[isp->index + i];
1673 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1675 Log("Rubbish header inode\n");
1678 Log("Rubbish header inode; deleted\n");
1679 } else if (!stuff[ip->u.special.type - 1].obsolete) {
1680 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1681 if (!check && ip->u.special.type != VI_LINKTABLE)
1682 ip->linkCount--; /* Keep the inode around */
1683 allinodesobsolete = 0;
1687 if (allinodesobsolete) {
1694 VGLinkH_cnt++; /* one for every header. */
1696 if (!RW && !check && isp->volSummary) {
1697 ClearROInUseBit(isp->volSummary);
1701 for (i = 0; i < MAXINODETYPE; i++) {
1702 if (stuff[i].inodeType == VI_LINKTABLE) {
1703 /* Gross hack: SalvageHeader does a bcmp on the volume header.
1704 * And we may have recreated the link table earlier, so set the
1705 * RW header as well.
1707 if (VALID_INO(VGLinkH->ih_ino)) {
1708 *stuff[i].inode = VGLinkH->ih_ino;
1712 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1716 if (isp->volSummary == NULL) {
1718 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1720 Log("No header file for volume %u\n", isp->volumeId);
1724 Log("No header file for volume %u; %screating %s/%s\n",
1725 isp->volumeId, (Testing ? "it would have been " : ""),
1726 fileSysPathName, name);
1727 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
1728 assert(headerFd != -1);
1729 isp->volSummary = (struct VolumeSummary *)
1730 malloc(sizeof(struct VolumeSummary));
1731 isp->volSummary->fileName = ToString(name);
1734 /* hack: these two fields are obsolete... */
1735 isp->volSummary->header.volumeAcl = 0;
1736 isp->volSummary->header.volumeMountTable = 0;
1739 (&isp->volSummary->header, &tempHeader,
1740 sizeof(struct VolumeHeader))) {
1741 /* We often remove the name before calling us, so we make a fake one up */
1742 if (isp->volSummary->fileName) {
1743 strcpy(name, isp->volSummary->fileName);
1745 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1746 isp->volSummary->fileName = ToString(name);
1749 Log("Header file %s is damaged or no longer valid%s\n", name,
1750 (check ? "" : "; repairing"));
1754 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
1755 assert(headerFd != -1);
1759 memcpy(&isp->volSummary->header, &tempHeader,
1760 sizeof(struct VolumeHeader));
1763 Log("It would have written a new header file for volume %u\n",
1766 VolumeHeaderToDisk(&diskHeader, &tempHeader);
1767 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1768 != sizeof(struct VolumeDiskHeader)) {
1769 Log("Couldn't rewrite volume header file!\n");
1776 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1777 isp->volSummary->header.volumeInfo);
1782 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1786 VolumeDiskData volumeInfo;
1787 struct versionStamp fileHeader;
1796 #ifndef AFS_NAMEI_ENV
1797 if (sp->inodeType == VI_LINKTABLE)
1800 if (*(sp->inode) == 0) {
1802 Log("Missing inode in volume header (%s)\n", sp->description);
1806 Log("Missing inode in volume header (%s); %s\n", sp->description,
1807 (Testing ? "it would have recreated it" : "recreating"));
1810 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1811 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1812 if (!VALID_INO(*(sp->inode)))
1814 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1815 sp->description, errno);
1820 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1821 fdP = IH_OPEN(specH);
1822 if (OKToZap && (fdP == NULL) && BadError(errno)) {
1823 /* bail out early and destroy the volume */
1825 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1832 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1833 sp->description, errno);
1836 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1837 || header.fileHeader.magic != sp->stamp.magic)) {
1839 Log("Part of the header (%s) is corrupted\n", sp->description);
1840 FDH_REALLYCLOSE(fdP);
1844 Log("Part of the header (%s) is corrupted; recreating\n",
1848 if (sp->inodeType == VI_VOLINFO
1849 && header.volumeInfo.destroyMe == DESTROY_ME) {
1852 FDH_REALLYCLOSE(fdP);
1856 if (recreate && !Testing) {
1859 ("Internal error: recreating volume header (%s) in check mode\n",
1861 code = FDH_TRUNC(fdP, 0);
1863 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1864 sp->description, errno);
1866 /* The following code should be moved into vutil.c */
1867 if (sp->inodeType == VI_VOLINFO) {
1869 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1870 header.volumeInfo.stamp = sp->stamp;
1871 header.volumeInfo.id = isp->volumeId;
1872 header.volumeInfo.parentId = isp->RWvolumeId;
1873 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1874 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1875 isp->volumeId, isp->volumeId);
1876 header.volumeInfo.inService = 0;
1877 header.volumeInfo.blessed = 0;
1878 /* The + 1000 is a hack in case there are any files out in venus caches */
1879 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1880 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
1881 header.volumeInfo.needsCallback = 0;
1882 gettimeofday(&tp, 0);
1883 header.volumeInfo.creationDate = tp.tv_sec;
1884 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1886 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1887 sp->description, errno);
1890 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1891 sizeof(header.volumeInfo));
1892 if (code != sizeof(header.volumeInfo)) {
1895 ("Unable to write volume header file (%s) (errno = %d)\n",
1896 sp->description, errno);
1897 Abort("Unable to write entire volume header file (%s)\n",
1901 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1903 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1904 sp->description, errno);
1906 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1907 if (code != sizeof(sp->stamp)) {
1910 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1911 sp->description, errno);
1913 ("Unable to write entire version stamp in volume header file (%s)\n",
1918 FDH_REALLYCLOSE(fdP);
1920 if (sp->inodeType == VI_VOLINFO) {
1921 VolInfo = header.volumeInfo;
1924 if (VolInfo.updateDate) {
1925 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1927 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1928 (Testing ? "it would have been " : ""), update);
1930 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1932 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1933 VolInfo.id, update);
1943 SalvageVnodes(register struct InodeSummary *rwIsp,
1944 register struct InodeSummary *thisIsp,
1945 register struct ViceInodeInfo *inodes, int check)
1947 int ilarge, ismall, ioffset, RW, nInodes;
1948 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
1951 RW = (rwIsp == thisIsp);
1952 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1954 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1955 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1956 if (check && ismall == -1)
1959 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1960 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1961 return (ilarge == 0 && ismall == 0 ? 0 : -1);
1965 SalvageIndex(Inode ino, VnodeClass class, int RW,
1966 register struct ViceInodeInfo *ip, int nInodes,
1967 struct VolumeSummary *volSummary, int check)
1969 VolumeId volumeNumber;
1970 char buf[SIZEOF_LARGEDISKVNODE];
1971 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1973 StreamHandle_t *file;
1974 struct VnodeClassInfo *vcp;
1976 afs_fsize_t vnodeLength;
1977 int vnodeIndex, nVnodes;
1978 afs_ino_str_t stmp1, stmp2;
1982 volumeNumber = volSummary->header.id;
1983 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
1984 fdP = IH_OPEN(handle);
1985 assert(fdP != NULL);
1986 file = FDH_FDOPEN(fdP, "r+");
1987 assert(file != NULL);
1988 vcp = &VnodeClassInfo[class];
1989 size = OS_SIZE(fdP->fd_fd);
1991 nVnodes = (size / vcp->diskSize) - 1;
1993 assert((nVnodes + 1) * vcp->diskSize == size);
1994 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
1998 for (vnodeIndex = 0;
1999 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2000 nVnodes--, vnodeIndex++) {
2001 if (vnode->type != vNull) {
2002 int vnodeChanged = 0;
2003 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2004 /* Log programs that belong to root (potentially suid root);
2005 * don't bother for read-only or backup volumes */
2006 #ifdef notdef /* This is done elsewhere */
2007 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2008 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);
2010 if (VNDISK_GET_INO(vnode) == 0) {
2012 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2013 memset(vnode, 0, vcp->diskSize);
2017 if (vcp->magic != vnode->vnodeMagic) {
2018 /* bad magic #, probably partially created vnode */
2019 Log("Partially allocated vnode %d deleted.\n",
2021 memset(vnode, 0, vcp->diskSize);
2025 /* ****** Should do a bit more salvage here: e.g. make sure
2026 * vnode type matches what it should be given the index */
2027 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2028 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2029 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2030 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2037 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2038 /* The following doesn't work, because the version number
2039 * is not maintained correctly by the file server */
2040 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2041 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2043 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2049 /* For RW volume, look for vnode with matching inode number;
2050 * if no such match, take the first determined by our sort
2052 register struct ViceInodeInfo *lip = ip;
2053 register int lnInodes = nInodes;
2055 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2056 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2065 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2066 /* "Matching" inode */
2070 vu = vnode->uniquifier;
2071 iu = ip->u.vnode.vnodeUniquifier;
2072 vd = vnode->dataVersion;
2073 id = ip->u.vnode.inodeDataVersion;
2075 * Because of the possibility of the uniquifier overflows (> 4M)
2076 * we compare them modulo the low 22-bits; we shouldn't worry
2077 * about mismatching since they shouldn't to many old
2078 * uniquifiers of the same vnode...
2080 if (IUnique(vu) != IUnique(iu)) {
2082 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2085 vnode->uniquifier = iu;
2086 #ifdef AFS_3DISPARES
2087 vnode->dataVersion = (id >= vd ?
2090 1887437 ? vd : id) :
2093 1887437 ? id : vd));
2095 #if defined(AFS_SGI_EXMAG)
2096 vnode->dataVersion = (id >= vd ?
2099 15099494 ? vd : id) :
2102 15099494 ? id : vd));
2104 vnode->dataVersion = (id > vd ? id : vd);
2105 #endif /* AFS_SGI_EXMAG */
2106 #endif /* AFS_3DISPARES */
2109 /* don't bother checking for vd > id any more, since
2110 * partial file transfers always result in this state,
2111 * and you can't do much else anyway (you've already
2112 * found the best data you can) */
2113 #ifdef AFS_3DISPARES
2114 if (!vnodeIsDirectory(vnodeNumber)
2115 && ((vd < id && (id - vd) < 1887437)
2116 || ((vd > id && (vd - id) > 1887437)))) {
2118 #if defined(AFS_SGI_EXMAG)
2119 if (!vnodeIsDirectory(vnodeNumber)
2120 && ((vd < id && (id - vd) < 15099494)
2121 || ((vd > id && (vd - id) > 15099494)))) {
2123 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2124 #endif /* AFS_SGI_EXMAG */
2127 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2128 vnode->dataVersion = id;
2133 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2136 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);
2138 VNDISK_SET_INO(vnode, ip->inodeNumber);
2143 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);
2145 VNDISK_SET_INO(vnode, ip->inodeNumber);
2148 VNDISK_GET_LEN(vnodeLength, vnode);
2149 if (ip->byteCount != vnodeLength) {
2152 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2157 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2158 VNDISK_SET_LEN(vnode, ip->byteCount);
2162 ip->linkCount--; /* Keep the inode around */
2165 } else { /* no matching inode */
2166 if (VNDISK_GET_INO(vnode) != 0
2167 || vnode->type == vDirectory) {
2168 /* No matching inode--get rid of the vnode */
2170 if (VNDISK_GET_INO(vnode)) {
2172 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2176 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2181 if (VNDISK_GET_INO(vnode)) {
2183 time_t serverModifyTime = vnode->serverModifyTime;
2184 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));
2188 time_t serverModifyTime = vnode->serverModifyTime;
2189 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2192 memset(vnode, 0, vcp->diskSize);
2195 /* Should not reach here becuase we checked for
2196 * (inodeNumber == 0) above. And where we zero the vnode,
2197 * we also goto vnodeDone.
2201 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2205 } /* VNDISK_GET_INO(vnode) != 0 */
2207 assert(!(vnodeChanged && check));
2208 if (vnodeChanged && !Testing) {
2210 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2211 (char *)vnode, vcp->diskSize)
2213 VolumeChanged = 1; /* For break call back */
2224 struct VnodeEssence *
2225 CheckVnodeNumber(VnodeId vnodeNumber)
2228 struct VnodeInfo *vip;
2231 class = vnodeIdToClass(vnodeNumber);
2232 vip = &vnodeInfo[class];
2233 offset = vnodeIdToBitNumber(vnodeNumber);
2234 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2238 CopyOnWrite(register struct DirSummary *dir)
2240 /* Copy the directory unconditionally if we are going to change it:
2241 * not just if was cloned.
2243 struct VnodeDiskObject vnode;
2244 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2245 Inode oldinode, newinode;
2248 if (dir->copied || Testing)
2250 DFlush(); /* Well justified paranoia... */
2253 IH_IREAD(vnodeInfo[vLarge].handle,
2254 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2256 assert(code == sizeof(vnode));
2257 oldinode = VNDISK_GET_INO(&vnode);
2258 /* Increment the version number by a whole lot to avoid problems with
2259 * clients that were promised new version numbers--but the file server
2260 * crashed before the versions were written to disk.
2263 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2264 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2266 assert(VALID_INO(newinode));
2267 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2269 VNDISK_SET_INO(&vnode, newinode);
2271 IH_IWRITE(vnodeInfo[vLarge].handle,
2272 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2274 assert(code == sizeof(vnode));
2276 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2277 fileSysDevice, newinode);
2278 /* Don't delete the original inode right away, because the directory is
2279 * still being scanned.
2285 * This function should either successfully create a new dir, or give up
2286 * and leave things the way they were. In particular, if it fails to write
2287 * the new dir properly, it should return w/o changing the reference to the
2291 CopyAndSalvage(register struct DirSummary *dir)
2293 struct VnodeDiskObject vnode;
2294 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2295 Inode oldinode, newinode;
2299 afs_int32 parentUnique = 1;
2300 struct VnodeEssence *vnodeEssence;
2304 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2306 IH_IREAD(vnodeInfo[vLarge].handle,
2307 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2309 assert(lcode == sizeof(vnode));
2310 oldinode = VNDISK_GET_INO(&vnode);
2311 /* Increment the version number by a whole lot to avoid problems with
2312 * clients that were promised new version numbers--but the file server
2313 * crashed before the versions were written to disk.
2316 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2317 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2319 assert(VALID_INO(newinode));
2320 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2322 /* Assign . and .. vnode numbers from dir and vnode.parent.
2323 * The uniquifier for . is in the vnode.
2324 * The uniquifier for .. might be set to a bogus value of 1 and
2325 * the salvager will later clean it up.
2327 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2328 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2331 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2333 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2338 /* didn't really build the new directory properly, let's just give up. */
2339 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2340 Log("Directory salvage returned code %d, continuing.\n", code);
2342 Log("also failed to decrement link count on new inode");
2346 Log("Checking the results of the directory salvage...\n");
2347 if (!DirOK(&newdir)) {
2348 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2349 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2354 VNDISK_SET_INO(&vnode, newinode);
2355 VNDISK_SET_LEN(&vnode, Length(&newdir));
2357 IH_IWRITE(vnodeInfo[vLarge].handle,
2358 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2360 assert(lcode == sizeof(vnode));
2362 nt_sync(fileSysDevice);
2364 sync(); /* this is slow, but hopefully rarely called. We don't have
2365 * an open FD on the file itself to fsync.
2368 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2370 dir->dirHandle = newdir;
2374 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2377 struct VnodeEssence *vnodeEssence;
2378 afs_int32 dirOrphaned, todelete;
2380 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2382 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2383 if (vnodeEssence == NULL) {
2385 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2389 assert(Delete(&dir->dirHandle, name) == 0);
2394 #ifndef AFS_NAMEI_ENV
2395 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2396 * mount inode for the partition. If this inode were deleted, it would crash
2399 if (vnodeEssence->InodeNumber == 0) {
2400 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"));
2403 assert(Delete(&dir->dirHandle, name) == 0);
2410 if (!(vnodeNumber & 1) && !Showmode
2411 && !(vnodeEssence->count || vnodeEssence->unique
2412 || vnodeEssence->modeBits)) {
2413 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2414 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2415 vnodeNumber, unique,
2416 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2421 assert(Delete(&dir->dirHandle, name) == 0);
2427 /* Check if the Uniquifiers match. If not, change the directory entry
2428 * so its unique matches the vnode unique. Delete if the unique is zero
2429 * or if the directory is orphaned.
2431 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2432 if (!vnodeEssence->unique
2433 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2434 /* This is an orphaned directory. Don't delete the . or ..
2435 * entry. Otherwise, it will get created in the next
2436 * salvage and deleted again here. So Just skip it.
2441 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2444 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")));
2448 fid.Vnode = vnodeNumber;
2449 fid.Unique = vnodeEssence->unique;
2451 assert(Delete(&dir->dirHandle, name) == 0);
2453 assert(Create(&dir->dirHandle, name, &fid) == 0);
2456 return; /* no need to continue */
2459 if (strcmp(name, ".") == 0) {
2460 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2463 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2466 assert(Delete(&dir->dirHandle, ".") == 0);
2467 fid.Vnode = dir->vnodeNumber;
2468 fid.Unique = dir->unique;
2469 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2472 vnodeNumber = fid.Vnode; /* Get the new Essence */
2473 unique = fid.Unique;
2474 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2477 } else if (strcmp(name, "..") == 0) {
2480 struct VnodeEssence *dotdot;
2481 pa.Vnode = dir->parent;
2482 dotdot = CheckVnodeNumber(pa.Vnode);
2483 assert(dotdot != NULL); /* XXX Should not be assert */
2484 pa.Unique = dotdot->unique;
2486 pa.Vnode = dir->vnodeNumber;
2487 pa.Unique = dir->unique;
2489 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2491 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2494 assert(Delete(&dir->dirHandle, "..") == 0);
2495 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2498 vnodeNumber = pa.Vnode; /* Get the new Essence */
2500 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2502 dir->haveDotDot = 1;
2503 } else if (strncmp(name, ".__afs", 6) == 0) {
2505 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);
2509 assert(Delete(&dir->dirHandle, name) == 0);
2511 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2512 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2515 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2516 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);
2517 if (ShowMounts && (vnodeEssence->type == vSymlink)
2518 && !(vnodeEssence->modeBits & 0111)) {
2524 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2525 vnodeEssence->InodeNumber);
2527 assert(fdP != NULL);
2528 size = FDH_SIZE(fdP);
2530 memset(buf, 0, 1024);
2533 code = FDH_READ(fdP, buf, size);
2534 assert(code == size);
2535 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2536 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2537 dir->name ? dir->name : "??", name, buf);
2538 FDH_REALLYCLOSE(fdP);
2541 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2542 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);
2543 if (vnodeIdToClass(vnodeNumber) == vLarge
2544 && vnodeEssence->name == NULL) {
2546 if ((n = (char *)malloc(strlen(name) + 1)))
2548 vnodeEssence->name = n;
2551 /* The directory entry points to the vnode. Check to see if the
2552 * vnode points back to the directory. If not, then let the
2553 * directory claim it (else it might end up orphaned). Vnodes
2554 * already claimed by another directory are deleted from this
2555 * directory: hardlinks to the same vnode are not allowed
2556 * from different directories.
2558 if (vnodeEssence->parent != dir->vnodeNumber) {
2559 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2560 /* Vnode does not point back to this directory.
2561 * Orphaned dirs cannot claim a file (it may belong to
2562 * another non-orphaned dir).
2565 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);
2567 vnodeEssence->parent = dir->vnodeNumber;
2568 vnodeEssence->changed = 1;
2570 /* Vnode was claimed by another directory */
2573 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 " : ""));
2574 } else if (vnodeNumber == 1) {
2575 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 " : ""));
2577 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 " : ""));
2582 assert(Delete(&dir->dirHandle, name) == 0);
2587 /* This directory claims the vnode */
2588 vnodeEssence->claimed = 1;
2590 vnodeEssence->count--;
2594 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2596 register struct VnodeInfo *vip = &vnodeInfo[class];
2597 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2598 char buf[SIZEOF_LARGEDISKVNODE];
2599 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2601 StreamHandle_t *file;
2606 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2607 fdP = IH_OPEN(vip->handle);
2608 assert(fdP != NULL);
2609 file = FDH_FDOPEN(fdP, "r+");
2610 assert(file != NULL);
2611 size = OS_SIZE(fdP->fd_fd);
2613 vip->nVnodes = (size / vcp->diskSize) - 1;
2614 if (vip->nVnodes > 0) {
2615 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2616 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2617 assert((vip->vnodes = (struct VnodeEssence *)
2618 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2619 if (class == vLarge) {
2620 assert((vip->inodes = (Inode *)
2621 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2630 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2631 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2632 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2633 nVnodes--, vnodeIndex++) {
2634 if (vnode->type != vNull) {
2635 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2636 afs_fsize_t vnodeLength;
2637 vip->nAllocatedVnodes++;
2638 vep->count = vnode->linkCount;
2639 VNDISK_GET_LEN(vnodeLength, vnode);
2640 vep->blockCount = nBlocks(vnodeLength);
2641 vip->volumeBlockCount += vep->blockCount;
2642 vep->parent = vnode->parent;
2643 vep->unique = vnode->uniquifier;
2644 if (*maxu < vnode->uniquifier)
2645 *maxu = vnode->uniquifier;
2646 vep->modeBits = vnode->modeBits;
2647 vep->InodeNumber = VNDISK_GET_INO(vnode);
2648 vep->type = vnode->type;
2649 vep->author = vnode->author;
2650 vep->owner = vnode->owner;
2651 vep->group = vnode->group;
2652 if (vnode->type == vDirectory) {
2653 assert(class == vLarge);
2654 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2663 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2665 struct VnodeEssence *parentvp;
2671 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2672 && GetDirName(vp->parent, parentvp, path)) {
2674 strcat(path, vp->name);
2680 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2681 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2684 IsVnodeOrphaned(VnodeId vnode)
2686 struct VnodeEssence *vep;
2689 return (1); /* Vnode zero does not exist */
2691 return (0); /* The root dir vnode is always claimed */
2692 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2693 if (!vep || !vep->claimed)
2694 return (1); /* Vnode is not claimed - it is orphaned */
2696 return (IsVnodeOrphaned(vep->parent));
2700 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2701 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2704 static struct DirSummary dir;
2705 static struct DirHandle dirHandle;
2706 struct VnodeEssence *parent;
2707 static char path[MAXPATHLEN];
2710 if (dirVnodeInfo->vnodes[i].salvaged)
2711 return; /* already salvaged */
2714 dirVnodeInfo->vnodes[i].salvaged = 1;
2716 if (dirVnodeInfo->inodes[i] == 0)
2717 return; /* Not allocated to a directory */
2719 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2720 if (dirVnodeInfo->vnodes[i].parent) {
2721 Log("Bad parent, vnode 1; %s...\n",
2722 (Testing ? "skipping" : "salvaging"));
2723 dirVnodeInfo->vnodes[i].parent = 0;
2724 dirVnodeInfo->vnodes[i].changed = 1;
2727 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2728 if (parent && parent->salvaged == 0)
2729 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2730 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2731 rootdir, rootdirfound);
2734 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2735 dir.unique = dirVnodeInfo->vnodes[i].unique;
2738 dir.parent = dirVnodeInfo->vnodes[i].parent;
2739 dir.haveDot = dir.haveDotDot = 0;
2740 dir.ds_linkH = alinkH;
2741 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2742 dirVnodeInfo->inodes[i]);
2744 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2747 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2748 (Testing ? "skipping" : "salvaging"));
2751 CopyAndSalvage(&dir);
2755 dirHandle = dir.dirHandle;
2758 GetDirName(bitNumberToVnodeNumber(i, vLarge),
2759 &dirVnodeInfo->vnodes[i], path);
2762 /* If enumeration failed for random reasons, we will probably delete
2763 * too much stuff, so we guard against this instead.
2765 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2768 /* Delete the old directory if it was copied in order to salvage.
2769 * CopyOnWrite has written the new inode # to the disk, but we still
2770 * have the old one in our local structure here. Thus, we idec the
2774 if (dir.copied && !Testing) {
2775 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2777 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2780 /* Remember rootdir DirSummary _after_ it has been judged */
2781 if (dir.vnodeNumber == 1 && dir.unique == 1) {
2782 memcpy(rootdir, &dir, sizeof(struct DirSummary));
2790 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2792 /* This routine, for now, will only be called for read-write volumes */
2794 int BlocksInVolume = 0, FilesInVolume = 0;
2795 register VnodeClass class;
2796 struct DirSummary rootdir, oldrootdir;
2797 struct VnodeInfo *dirVnodeInfo;
2798 struct VnodeDiskObject vnode;
2799 VolumeDiskData volHeader;
2801 int orphaned, rootdirfound = 0;
2802 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
2803 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
2804 struct VnodeEssence *vep;
2807 afs_sfsize_t nBytes;
2809 VnodeId LFVnode, ThisVnode;
2810 Unique LFUnique, ThisUnique;
2813 vid = rwIsp->volSummary->header.id;
2814 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2815 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2816 assert(nBytes == sizeof(volHeader));
2817 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2818 assert(volHeader.destroyMe != DESTROY_ME);
2819 /* (should not have gotten this far with DESTROY_ME flag still set!) */
2821 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2823 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2826 dirVnodeInfo = &vnodeInfo[vLarge];
2827 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2828 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2836 /* Parse each vnode looking for orphaned vnodes and
2837 * connect them to the tree as orphaned (if requested).
2839 oldrootdir = rootdir;
2840 for (class = 0; class < nVNODECLASSES; class++) {
2841 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2842 vep = &(vnodeInfo[class].vnodes[v]);
2843 ThisVnode = bitNumberToVnodeNumber(v, class);
2844 ThisUnique = vep->unique;
2846 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2847 continue; /* Ignore unused, claimed, and root vnodes */
2849 /* This vnode is orphaned. If it is a directory vnode, then the '..'
2850 * entry in this vnode had incremented the parent link count (In
2851 * JudgeEntry()). We need to go to the parent and decrement that
2852 * link count. But if the parent's unique is zero, then the parent
2853 * link count was not incremented in JudgeEntry().
2855 if (class == vLarge) { /* directory vnode */
2856 pv = vnodeIdToBitNumber(vep->parent);
2857 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2858 vnodeInfo[vLarge].vnodes[pv].count++;
2862 continue; /* If no rootdir, can't attach orphaned files */
2864 /* Here we attach orphaned files and directories into the
2865 * root directory, LVVnode, making sure link counts stay correct.
2867 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2868 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
2869 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
2871 /* Update this orphaned vnode's info. Its parent info and
2872 * link count (do for orphaned directories and files).
2874 vep->parent = LFVnode; /* Parent is the root dir */
2875 vep->unique = LFUnique;
2878 vep->count--; /* Inc link count (root dir will pt to it) */
2880 /* If this orphaned vnode is a directory, change '..'.
2881 * The name of the orphaned dir/file is unknown, so we
2882 * build a unique name. No need to CopyOnWrite the directory
2883 * since it is not connected to tree in BK or RO volume and
2884 * won't be visible there.
2886 if (class == vLarge) {
2890 /* Remove and recreate the ".." entry in this orphaned directory */
2891 SetSalvageDirHandle(&dh, vid, fileSysDevice,
2892 vnodeInfo[class].inodes[v]);
2894 pa.Unique = LFUnique;
2895 assert(Delete(&dh, "..") == 0);
2896 assert(Create(&dh, "..", &pa) == 0);
2898 /* The original parent's link count was decremented above.
2899 * Here we increment the new parent's link count.
2901 pv = vnodeIdToBitNumber(LFVnode);
2902 vnodeInfo[vLarge].vnodes[pv].count--;
2906 /* Go to the root dir and add this entry. The link count of the
2907 * root dir was incremented when ".." was created. Try 10 times.
2909 for (j = 0; j < 10; j++) {
2910 pa.Vnode = ThisVnode;
2911 pa.Unique = ThisUnique;
2913 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2915 vLarge) ? "__ORPHANDIR__" :
2916 "__ORPHANFILE__"), ThisVnode,
2919 CopyOnWrite(&rootdir);
2920 code = Create(&rootdir.dirHandle, npath, &pa);
2924 ThisUnique += 50; /* Try creating a different file */
2927 Log("Attaching orphaned %s to volume's root dir as %s\n",
2928 ((class == vLarge) ? "directory" : "file"), npath);
2930 } /* for each vnode in the class */
2931 } /* for each class of vnode */
2933 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2935 if (!oldrootdir.copied && rootdir.copied) {
2937 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2940 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
2943 DFlush(); /* Flush the changes */
2944 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
2945 Log("Cannot attach orphaned files and directories: Root directory not found\n");
2946 orphans = ORPH_IGNORE;
2949 /* Write out all changed vnodes. Orphaned files and directories
2950 * will get removed here also (if requested).
2952 for (class = 0; class < nVNODECLASSES; class++) {
2953 int nVnodes = vnodeInfo[class].nVnodes;
2954 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2955 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
2956 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
2957 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
2958 for (i = 0; i < nVnodes; i++) {
2959 register struct VnodeEssence *vnp = &vnodes[i];
2960 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
2962 /* If the vnode is good but is unclaimed (not listed in
2963 * any directory entries), then it is orphaned.
2966 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
2967 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
2971 if (vnp->changed || vnp->count) {
2975 IH_IREAD(vnodeInfo[class].handle,
2976 vnodeIndexOffset(vcp, vnodeNumber),
2977 (char *)&vnode, sizeof(vnode));
2978 assert(nBytes == sizeof(vnode));
2980 vnode.parent = vnp->parent;
2981 oldCount = vnode.linkCount;
2982 vnode.linkCount = vnode.linkCount - vnp->count;
2985 orphaned = IsVnodeOrphaned(vnodeNumber);
2987 if (!vnp->todelete) {
2988 /* Orphans should have already been attached (if requested) */
2989 assert(orphans != ORPH_ATTACH);
2990 oblocks += vnp->blockCount;
2993 if (((orphans == ORPH_REMOVE) || vnp->todelete)
2995 BlocksInVolume -= vnp->blockCount;
2997 if (VNDISK_GET_INO(&vnode)) {
2999 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3002 memset(&vnode, 0, sizeof(vnode));
3004 } else if (vnp->count) {
3006 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3010 vnode.dataVersion++;
3013 IH_IWRITE(vnodeInfo[class].handle,
3014 vnodeIndexOffset(vcp, vnodeNumber),
3015 (char *)&vnode, sizeof(vnode));
3016 assert(nBytes == sizeof(vnode));
3022 if (!Showmode && ofiles) {
3023 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3025 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3029 for (class = 0; class < nVNODECLASSES; class++) {
3030 register struct VnodeInfo *vip = &vnodeInfo[class];
3031 for (i = 0; i < vip->nVnodes; i++)
3032 if (vip->vnodes[i].name)
3033 free(vip->vnodes[i].name);
3040 /* Set correct resource utilization statistics */
3041 volHeader.filecount = FilesInVolume;
3042 volHeader.diskused = BlocksInVolume;
3044 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3045 if (volHeader.uniquifier < (maxunique + 1)) {
3047 Log("Volume uniquifier is too low; fixed\n");
3048 /* Plus 2,000 in case there are workstations out there with
3049 * cached vnodes that have since been deleted
3051 volHeader.uniquifier = (maxunique + 1 + 2000);
3054 /* Turn off the inUse bit; the volume's been salvaged! */
3055 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3056 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3057 volHeader.inService = 1; /* allow service again */
3058 volHeader.needsCallback = (VolumeChanged != 0);
3059 volHeader.dontSalvage = DONT_SALVAGE;
3062 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3063 assert(nBytes == sizeof(volHeader));
3066 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3067 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3068 FilesInVolume, BlocksInVolume);
3070 IH_RELEASE(vnodeInfo[vSmall].handle);
3071 IH_RELEASE(vnodeInfo[vLarge].handle);
3077 ClearROInUseBit(struct VolumeSummary *summary)
3079 IHandle_t *h = summary->volumeInfoHandle;
3080 afs_sfsize_t nBytes;
3082 VolumeDiskData volHeader;
3084 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3085 assert(nBytes == sizeof(volHeader));
3086 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3087 volHeader.inUse = 0;
3088 volHeader.needsSalvaged = 0;
3089 volHeader.inService = 1;
3090 volHeader.dontSalvage = DONT_SALVAGE;
3092 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3093 assert(nBytes == sizeof(volHeader));
3098 * Possible delete the volume.
3100 * deleteMe - Always do so, only a partial volume.
3103 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3106 if (readOnly(isp) || deleteMe) {
3107 if (isp->volSummary && isp->volSummary->fileName) {
3110 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);
3112 Log("It will be deleted on this server (you may find it elsewhere)\n");
3115 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3117 Log("it will be deleted instead. It should be recloned.\n");
3120 unlink(isp->volSummary->fileName);
3122 } else if (!check) {
3123 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3125 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3131 AskOffline(VolumeId volumeId)
3135 for (i = 0; i < 3; i++) {
3136 code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3138 if (code == SYNC_OK) {
3140 } else if (code == SYNC_DENIED) {
3141 #ifdef DEMAND_ATTACH_ENABLE
3142 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3144 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3146 Abort("Salvage aborted\n");
3147 } else if (code == SYNC_BAD_COMMAND) {
3148 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3150 #ifdef DEMAND_ATTACH_ENABLE
3151 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3153 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3155 Abort("Salvage aborted\n");
3158 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3159 FSYNC_clientFinis();
3163 if (code != SYNC_OK) {
3164 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3165 Abort("Salvage aborted\n");
3170 AskOnline(VolumeId volumeId, char *partition)
3174 for (i = 0; i < 3; i++) {
3175 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3177 if (code == SYNC_OK) {
3179 } else if (code == SYNC_DENIED) {
3180 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3181 } else if (code == SYNC_BAD_COMMAND) {
3182 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3184 #ifdef DEMAND_ATTACH_ENABLE
3185 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3187 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3192 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3193 FSYNC_clientFinis();
3200 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3202 /* Volume parameter is passed in case iopen is upgraded in future to
3203 * require a volume Id to be passed
3206 IHandle_t *srcH, *destH;
3207 FdHandle_t *srcFdP, *destFdP;
3210 IH_INIT(srcH, device, rwvolume, inode1);
3211 srcFdP = IH_OPEN(srcH);
3212 assert(srcFdP != NULL);
3213 IH_INIT(destH, device, rwvolume, inode2);
3214 destFdP = IH_OPEN(destH);
3216 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3217 assert(FDH_WRITE(destFdP, buf, n) == n);
3219 FDH_REALLYCLOSE(srcFdP);
3220 FDH_REALLYCLOSE(destFdP);
3227 PrintInodeList(void)
3229 register struct ViceInodeInfo *ip;
3230 struct ViceInodeInfo *buf;
3231 struct afs_stat status;
3234 assert(afs_fstat(inodeFd, &status) == 0);
3235 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3236 assert(buf != NULL);
3237 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3238 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3239 for (ip = buf; nInodes--; ip++) {
3240 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3241 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3242 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3243 ip->u.param[2], ip->u.param[3]);
3249 PrintInodeSummary(void)
3252 struct InodeSummary *isp;
3254 for (i = 0; i < nVolumesInInodeFile; i++) {
3255 isp = &inodeSummary[i];
3256 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);
3261 PrintVolumeSummary(void)
3264 struct VolumeSummary *vsp;
3266 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3267 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3277 assert(0); /* Fork is never executed in the NT code path */
3292 if (main_thread != pthread_self())
3293 pthread_exit((void *)code);
3306 pid = wait(&status);
3308 if (WCOREDUMP(status))
3309 Log("\"%s\" core dumped!\n", prog);
3310 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3316 TimeStamp(time_t clock, int precision)
3319 static char timestamp[20];
3320 lt = localtime(&clock);
3322 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3324 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3329 CheckLogFile(char * log_path)
3331 char oldSlvgLog[AFSDIR_PATH_MAX];
3333 #ifndef AFS_NT40_ENV
3340 strcpy(oldSlvgLog, log_path);
3341 strcat(oldSlvgLog, ".old");
3343 renamefile(log_path, oldSlvgLog);
3344 logFile = afs_fopen(log_path, "a");
3346 if (!logFile) { /* still nothing, use stdout */
3350 #ifndef AFS_NAMEI_ENV
3351 AFS_DEBUG_IOPS_LOG(logFile);
3356 #ifndef AFS_NT40_ENV
3358 TimeStampLogFile(char * log_path)
3360 char stampSlvgLog[AFSDIR_PATH_MAX];
3365 lt = localtime(&now);
3366 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3367 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3368 log_path, lt->tm_year + 1900,
3369 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3372 /* try to link the logfile to a timestamped filename */
3373 /* if it fails, oh well, nothing we can do */
3374 link(log_path, stampSlvgLog);
3383 #ifndef AFS_NT40_ENV
3385 printf("Can't show log since using syslog.\n");
3394 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3397 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3400 while (fgets(line, sizeof(line), logFile))
3407 Log(const char *format, ...)
3413 va_start(args, format);
3414 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3416 #ifndef AFS_NT40_ENV
3418 syslog(LOG_INFO, "%s", tmp);
3422 gettimeofday(&now, 0);
3423 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3429 Abort(const char *format, ...)
3434 va_start(args, format);
3435 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3437 #ifndef AFS_NT40_ENV
3439 syslog(LOG_INFO, "%s", tmp);
3443 fprintf(logFile, "%s", tmp);
3458 p = (char *)malloc(strlen(s) + 1);
3465 /* Remove the FORCESALVAGE file */
3467 RemoveTheForce(char *path)
3469 if (!Testing && ForceSalvage) {
3470 if (chdir(path) == 0)
3471 unlink("FORCESALVAGE");
3475 #ifndef AFS_AIX32_ENV
3477 * UseTheForceLuke - see if we can use the force
3480 UseTheForceLuke(char *path)
3482 struct afs_stat force;
3484 assert(chdir(path) != -1);
3486 return (afs_stat("FORCESALVAGE", &force) == 0);
3490 * UseTheForceLuke - see if we can use the force
3493 * The VRMIX fsck will not muck with the filesystem it is supposedly
3494 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
3495 * muck directly with the root inode, which is within the normal
3497 * ListViceInodes() has a side effect of setting ForceSalvage if
3498 * it detects a need, based on root inode examination.
3501 UseTheForceLuke(char *path)
3504 return 0; /* sorry OB1 */
3509 /* NT support routines */
3511 static char execpathname[MAX_PATH];
3513 nt_SalvagePartition(char *partName, int jobn)
3518 if (!*execpathname) {
3519 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3520 if (!n || n == 1023)
3523 job.cj_magic = SALVAGER_MAGIC;
3524 job.cj_number = jobn;
3525 (void)strcpy(job.cj_part, partName);
3526 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3531 nt_SetupPartitionSalvage(void *datap, int len)
3533 childJob_t *jobp = (childJob_t *) datap;
3534 char logname[AFSDIR_PATH_MAX];
3536 if (len != sizeof(childJob_t))
3538 if (jobp->cj_magic != SALVAGER_MAGIC)
3543 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3545 logFile = afs_fopen(logname, "w");
3553 #endif /* AFS_NT40_ENV */