2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #include <afsconfig.h>
87 #include <afs/param.h>
91 #include <sys/param.h>
95 #endif /* ITIMER_REAL */
101 #include <sys/stat.h>
106 #include <WINNT/afsevent.h>
108 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
109 #define WCOREDUMP(x) (x & 0200)
112 #include <afs/afsint.h>
113 #include <afs/assert.h>
114 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
115 #if defined(AFS_VFSINCL_ENV)
116 #include <sys/vnode.h>
118 #include <sys/fs/ufs_inode.h>
120 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
121 #include <ufs/ufs/dinode.h>
122 #include <ufs/ffs/fs.h>
124 #include <ufs/inode.h>
127 #else /* AFS_VFSINCL_ENV */
129 #include <ufs/inode.h>
130 #else /* AFS_OSF_ENV */
131 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
132 #include <sys/inode.h>
135 #endif /* AFS_VFSINCL_ENV */
136 #endif /* AFS_SGI_ENV */
139 #include <sys/lockf.h>
143 #include <checklist.h>
145 #if defined(AFS_SGI_ENV)
150 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
153 #include <sys/mnttab.h>
154 #include <sys/mntent.h>
159 #endif /* AFS_SGI_ENV */
160 #endif /* AFS_HPUX_ENV */
165 #include <afs/osi_inode.h>
168 #include <afs/afsutil.h>
169 #include <afs/fileutil.h>
170 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
178 #include <afs/afssyscalls.h>
182 #include "partition.h"
183 #include "daemon_com.h"
185 #include "salvsync.h"
186 #include "viceinode.h"
188 #include "volinodes.h" /* header magic number, etc. stuff */
189 #include "vol-salvage.h"
194 /*@+fcnmacros +macrofcndecl@*/
197 extern off64_t afs_lseek(int FD, off64_t O, int F);
198 #endif /*S_SPLINT_S */
199 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
200 #define afs_stat stat64
201 #define afs_fstat fstat64
202 #define afs_open open64
203 #define afs_fopen fopen64
204 #else /* !O_LARGEFILE */
206 extern off_t afs_lseek(int FD, off_t O, int F);
207 #endif /*S_SPLINT_S */
208 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
209 #define afs_stat stat
210 #define afs_fstat fstat
211 #define afs_open open
212 #define afs_fopen fopen
213 #endif /* !O_LARGEFILE */
214 /*@=fcnmacros =macrofcndecl@*/
217 extern void *calloc();
219 static char *TimeStamp(time_t clock, int precision);
222 int debug; /* -d flag */
223 extern int Testing; /* -n flag */
224 int ListInodeOption; /* -i flag */
225 int ShowRootFiles; /* -r flag */
226 int RebuildDirs; /* -sal flag */
227 int Parallel = 4; /* -para X flag */
228 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
229 int forceR = 0; /* -b flag */
230 int ShowLog = 0; /* -showlog flag */
231 int ShowSuid = 0; /* -showsuid flag */
232 int ShowMounts = 0; /* -showmounts flag */
233 int orphans = ORPH_IGNORE; /* -orphans option */
238 int useSyslog = 0; /* -syslog flag */
239 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
248 #define MAXPARALLEL 32
250 int OKToZap; /* -o flag */
251 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
252 * in the volume header */
254 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
256 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
258 Device fileSysDevice; /* The device number of the current
259 * partition being salvaged */
263 char *fileSysPath; /* The path of the mounted partition currently
264 * being salvaged, i.e. the directory
265 * containing the volume headers */
267 char *fileSysPathName; /* NT needs this to make name pretty in log. */
268 IHandle_t *VGLinkH; /* Link handle for current volume group. */
269 int VGLinkH_cnt; /* # of references to lnk handle. */
270 struct DiskPartition64 *fileSysPartition; /* Partition being salvaged */
272 char *fileSysDeviceName; /* The block device where the file system
273 * being salvaged was mounted */
274 char *filesysfulldev;
276 int VolumeChanged; /* Set by any routine which would change the volume in
277 * a way which would require callback is to be broken if the
278 * volume was put back on line by an active file server */
280 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
282 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
283 int inodeFd; /* File descriptor for inode file */
286 struct VnodeInfo vnodeInfo[nVNODECLASSES];
289 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
290 int nVolumes; /* Number of volumes (read-write and read-only)
291 * in volume summary */
297 /* Forward declarations */
298 /*@printflike@*/ void Log(const char *format, ...);
299 /*@printflike@*/ void Abort(const char *format, ...);
300 static int IsVnodeOrphaned(VnodeId vnode);
302 /* Uniquifier stored in the Inode */
307 return (u & 0x3fffff);
309 #if defined(AFS_SGI_EXMAG)
310 return (u & SGI_UNIQMASK);
313 #endif /* AFS_SGI_EXMAG */
318 BadError(register int aerror)
320 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
322 return 0; /* otherwise may be transient, e.g. EMFILE */
327 char *save_args[MAX_ARGS];
329 extern pthread_t main_thread;
330 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
333 /* Get the salvage lock if not already held. Hold until process exits. */
335 ObtainSalvageLock(void)
341 (FD_t)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
342 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
343 if (salvageLock == INVALID_FD) {
345 "salvager: There appears to be another salvager running! Aborted.\n");
350 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
351 if (salvageLock < 0) {
353 "salvager: can't open salvage lock file %s, aborting\n",
354 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
357 #ifdef AFS_DARWIN_ENV
358 if (flock(salvageLock, LOCK_EX) == -1) {
360 if (lockf(salvageLock, F_LOCK, 0) == -1) {
363 "salvager: There appears to be another salvager running! Aborted.\n");
370 #ifdef AFS_SGI_XFS_IOPS_ENV
371 /* Check if the given partition is mounted. For XFS, the root inode is not a
372 * constant. So we check the hard way.
375 IsPartitionMounted(char *part)
378 struct mntent *mntent;
380 assert(mntfp = setmntent(MOUNTED, "r"));
381 while (mntent = getmntent(mntfp)) {
382 if (!strcmp(part, mntent->mnt_dir))
387 return mntent ? 1 : 1;
390 /* Check if the given inode is the root of the filesystem. */
391 #ifndef AFS_SGI_XFS_IOPS_ENV
393 IsRootInode(struct afs_stat *status)
396 * The root inode is not a fixed value in XFS partitions. So we need to
397 * see if the partition is in the list of mounted partitions. This only
398 * affects the SalvageFileSys path, so we check there.
400 return (status->st_ino == ROOTINODE);
405 #ifndef AFS_NAMEI_ENV
406 /* We don't want to salvage big files filesystems, since we can't put volumes on
410 CheckIfBigFilesFS(char *mountPoint, char *devName)
412 struct superblock fs;
415 if (strncmp(devName, "/dev/", 5)) {
416 (void)sprintf(name, "/dev/%s", devName);
418 (void)strcpy(name, devName);
421 if (ReadSuper(&fs, name) < 0) {
422 Log("Unable to read superblock. Not salvaging partition %s.\n",
426 if (IsBigFilesFileSystem(&fs)) {
427 Log("Partition %s is a big files filesystem, not salvaging.\n",
437 #define HDSTR "\\Device\\Harddisk"
438 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
440 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
445 static int dowarn = 1;
447 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
449 if (strncmp(res, HDSTR, HDLEN)) {
452 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
453 res, HDSTR, p1->devName);
457 d1 = atoi(&res[HDLEN]);
459 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
461 if (strncmp(res, HDSTR, HDLEN)) {
464 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
465 res, HDSTR, p2->devName);
469 d2 = atoi(&res[HDLEN]);
474 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
477 /* This assumes that two partitions with the same device number divided by
478 * PartsPerDisk are on the same disk.
481 SalvageFileSysParallel(struct DiskPartition64 *partP)
484 struct DiskPartition64 *partP;
485 int pid; /* Pid for this job */
486 int jobnumb; /* Log file job number */
487 struct job *nextjob; /* Next partition on disk to salvage */
489 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
490 struct job *thisjob = 0;
491 static int numjobs = 0;
492 static int jobcount = 0;
498 char logFileName[256];
502 /* We have a partition to salvage. Copy it into thisjob */
503 thisjob = (struct job *)malloc(sizeof(struct job));
505 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
508 memset(thisjob, 0, sizeof(struct job));
509 thisjob->partP = partP;
510 thisjob->jobnumb = jobcount;
512 } else if (jobcount == 0) {
513 /* We are asking to wait for all jobs (partp == 0), yet we never
516 Log("No file system partitions named %s* found; not salvaged\n",
517 VICE_PARTITION_PREFIX);
521 if (debug || Parallel == 1) {
523 SalvageFileSys(thisjob->partP, 0);
530 /* Check to see if thisjob is for a disk that we are already
531 * salvaging. If it is, link it in as the next job to do. The
532 * jobs array has 1 entry per disk being salvages. numjobs is
533 * the total number of disks currently being salvaged. In
534 * order to keep thejobs array compact, when a disk is
535 * completed, the hightest element in the jobs array is moved
536 * down to now open slot.
538 for (j = 0; j < numjobs; j++) {
539 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
540 /* On same disk, add it to this list and return */
541 thisjob->nextjob = jobs[j]->nextjob;
542 jobs[j]->nextjob = thisjob;
549 /* Loop until we start thisjob or until all existing jobs are finished */
550 while (thisjob || (!partP && (numjobs > 0))) {
551 startjob = -1; /* No new job to start */
553 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
554 /* Either the max jobs are running or we have to wait for all
555 * the jobs to finish. In either case, we wait for at least one
556 * job to finish. When it's done, clean up after it.
558 pid = wait(&wstatus);
560 for (j = 0; j < numjobs; j++) { /* Find which job it is */
561 if (pid == jobs[j]->pid)
565 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
566 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
569 numjobs--; /* job no longer running */
570 oldjob = jobs[j]; /* remember */
571 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
572 free(oldjob); /* free the old job */
574 /* If there is another partition on the disk to salvage, then
575 * say we will start it (startjob). If not, then put thisjob there
576 * and say we will start it.
578 if (jobs[j]) { /* Another partitions to salvage */
579 startjob = j; /* Will start it */
580 } else { /* There is not another partition to salvage */
582 jobs[j] = thisjob; /* Add thisjob */
584 startjob = j; /* Will start it */
586 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
587 startjob = -1; /* Don't start it - already running */
591 /* We don't have to wait for a job to complete */
593 jobs[numjobs] = thisjob; /* Add this job */
595 startjob = numjobs; /* Will start it */
599 /* Start up a new salvage job on a partition in job slot "startjob" */
600 if (startjob != -1) {
602 Log("Starting salvage of file system partition %s\n",
603 jobs[startjob]->partP->name);
605 /* For NT, we not only fork, but re-exec the salvager. Pass in the
606 * commands and pass the child job number via the data path.
609 nt_SalvagePartition(jobs[startjob]->partP->name,
610 jobs[startjob]->jobnumb);
611 jobs[startjob]->pid = pid;
616 jobs[startjob]->pid = pid;
622 for (fd = 0; fd < 16; fd++)
629 openlog("salvager", LOG_PID, useSyslogFacility);
633 (void)afs_snprintf(logFileName, sizeof logFileName,
635 AFSDIR_SERVER_SLVGLOG_FILEPATH,
636 jobs[startjob]->jobnumb);
637 logFile = afs_fopen(logFileName, "w");
642 SalvageFileSys1(jobs[startjob]->partP, 0);
647 } /* while ( thisjob || (!partP && numjobs > 0) ) */
649 /* If waited for all jobs to complete, now collect log files and return */
651 if (!useSyslog) /* if syslogging - no need to collect */
654 for (i = 0; i < jobcount; i++) {
655 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
656 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
657 if ((passLog = afs_fopen(logFileName, "r"))) {
658 while (fgets(buf, sizeof(buf), passLog)) {
663 (void)unlink(logFileName);
672 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
674 if (!canfork || debug || Fork() == 0) {
675 SalvageFileSys1(partP, singleVolumeNumber);
676 if (canfork && !debug) {
681 Wait("SalvageFileSys");
685 get_DevName(char *pbuffer, char *wpath)
687 char pbuf[128], *ptr;
688 strcpy(pbuf, pbuffer);
689 ptr = (char *)strrchr(pbuf, '/');
695 ptr = (char *)strrchr(pbuffer, '/');
697 strcpy(pbuffer, ptr + 1);
704 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
707 char inodeListPath[256];
708 static char tmpDevName[100];
709 static char wpath[100];
710 struct VolumeSummary *vsp, *esp;
713 fileSysPartition = partP;
714 fileSysDevice = fileSysPartition->device;
715 fileSysPathName = VPartitionPath(fileSysPartition);
718 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
719 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
720 name = partP->devName;
722 fileSysPath = fileSysPathName;
723 strcpy(tmpDevName, partP->devName);
724 name = get_DevName(tmpDevName, wpath);
725 fileSysDeviceName = name;
726 filesysfulldev = wpath;
729 VLockPartition(partP->name);
730 if (singleVolumeNumber || ForceSalvage)
733 ForceSalvage = UseTheForceLuke(fileSysPath);
735 if (singleVolumeNumber) {
736 /* salvageserver already setup fssync conn for us */
737 if ((programType != salvageServer) && !VConnectFS()) {
738 Abort("Couldn't connect to file server\n");
740 AskOffline(singleVolumeNumber, partP->name);
743 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
744 partP->name, name, (Testing ? "(READONLY mode)" : ""));
746 Log("***Forced salvage of all volumes on this partition***\n");
751 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
758 assert((dirp = opendir(fileSysPath)) != NULL);
759 while ((dp = readdir(dirp))) {
760 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
761 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
763 Log("Removing old salvager temp files %s\n", dp->d_name);
764 strcpy(npath, fileSysPath);
766 strcat(npath, dp->d_name);
772 tdir = (tmpdir ? tmpdir : fileSysPath);
774 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
775 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
777 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
780 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
781 unlink(inodeListPath);
785 /* Using nt_unlink here since we're really using the delete on close
786 * semantics of unlink. In most places in the salvager, we really do
787 * mean to unlink the file at that point. Those places have been
788 * modified to actually do that so that the NT crt can be used there.
791 _open_osfhandle((intptr_t)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
792 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
794 inodeFd = afs_open(inodeListPath, O_RDONLY);
795 unlink(inodeListPath);
798 Abort("Temporary file %s is missing...\n", inodeListPath);
799 if (ListInodeOption) {
803 /* enumerate volumes in the partition.
804 * figure out sets of read-only + rw volumes.
805 * salvage each set, read-only volumes first, then read-write.
806 * Fix up inodes on last volume in set (whether it is read-write
809 GetVolumeSummary(singleVolumeNumber);
811 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
812 i < nVolumesInInodeFile; i = j) {
813 VolumeId rwvid = inodeSummary[i].RWvolumeId;
815 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
817 VolumeId vid = inodeSummary[j].volumeId;
818 struct VolumeSummary *tsp;
819 /* Scan volume list (from partition root directory) looking for the
820 * current rw volume number in the volume list from the inode scan.
821 * If there is one here that is not in the inode volume list,
823 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
825 DeleteExtraVolumeHeaderFile(vsp);
827 /* Now match up the volume summary info from the root directory with the
828 * entry in the volume list obtained from scanning inodes */
829 inodeSummary[j].volSummary = NULL;
830 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
831 if (tsp->header.id == vid) {
832 inodeSummary[j].volSummary = tsp;
838 /* Salvage the group of volumes (several read-only + 1 read/write)
839 * starting with the current read-only volume we're looking at.
841 SalvageVolumeGroup(&inodeSummary[i], j - i);
844 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
845 for (; vsp < esp; vsp++) {
847 DeleteExtraVolumeHeaderFile(vsp);
850 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
851 RemoveTheForce(fileSysPath);
853 if (!Testing && singleVolumeNumber) {
854 AskOnline(singleVolumeNumber, fileSysPartition->name);
856 /* Step through the volumeSummary list and set all volumes on-line.
857 * The volumes were taken off-line in GetVolumeSummary.
859 for (j = 0; j < nVolumes; j++) {
860 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
864 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
865 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
868 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
872 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
875 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
877 unlink(vsp->fileName);
882 CompareInodes(const void *_p1, const void *_p2)
884 register const struct ViceInodeInfo *p1 = _p1;
885 register const struct ViceInodeInfo *p2 = _p2;
886 if (p1->u.vnode.vnodeNumber == INODESPECIAL
887 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
888 VolumeId p1rwid, p2rwid;
890 (p1->u.vnode.vnodeNumber ==
891 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
893 (p2->u.vnode.vnodeNumber ==
894 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
899 if (p1->u.vnode.vnodeNumber == INODESPECIAL
900 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
901 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
902 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
903 if (p1->u.vnode.volumeId == p1rwid)
905 if (p2->u.vnode.volumeId == p2rwid)
907 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
909 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
910 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
911 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
913 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
915 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
917 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
919 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
921 /* The following tests are reversed, so that the most desirable
922 * of several similar inodes comes first */
923 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
925 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
926 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
930 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
931 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
936 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
938 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
939 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
943 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
944 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
949 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
951 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
952 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
956 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
957 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
962 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
964 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
965 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
969 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
970 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
979 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
980 register struct InodeSummary *summary)
982 VolumeId volume = ip->u.vnode.volumeId;
983 VolumeId rwvolume = volume;
984 register int n, nSpecial;
985 register Unique maxunique;
988 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
990 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
992 rwvolume = ip->u.special.parentId;
993 /* This isn't quite right, as there could (in error) be different
994 * parent inodes in different special vnodes */
996 if (maxunique < ip->u.vnode.vnodeUniquifier)
997 maxunique = ip->u.vnode.vnodeUniquifier;
1001 summary->volumeId = volume;
1002 summary->RWvolumeId = rwvolume;
1003 summary->nInodes = n;
1004 summary->nSpecialInodes = nSpecial;
1005 summary->maxUniquifier = maxunique;
1009 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1011 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1012 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1013 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1018 * Collect list of inodes in file named by path. If a truly fatal error,
1019 * unlink the file and abort. For lessor errors, return -1. The file will
1020 * be unlinked by the caller.
1023 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1025 struct afs_stat status;
1027 struct ViceInodeInfo *ip;
1028 struct InodeSummary summary;
1029 char summaryFileName[50];
1032 char *dev = fileSysPath;
1033 char *wpath = fileSysPath;
1035 char *dev = fileSysDeviceName;
1036 char *wpath = filesysfulldev;
1038 char *part = fileSysPath;
1041 /* This file used to come from vfsck; cobble it up ourselves now... */
1043 ListViceInodes(dev, fileSysPath, path,
1044 singleVolumeNumber ? OnlyOneVolume : 0,
1045 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1047 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);
1051 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1053 if (forceSal && !ForceSalvage) {
1054 Log("***Forced salvage of all volumes on this partition***\n");
1057 inodeFd = afs_open(path, O_RDWR);
1058 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1060 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1062 tdir = (tmpdir ? tmpdir : part);
1064 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1065 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1067 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1068 "%s/salvage.temp.%d", tdir, getpid());
1070 summaryFile = afs_fopen(summaryFileName, "a+");
1071 if (summaryFile == NULL) {
1074 Abort("Unable to create inode summary file\n");
1076 if (!canfork || debug || Fork() == 0) {
1078 unsigned long st_size=(unsigned long) status.st_size;
1079 nInodes = st_size / sizeof(struct ViceInodeInfo);
1081 fclose(summaryFile);
1083 unlink(summaryFileName);
1084 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1085 RemoveTheForce(fileSysPath);
1087 struct VolumeSummary *vsp;
1090 GetVolumeSummary(singleVolumeNumber);
1092 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1094 DeleteExtraVolumeHeaderFile(vsp);
1097 Log("%s vice inodes on %s; not salvaged\n",
1098 singleVolumeNumber ? "No applicable" : "No", dev);
1101 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1103 fclose(summaryFile);
1106 unlink(summaryFileName);
1108 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1111 if (read(inodeFd, ip, st_size) != st_size) {
1112 fclose(summaryFile);
1115 unlink(summaryFileName);
1116 Abort("Unable to read inode table; %s not salvaged\n", dev);
1118 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1119 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1120 || write(inodeFd, ip, st_size) != st_size) {
1121 fclose(summaryFile);
1124 unlink(summaryFileName);
1125 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1129 CountVolumeInodes(ip, nInodes, &summary);
1130 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1131 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1132 fclose(summaryFile);
1136 summary.index += (summary.nInodes);
1137 nInodes -= summary.nInodes;
1138 ip += summary.nInodes;
1140 /* Following fflush is not fclose, because if it was debug mode would not work */
1141 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1142 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1143 fclose(summaryFile);
1147 if (canfork && !debug) {
1152 if (Wait("Inode summary") == -1) {
1153 fclose(summaryFile);
1156 unlink(summaryFileName);
1157 Exit(1); /* salvage of this partition aborted */
1160 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1161 if (status.st_size != 0) {
1163 unsigned long st_status=(unsigned long)status.st_size;
1164 inodeSummary = (struct InodeSummary *)malloc(st_status);
1165 assert(inodeSummary != NULL);
1166 /* For GNU we need to do lseek to get the file pointer moved. */
1167 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1168 ret = read(fileno(summaryFile), inodeSummary, st_status);
1169 assert(ret == st_status);
1171 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1172 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1173 fclose(summaryFile);
1175 unlink(summaryFileName);
1179 /* Comparison routine for volume sort.
1180 This is setup so that a read-write volume comes immediately before
1181 any read-only clones of that volume */
1183 CompareVolumes(const void *_p1, const void *_p2)
1185 register const struct VolumeSummary *p1 = _p1;
1186 register const struct VolumeSummary *p2 = _p2;
1187 if (p1->header.parent != p2->header.parent)
1188 return p1->header.parent < p2->header.parent ? -1 : 1;
1189 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1191 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1193 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1197 GetVolumeSummary(VolumeId singleVolumeNumber)
1200 afs_int32 nvols = 0;
1201 struct VolumeSummary *vsp, vs;
1202 struct VolumeDiskHeader diskHeader;
1205 /* Get headers from volume directory */
1206 dirp = opendir(fileSysPath);
1208 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1209 if (!singleVolumeNumber) {
1210 while ((dp = readdir(dirp))) {
1211 char *p = dp->d_name;
1212 p = strrchr(dp->d_name, '.');
1213 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1216 sprintf(name, "%s/%s", fileSysPath, dp->d_name);
1217 if ((fd = afs_open(name, O_RDONLY)) != -1
1218 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1219 == sizeof(diskHeader)
1220 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1221 DiskToVolumeHeader(&vs.header, &diskHeader);
1229 dirp = opendir("."); /* No rewinddir for NT */
1236 (struct VolumeSummary *)malloc(nvols *
1237 sizeof(struct VolumeSummary));
1240 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1241 assert(volumeSummaryp != NULL);
1244 vsp = volumeSummaryp;
1245 while ((dp = readdir(dirp))) {
1246 char *p = dp->d_name;
1247 p = strrchr(dp->d_name, '.');
1248 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1252 sprintf(name, "%s/%s", fileSysPath, dp->d_name);
1253 if ((fd = afs_open(name, O_RDONLY)) == -1
1254 || read(fd, &diskHeader, sizeof(diskHeader))
1255 != sizeof(diskHeader)
1256 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1261 if (!singleVolumeNumber) {
1263 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1268 char nameShouldBe[64];
1269 DiskToVolumeHeader(&vsp->header, &diskHeader);
1270 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1271 && vsp->header.parent != singleVolumeNumber) {
1272 if (programType == salvageServer) {
1273 #ifdef SALVSYNC_BUILD_CLIENT
1274 Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1275 vsp->header.id, vsp->header.parent);
1276 if (SALVSYNC_LinkVolume(vsp->header.parent,
1278 fileSysPartition->name,
1280 Log("schedule request failed\n");
1283 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1285 Log("%u is a read-only volume; not salvaged\n",
1286 singleVolumeNumber);
1290 if (!singleVolumeNumber
1291 || (vsp->header.id == singleVolumeNumber
1292 || vsp->header.parent == singleVolumeNumber)) {
1293 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1294 VFORMAT, afs_printable_uint32_lu(vsp->header.id));
1295 if (singleVolumeNumber
1296 && vsp->header.id != singleVolumeNumber)
1297 AskOffline(vsp->header.id, fileSysPartition->name);
1298 if (strcmp(nameShouldBe, dp->d_name)) {
1300 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 " : ""));
1304 vsp->fileName = ToString(dp->d_name);
1314 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1318 /* Find the link table. This should be associated with the RW volume or, if
1319 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1322 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1323 struct ViceInodeInfo *allInodes)
1326 struct ViceInodeInfo *ip;
1328 for (i = 0; i < nVols; i++) {
1329 ip = allInodes + isp[i].index;
1330 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1331 if (ip[j].u.special.type == VI_LINKTABLE)
1332 return ip[j].inodeNumber;
1339 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1341 struct versionStamp version;
1344 if (!VALID_INO(ino))
1346 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1347 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1348 if (!VALID_INO(ino))
1350 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1351 isp->RWvolumeId, errno);
1352 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1353 fdP = IH_OPEN(VGLinkH);
1355 Abort("Can't open link table for volume %u (error = %d)\n",
1356 isp->RWvolumeId, errno);
1358 if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1359 Abort("Can't truncate link table for volume %u (error = %d)\n",
1360 isp->RWvolumeId, errno);
1362 version.magic = LINKTABLEMAGIC;
1363 version.version = LINKTABLEVERSION;
1365 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1367 Abort("Can't truncate link table for volume %u (error = %d)\n",
1368 isp->RWvolumeId, errno);
1370 FDH_REALLYCLOSE(fdP);
1372 /* If the volume summary exits (i.e., the V*.vol header file exists),
1373 * then set this inode there as well.
1375 if (isp->volSummary)
1376 isp->volSummary->header.linkTable = ino;
1385 SVGParms_t *parms = (SVGParms_t *) arg;
1386 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1391 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1394 pthread_attr_t tattr;
1398 /* Initialize per volume global variables, even if later code does so */
1402 memset(&VolInfo, 0, sizeof(VolInfo));
1404 parms.svgp_inodeSummaryp = isp;
1405 parms.svgp_count = nVols;
1406 code = pthread_attr_init(&tattr);
1408 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1412 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1414 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1417 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1419 Log("Failed to create thread to salvage volume group %u\n",
1423 (void)pthread_join(tid, NULL);
1425 #endif /* AFS_NT40_ENV */
1428 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1430 struct ViceInodeInfo *inodes, *allInodes, *ip;
1431 int i, totalInodes, size, salvageTo;
1435 int dec_VGLinkH = 0;
1437 FdHandle_t *fdP = NULL;
1440 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1441 && isp->nSpecialInodes > 0);
1442 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1443 if (!ForceSalvage && QuickCheck(isp, nVols))
1446 if (ShowMounts && !haveRWvolume)
1448 if (canfork && !debug && Fork() != 0) {
1449 (void)Wait("Salvage volume group");
1452 for (i = 0, totalInodes = 0; i < nVols; i++)
1453 totalInodes += isp[i].nInodes;
1454 size = totalInodes * sizeof(struct ViceInodeInfo);
1455 inodes = (struct ViceInodeInfo *)malloc(size);
1456 allInodes = inodes - isp->index; /* this would the base of all the inodes
1457 * for the partition, if all the inodes
1458 * had been read into memory */
1460 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1462 assert(read(inodeFd, inodes, size) == size);
1464 /* Don't try to salvage a read write volume if there isn't one on this
1466 salvageTo = haveRWvolume ? 0 : 1;
1468 #ifdef AFS_NAMEI_ENV
1469 ino = FindLinkHandle(isp, nVols, allInodes);
1470 if (VALID_INO(ino)) {
1471 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1472 fdP = IH_OPEN(VGLinkH);
1474 if (!VALID_INO(ino) || fdP == NULL) {
1475 Log("%s link table for volume %u.\n",
1476 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1478 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1481 struct ViceInodeInfo *ip;
1482 CreateLinkTable(isp, ino);
1483 fdP = IH_OPEN(VGLinkH);
1484 /* Sync fake 1 link counts to the link table, now that it exists */
1486 for (i = 0; i < nVols; i++) {
1487 ip = allInodes + isp[i].index;
1488 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1490 nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1492 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1500 FDH_REALLYCLOSE(fdP);
1502 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1505 /* Salvage in reverse order--read/write volume last; this way any
1506 * Inodes not referenced by the time we salvage the read/write volume
1507 * can be picked up by the read/write volume */
1508 /* ACTUALLY, that's not done right now--the inodes just vanish */
1509 for (i = nVols - 1; i >= salvageTo; i--) {
1511 struct InodeSummary *lisp = &isp[i];
1512 #ifdef AFS_NAMEI_ENV
1513 /* If only the RO is present on this partition, the link table
1514 * shows up as a RW volume special file. Need to make sure the
1515 * salvager doesn't try to salvage the non-existent RW.
1517 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1518 /* If this only special inode is the link table, continue */
1519 if (inodes->u.special.type == VI_LINKTABLE) {
1526 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1527 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1528 /* Check inodes twice. The second time do things seriously. This
1529 * way the whole RO volume can be deleted, below, if anything goes wrong */
1530 for (check = 1; check >= 0; check--) {
1532 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1534 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1535 if (rw && deleteMe) {
1536 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1537 * volume won't be called */
1543 if (rw && check == 1)
1545 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1546 MaybeZapVolume(lisp, "Vnode index", 0, check);
1552 /* Fix actual inode counts */
1554 Log("totalInodes %d\n",totalInodes);
1555 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1556 static int TraceBadLinkCounts = 0;
1557 #ifdef AFS_NAMEI_ENV
1558 if (VGLinkH->ih_ino == ip->inodeNumber) {
1559 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1560 VGLinkH_p1 = ip->u.param[0];
1561 continue; /* Deal with this last. */
1564 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1565 TraceBadLinkCounts--; /* Limit reports, per volume */
1566 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]);
1568 while (ip->linkCount > 0) {
1569 /* below used to assert, not break */
1571 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1572 Log("idec failed. inode %s errno %d\n",
1573 PrintInode(NULL, ip->inodeNumber), errno);
1579 while (ip->linkCount < 0) {
1580 /* these used to be asserts */
1582 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1583 Log("iinc failed. inode %s errno %d\n",
1584 PrintInode(NULL, ip->inodeNumber), errno);
1591 #ifdef AFS_NAMEI_ENV
1592 while (dec_VGLinkH > 0) {
1593 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1594 Log("idec failed on link table, errno = %d\n", errno);
1598 while (dec_VGLinkH < 0) {
1599 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1600 Log("iinc failed on link table, errno = %d\n", errno);
1607 /* Directory consistency checks on the rw volume */
1609 SalvageVolume(isp, VGLinkH);
1610 IH_RELEASE(VGLinkH);
1612 if (canfork && !debug) {
1619 QuickCheck(register struct InodeSummary *isp, int nVols)
1621 /* Check headers BEFORE forking */
1625 for (i = 0; i < nVols; i++) {
1626 struct VolumeSummary *vs = isp[i].volSummary;
1627 VolumeDiskData volHeader;
1629 /* Don't salvage just because phantom rw volume is there... */
1630 /* (If a read-only volume exists, read/write inodes must also exist) */
1631 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1635 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1636 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1637 == sizeof(volHeader)
1638 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1639 && volHeader.dontSalvage == DONT_SALVAGE
1640 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1641 if (volHeader.inUse != 0) {
1642 volHeader.inUse = 0;
1643 volHeader.inService = 1;
1645 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1646 != sizeof(volHeader)) {
1662 /* SalvageVolumeHeaderFile
1664 * Salvage the top level V*.vol header file. Make sure the special files
1665 * exist and that there are no duplicates.
1667 * Calls SalvageHeader for each possible type of volume special file.
1671 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1672 register struct ViceInodeInfo *inodes, int RW,
1673 int check, int *deleteMe)
1677 register struct ViceInodeInfo *ip;
1678 int allinodesobsolete = 1;
1679 struct VolumeDiskHeader diskHeader;
1683 memset(&tempHeader, 0, sizeof(tempHeader));
1684 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1685 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1686 tempHeader.id = isp->volumeId;
1687 tempHeader.parent = isp->RWvolumeId;
1688 /* Check for duplicates (inodes are sorted by type field) */
1689 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1690 ip = &inodes[isp->index + i];
1691 if (ip->u.special.type == (ip + 1)->u.special.type) {
1693 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1697 for (i = 0; i < isp->nSpecialInodes; i++) {
1698 ip = &inodes[isp->index + i];
1699 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1701 Log("Rubbish header inode\n");
1704 Log("Rubbish header inode; deleted\n");
1705 } else if (!stuff[ip->u.special.type - 1].obsolete) {
1706 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1707 if (!check && ip->u.special.type != VI_LINKTABLE)
1708 ip->linkCount--; /* Keep the inode around */
1709 allinodesobsolete = 0;
1713 if (allinodesobsolete) {
1720 VGLinkH_cnt++; /* one for every header. */
1722 if (!RW && !check && isp->volSummary) {
1723 ClearROInUseBit(isp->volSummary);
1727 for (i = 0; i < MAXINODETYPE; i++) {
1728 if (stuff[i].inodeType == VI_LINKTABLE) {
1729 /* Gross hack: SalvageHeader does a bcmp on the volume header.
1730 * And we may have recreated the link table earlier, so set the
1731 * RW header as well.
1733 if (VALID_INO(VGLinkH->ih_ino)) {
1734 *stuff[i].inode = VGLinkH->ih_ino;
1738 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1742 if (isp->volSummary == NULL) {
1744 char headerName[64];
1745 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
1746 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
1748 Log("No header file for volume %u\n", isp->volumeId);
1752 Log("No header file for volume %u; %screating %s\n",
1753 isp->volumeId, (Testing ? "it would have been " : ""),
1755 headerFd = afs_open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
1756 assert(headerFd != -1);
1757 isp->volSummary = (struct VolumeSummary *)
1758 malloc(sizeof(struct VolumeSummary));
1759 isp->volSummary->fileName = ToString(headerName);
1762 char headerName[64];
1763 /* hack: these two fields are obsolete... */
1764 isp->volSummary->header.volumeAcl = 0;
1765 isp->volSummary->header.volumeMountTable = 0;
1768 (&isp->volSummary->header, &tempHeader,
1769 sizeof(struct VolumeHeader))) {
1770 /* We often remove the name before calling us, so we make a fake one up */
1771 if (isp->volSummary->fileName) {
1772 strcpy(headerName, isp->volSummary->fileName);
1774 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
1775 isp->volSummary->fileName = ToString(headerName);
1777 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
1779 Log("Header file %s is damaged or no longer valid%s\n", path,
1780 (check ? "" : "; repairing"));
1784 headerFd = afs_open(path, O_RDWR | O_TRUNC, 0644);
1785 assert(headerFd != -1);
1789 memcpy(&isp->volSummary->header, &tempHeader,
1790 sizeof(struct VolumeHeader));
1793 Log("It would have written a new header file for volume %u\n",
1796 VolumeHeaderToDisk(&diskHeader, &tempHeader);
1797 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1798 != sizeof(struct VolumeDiskHeader)) {
1799 Log("Couldn't rewrite volume header file!\n");
1806 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1807 isp->volSummary->header.volumeInfo);
1812 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1816 VolumeDiskData volumeInfo;
1817 struct versionStamp fileHeader;
1826 #ifndef AFS_NAMEI_ENV
1827 if (sp->inodeType == VI_LINKTABLE)
1830 if (*(sp->inode) == 0) {
1832 Log("Missing inode in volume header (%s)\n", sp->description);
1836 Log("Missing inode in volume header (%s); %s\n", sp->description,
1837 (Testing ? "it would have recreated it" : "recreating"));
1840 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1841 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1842 if (!VALID_INO(*(sp->inode)))
1844 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1845 sp->description, errno);
1850 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1851 fdP = IH_OPEN(specH);
1852 if (OKToZap && (fdP == NULL) && BadError(errno)) {
1853 /* bail out early and destroy the volume */
1855 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1862 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1863 sp->description, errno);
1866 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1867 || header.fileHeader.magic != sp->stamp.magic)) {
1869 Log("Part of the header (%s) is corrupted\n", sp->description);
1870 FDH_REALLYCLOSE(fdP);
1874 Log("Part of the header (%s) is corrupted; recreating\n",
1878 if (sp->inodeType == VI_VOLINFO
1879 && header.volumeInfo.destroyMe == DESTROY_ME) {
1882 FDH_REALLYCLOSE(fdP);
1886 if (recreate && !Testing) {
1889 ("Internal error: recreating volume header (%s) in check mode\n",
1891 code = FDH_TRUNC(fdP, 0);
1893 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1894 sp->description, errno);
1896 /* The following code should be moved into vutil.c */
1897 if (sp->inodeType == VI_VOLINFO) {
1899 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1900 header.volumeInfo.stamp = sp->stamp;
1901 header.volumeInfo.id = isp->volumeId;
1902 header.volumeInfo.parentId = isp->RWvolumeId;
1903 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1904 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1905 isp->volumeId, isp->volumeId);
1906 header.volumeInfo.inService = 0;
1907 header.volumeInfo.blessed = 0;
1908 /* The + 1000 is a hack in case there are any files out in venus caches */
1909 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1910 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
1911 header.volumeInfo.needsCallback = 0;
1912 gettimeofday(&tp, 0);
1913 header.volumeInfo.creationDate = tp.tv_sec;
1914 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1916 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1917 sp->description, errno);
1920 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1921 sizeof(header.volumeInfo));
1922 if (code != sizeof(header.volumeInfo)) {
1925 ("Unable to write volume header file (%s) (errno = %d)\n",
1926 sp->description, errno);
1927 Abort("Unable to write entire volume header file (%s)\n",
1931 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1933 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1934 sp->description, errno);
1936 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1937 if (code != sizeof(sp->stamp)) {
1940 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1941 sp->description, errno);
1943 ("Unable to write entire version stamp in volume header file (%s)\n",
1948 FDH_REALLYCLOSE(fdP);
1950 if (sp->inodeType == VI_VOLINFO) {
1951 VolInfo = header.volumeInfo;
1954 if (VolInfo.updateDate) {
1955 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1957 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1958 (Testing ? "it would have been " : ""), update);
1960 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1962 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1963 VolInfo.id, update);
1973 SalvageVnodes(register struct InodeSummary *rwIsp,
1974 register struct InodeSummary *thisIsp,
1975 register struct ViceInodeInfo *inodes, int check)
1977 int ilarge, ismall, ioffset, RW, nInodes;
1978 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
1981 RW = (rwIsp == thisIsp);
1982 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1984 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1985 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1986 if (check && ismall == -1)
1989 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1990 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1991 return (ilarge == 0 && ismall == 0 ? 0 : -1);
1995 SalvageIndex(Inode ino, VnodeClass class, int RW,
1996 register struct ViceInodeInfo *ip, int nInodes,
1997 struct VolumeSummary *volSummary, int check)
1999 VolumeId volumeNumber;
2000 char buf[SIZEOF_LARGEDISKVNODE];
2001 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2003 StreamHandle_t *file;
2004 struct VnodeClassInfo *vcp;
2006 afs_fsize_t vnodeLength;
2007 int vnodeIndex, nVnodes;
2008 afs_ino_str_t stmp1, stmp2;
2012 volumeNumber = volSummary->header.id;
2013 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2014 fdP = IH_OPEN(handle);
2015 assert(fdP != NULL);
2016 file = FDH_FDOPEN(fdP, "r+");
2017 assert(file != NULL);
2018 vcp = &VnodeClassInfo[class];
2019 size = OS_SIZE(fdP->fd_fd);
2021 nVnodes = (size / vcp->diskSize) - 1;
2023 assert((nVnodes + 1) * vcp->diskSize == size);
2024 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2028 for (vnodeIndex = 0;
2029 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2030 nVnodes--, vnodeIndex++) {
2031 if (vnode->type != vNull) {
2032 int vnodeChanged = 0;
2033 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2034 /* Log programs that belong to root (potentially suid root);
2035 * don't bother for read-only or backup volumes */
2036 #ifdef notdef /* This is done elsewhere */
2037 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2038 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);
2040 if (VNDISK_GET_INO(vnode) == 0) {
2042 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2043 memset(vnode, 0, vcp->diskSize);
2047 if (vcp->magic != vnode->vnodeMagic) {
2048 /* bad magic #, probably partially created vnode */
2049 Log("Partially allocated vnode %d deleted.\n",
2051 memset(vnode, 0, vcp->diskSize);
2055 /* ****** Should do a bit more salvage here: e.g. make sure
2056 * vnode type matches what it should be given the index */
2057 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2058 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2059 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2060 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2067 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2068 /* The following doesn't work, because the version number
2069 * is not maintained correctly by the file server */
2070 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2071 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2073 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2079 /* For RW volume, look for vnode with matching inode number;
2080 * if no such match, take the first determined by our sort
2082 register struct ViceInodeInfo *lip = ip;
2083 register int lnInodes = nInodes;
2085 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2086 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2095 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2096 /* "Matching" inode */
2100 vu = vnode->uniquifier;
2101 iu = ip->u.vnode.vnodeUniquifier;
2102 vd = vnode->dataVersion;
2103 id = ip->u.vnode.inodeDataVersion;
2105 * Because of the possibility of the uniquifier overflows (> 4M)
2106 * we compare them modulo the low 22-bits; we shouldn't worry
2107 * about mismatching since they shouldn't to many old
2108 * uniquifiers of the same vnode...
2110 if (IUnique(vu) != IUnique(iu)) {
2112 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2115 vnode->uniquifier = iu;
2116 #ifdef AFS_3DISPARES
2117 vnode->dataVersion = (id >= vd ?
2120 1887437 ? vd : id) :
2123 1887437 ? id : vd));
2125 #if defined(AFS_SGI_EXMAG)
2126 vnode->dataVersion = (id >= vd ?
2129 15099494 ? vd : id) :
2132 15099494 ? id : vd));
2134 vnode->dataVersion = (id > vd ? id : vd);
2135 #endif /* AFS_SGI_EXMAG */
2136 #endif /* AFS_3DISPARES */
2139 /* don't bother checking for vd > id any more, since
2140 * partial file transfers always result in this state,
2141 * and you can't do much else anyway (you've already
2142 * found the best data you can) */
2143 #ifdef AFS_3DISPARES
2144 if (!vnodeIsDirectory(vnodeNumber)
2145 && ((vd < id && (id - vd) < 1887437)
2146 || ((vd > id && (vd - id) > 1887437)))) {
2148 #if defined(AFS_SGI_EXMAG)
2149 if (!vnodeIsDirectory(vnodeNumber)
2150 && ((vd < id && (id - vd) < 15099494)
2151 || ((vd > id && (vd - id) > 15099494)))) {
2153 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2154 #endif /* AFS_SGI_EXMAG */
2157 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2158 vnode->dataVersion = id;
2163 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2166 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);
2168 VNDISK_SET_INO(vnode, ip->inodeNumber);
2173 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);
2175 VNDISK_SET_INO(vnode, ip->inodeNumber);
2178 VNDISK_GET_LEN(vnodeLength, vnode);
2179 if (ip->byteCount != vnodeLength) {
2182 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2187 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2188 VNDISK_SET_LEN(vnode, ip->byteCount);
2192 ip->linkCount--; /* Keep the inode around */
2195 } else { /* no matching inode */
2196 if (VNDISK_GET_INO(vnode) != 0
2197 || vnode->type == vDirectory) {
2198 /* No matching inode--get rid of the vnode */
2200 if (VNDISK_GET_INO(vnode)) {
2202 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2206 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2211 if (VNDISK_GET_INO(vnode)) {
2213 time_t serverModifyTime = vnode->serverModifyTime;
2214 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));
2218 time_t serverModifyTime = vnode->serverModifyTime;
2219 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2222 memset(vnode, 0, vcp->diskSize);
2225 /* Should not reach here becuase we checked for
2226 * (inodeNumber == 0) above. And where we zero the vnode,
2227 * we also goto vnodeDone.
2231 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2235 } /* VNDISK_GET_INO(vnode) != 0 */
2237 assert(!(vnodeChanged && check));
2238 if (vnodeChanged && !Testing) {
2240 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2241 (char *)vnode, vcp->diskSize)
2243 VolumeChanged = 1; /* For break call back */
2254 struct VnodeEssence *
2255 CheckVnodeNumber(VnodeId vnodeNumber)
2258 struct VnodeInfo *vip;
2261 class = vnodeIdToClass(vnodeNumber);
2262 vip = &vnodeInfo[class];
2263 offset = vnodeIdToBitNumber(vnodeNumber);
2264 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2268 CopyOnWrite(register struct DirSummary *dir)
2270 /* Copy the directory unconditionally if we are going to change it:
2271 * not just if was cloned.
2273 struct VnodeDiskObject vnode;
2274 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2275 Inode oldinode, newinode;
2278 if (dir->copied || Testing)
2280 DFlush(); /* Well justified paranoia... */
2283 IH_IREAD(vnodeInfo[vLarge].handle,
2284 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2286 assert(code == sizeof(vnode));
2287 oldinode = VNDISK_GET_INO(&vnode);
2288 /* Increment the version number by a whole lot to avoid problems with
2289 * clients that were promised new version numbers--but the file server
2290 * crashed before the versions were written to disk.
2293 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2294 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2296 assert(VALID_INO(newinode));
2297 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2299 VNDISK_SET_INO(&vnode, newinode);
2301 IH_IWRITE(vnodeInfo[vLarge].handle,
2302 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2304 assert(code == sizeof(vnode));
2306 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2307 fileSysDevice, newinode);
2308 /* Don't delete the original inode right away, because the directory is
2309 * still being scanned.
2315 * This function should either successfully create a new dir, or give up
2316 * and leave things the way they were. In particular, if it fails to write
2317 * the new dir properly, it should return w/o changing the reference to the
2321 CopyAndSalvage(register struct DirSummary *dir)
2323 struct VnodeDiskObject vnode;
2324 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2325 Inode oldinode, newinode;
2330 afs_int32 parentUnique = 1;
2331 struct VnodeEssence *vnodeEssence;
2336 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2338 IH_IREAD(vnodeInfo[vLarge].handle,
2339 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2341 assert(lcode == sizeof(vnode));
2342 oldinode = VNDISK_GET_INO(&vnode);
2343 /* Increment the version number by a whole lot to avoid problems with
2344 * clients that were promised new version numbers--but the file server
2345 * crashed before the versions were written to disk.
2348 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2349 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2351 assert(VALID_INO(newinode));
2352 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2354 /* Assign . and .. vnode numbers from dir and vnode.parent.
2355 * The uniquifier for . is in the vnode.
2356 * The uniquifier for .. might be set to a bogus value of 1 and
2357 * the salvager will later clean it up.
2359 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2360 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2363 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2365 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2370 /* didn't really build the new directory properly, let's just give up. */
2371 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2372 Log("Directory salvage returned code %d, continuing.\n", code);
2374 Log("also failed to decrement link count on new inode");
2378 Log("Checking the results of the directory salvage...\n");
2379 if (!DirOK(&newdir)) {
2380 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2381 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2386 VNDISK_SET_INO(&vnode, newinode);
2387 length = Length(&newdir);
2388 VNDISK_SET_LEN(&vnode, length);
2390 IH_IWRITE(vnodeInfo[vLarge].handle,
2391 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2393 assert(lcode == sizeof(vnode));
2396 nt_sync(fileSysDevice);
2398 sync(); /* this is slow, but hopefully rarely called. We don't have
2399 * an open FD on the file itself to fsync.
2403 vnodeInfo[vLarge].handle->ih_synced = 1;
2405 /* make sure old directory file is really closed */
2406 fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2407 FDH_REALLYCLOSE(fdP);
2409 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2411 dir->dirHandle = newdir;
2415 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2418 struct VnodeEssence *vnodeEssence;
2419 afs_int32 dirOrphaned, todelete;
2421 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2423 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2424 if (vnodeEssence == NULL) {
2426 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2430 assert(Delete(&dir->dirHandle, name) == 0);
2435 #ifndef AFS_NAMEI_ENV
2436 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2437 * mount inode for the partition. If this inode were deleted, it would crash
2440 if (vnodeEssence->InodeNumber == 0) {
2441 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"));
2444 assert(Delete(&dir->dirHandle, name) == 0);
2451 if (!(vnodeNumber & 1) && !Showmode
2452 && !(vnodeEssence->count || vnodeEssence->unique
2453 || vnodeEssence->modeBits)) {
2454 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2455 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2456 vnodeNumber, unique,
2457 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2462 assert(Delete(&dir->dirHandle, name) == 0);
2468 /* Check if the Uniquifiers match. If not, change the directory entry
2469 * so its unique matches the vnode unique. Delete if the unique is zero
2470 * or if the directory is orphaned.
2472 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2473 if (!vnodeEssence->unique
2474 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2475 /* This is an orphaned directory. Don't delete the . or ..
2476 * entry. Otherwise, it will get created in the next
2477 * salvage and deleted again here. So Just skip it.
2482 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2485 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")));
2489 fid.Vnode = vnodeNumber;
2490 fid.Unique = vnodeEssence->unique;
2492 assert(Delete(&dir->dirHandle, name) == 0);
2494 assert(Create(&dir->dirHandle, name, &fid) == 0);
2497 return 0; /* no need to continue */
2500 if (strcmp(name, ".") == 0) {
2501 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2504 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2507 assert(Delete(&dir->dirHandle, ".") == 0);
2508 fid.Vnode = dir->vnodeNumber;
2509 fid.Unique = dir->unique;
2510 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2513 vnodeNumber = fid.Vnode; /* Get the new Essence */
2514 unique = fid.Unique;
2515 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2518 } else if (strcmp(name, "..") == 0) {
2521 struct VnodeEssence *dotdot;
2522 pa.Vnode = dir->parent;
2523 dotdot = CheckVnodeNumber(pa.Vnode);
2524 assert(dotdot != NULL); /* XXX Should not be assert */
2525 pa.Unique = dotdot->unique;
2527 pa.Vnode = dir->vnodeNumber;
2528 pa.Unique = dir->unique;
2530 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2532 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2535 assert(Delete(&dir->dirHandle, "..") == 0);
2536 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2539 vnodeNumber = pa.Vnode; /* Get the new Essence */
2541 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2543 dir->haveDotDot = 1;
2544 } else if (strncmp(name, ".__afs", 6) == 0) {
2546 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);
2550 assert(Delete(&dir->dirHandle, name) == 0);
2552 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2553 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2556 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2557 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);
2558 if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
2559 && !(vnodeEssence->modeBits & 0111)) {
2565 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2566 vnodeEssence->InodeNumber);
2569 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
2573 size = FDH_SIZE(fdP);
2575 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
2576 FDH_REALLYCLOSE(fdP);
2583 code = FDH_READ(fdP, buf, size);
2586 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
2587 Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
2588 dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
2589 Testing ? "would convert" : "converted");
2590 vnodeEssence->modeBits |= 0111;
2591 vnodeEssence->changed = 1;
2592 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2593 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2594 dir->name ? dir->name : "??", name, buf);
2596 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
2597 dir->vname, vnodeNumber, size, code);
2599 FDH_REALLYCLOSE(fdP);
2602 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2603 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);
2604 if (vnodeIdToClass(vnodeNumber) == vLarge
2605 && vnodeEssence->name == NULL) {
2607 if ((n = (char *)malloc(strlen(name) + 1)))
2609 vnodeEssence->name = n;
2612 /* The directory entry points to the vnode. Check to see if the
2613 * vnode points back to the directory. If not, then let the
2614 * directory claim it (else it might end up orphaned). Vnodes
2615 * already claimed by another directory are deleted from this
2616 * directory: hardlinks to the same vnode are not allowed
2617 * from different directories.
2619 if (vnodeEssence->parent != dir->vnodeNumber) {
2620 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2621 /* Vnode does not point back to this directory.
2622 * Orphaned dirs cannot claim a file (it may belong to
2623 * another non-orphaned dir).
2626 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);
2628 vnodeEssence->parent = dir->vnodeNumber;
2629 vnodeEssence->changed = 1;
2631 /* Vnode was claimed by another directory */
2634 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 " : ""));
2635 } else if (vnodeNumber == 1) {
2636 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 " : ""));
2638 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 " : ""));
2643 assert(Delete(&dir->dirHandle, name) == 0);
2648 /* This directory claims the vnode */
2649 vnodeEssence->claimed = 1;
2651 vnodeEssence->count--;
2656 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2658 register struct VnodeInfo *vip = &vnodeInfo[class];
2659 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2660 char buf[SIZEOF_LARGEDISKVNODE];
2661 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2663 StreamHandle_t *file;
2668 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2669 fdP = IH_OPEN(vip->handle);
2670 assert(fdP != NULL);
2671 file = FDH_FDOPEN(fdP, "r+");
2672 assert(file != NULL);
2673 size = OS_SIZE(fdP->fd_fd);
2675 vip->nVnodes = (size / vcp->diskSize) - 1;
2676 if (vip->nVnodes > 0) {
2677 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2678 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2679 assert((vip->vnodes = (struct VnodeEssence *)
2680 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2681 if (class == vLarge) {
2682 assert((vip->inodes = (Inode *)
2683 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2692 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2693 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2694 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2695 nVnodes--, vnodeIndex++) {
2696 if (vnode->type != vNull) {
2697 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2698 afs_fsize_t vnodeLength;
2699 vip->nAllocatedVnodes++;
2700 vep->count = vnode->linkCount;
2701 VNDISK_GET_LEN(vnodeLength, vnode);
2702 vep->blockCount = nBlocks(vnodeLength);
2703 vip->volumeBlockCount += vep->blockCount;
2704 vep->parent = vnode->parent;
2705 vep->unique = vnode->uniquifier;
2706 if (*maxu < vnode->uniquifier)
2707 *maxu = vnode->uniquifier;
2708 vep->modeBits = vnode->modeBits;
2709 vep->InodeNumber = VNDISK_GET_INO(vnode);
2710 vep->type = vnode->type;
2711 vep->author = vnode->author;
2712 vep->owner = vnode->owner;
2713 vep->group = vnode->group;
2714 if (vnode->type == vDirectory) {
2715 assert(class == vLarge);
2716 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2725 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2727 struct VnodeEssence *parentvp;
2733 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2734 && GetDirName(vp->parent, parentvp, path)) {
2736 strcat(path, vp->name);
2742 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2743 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2746 IsVnodeOrphaned(VnodeId vnode)
2748 struct VnodeEssence *vep;
2751 return (1); /* Vnode zero does not exist */
2753 return (0); /* The root dir vnode is always claimed */
2754 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2755 if (!vep || !vep->claimed)
2756 return (1); /* Vnode is not claimed - it is orphaned */
2758 return (IsVnodeOrphaned(vep->parent));
2762 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2763 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2766 static struct DirSummary dir;
2767 static struct DirHandle dirHandle;
2768 struct VnodeEssence *parent;
2769 static char path[MAXPATHLEN];
2772 if (dirVnodeInfo->vnodes[i].salvaged)
2773 return; /* already salvaged */
2776 dirVnodeInfo->vnodes[i].salvaged = 1;
2778 if (dirVnodeInfo->inodes[i] == 0)
2779 return; /* Not allocated to a directory */
2781 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2782 if (dirVnodeInfo->vnodes[i].parent) {
2783 Log("Bad parent, vnode 1; %s...\n",
2784 (Testing ? "skipping" : "salvaging"));
2785 dirVnodeInfo->vnodes[i].parent = 0;
2786 dirVnodeInfo->vnodes[i].changed = 1;
2789 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2790 if (parent && parent->salvaged == 0)
2791 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2792 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2793 rootdir, rootdirfound);
2796 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2797 dir.unique = dirVnodeInfo->vnodes[i].unique;
2800 dir.parent = dirVnodeInfo->vnodes[i].parent;
2801 dir.haveDot = dir.haveDotDot = 0;
2802 dir.ds_linkH = alinkH;
2803 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2804 dirVnodeInfo->inodes[i]);
2806 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2809 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2810 (Testing ? "skipping" : "salvaging"));
2813 CopyAndSalvage(&dir);
2817 dirHandle = dir.dirHandle;
2820 GetDirName(bitNumberToVnodeNumber(i, vLarge),
2821 &dirVnodeInfo->vnodes[i], path);
2824 /* If enumeration failed for random reasons, we will probably delete
2825 * too much stuff, so we guard against this instead.
2827 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2830 /* Delete the old directory if it was copied in order to salvage.
2831 * CopyOnWrite has written the new inode # to the disk, but we still
2832 * have the old one in our local structure here. Thus, we idec the
2836 if (dir.copied && !Testing) {
2837 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2839 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2842 /* Remember rootdir DirSummary _after_ it has been judged */
2843 if (dir.vnodeNumber == 1 && dir.unique == 1) {
2844 memcpy(rootdir, &dir, sizeof(struct DirSummary));
2852 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2854 /* This routine, for now, will only be called for read-write volumes */
2856 int BlocksInVolume = 0, FilesInVolume = 0;
2857 register VnodeClass class;
2858 struct DirSummary rootdir, oldrootdir;
2859 struct VnodeInfo *dirVnodeInfo;
2860 struct VnodeDiskObject vnode;
2861 VolumeDiskData volHeader;
2863 int orphaned, rootdirfound = 0;
2864 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
2865 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
2866 struct VnodeEssence *vep;
2869 afs_sfsize_t nBytes;
2871 VnodeId LFVnode, ThisVnode;
2872 Unique LFUnique, ThisUnique;
2875 vid = rwIsp->volSummary->header.id;
2876 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2877 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2878 assert(nBytes == sizeof(volHeader));
2879 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2880 assert(volHeader.destroyMe != DESTROY_ME);
2881 /* (should not have gotten this far with DESTROY_ME flag still set!) */
2883 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2885 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2888 dirVnodeInfo = &vnodeInfo[vLarge];
2889 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2890 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2894 nt_sync(fileSysDevice);
2896 sync(); /* This used to be done lower level, for every dir */
2903 /* Parse each vnode looking for orphaned vnodes and
2904 * connect them to the tree as orphaned (if requested).
2906 oldrootdir = rootdir;
2907 for (class = 0; class < nVNODECLASSES; class++) {
2908 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2909 vep = &(vnodeInfo[class].vnodes[v]);
2910 ThisVnode = bitNumberToVnodeNumber(v, class);
2911 ThisUnique = vep->unique;
2913 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2914 continue; /* Ignore unused, claimed, and root vnodes */
2916 /* This vnode is orphaned. If it is a directory vnode, then the '..'
2917 * entry in this vnode had incremented the parent link count (In
2918 * JudgeEntry()). We need to go to the parent and decrement that
2919 * link count. But if the parent's unique is zero, then the parent
2920 * link count was not incremented in JudgeEntry().
2922 if (class == vLarge) { /* directory vnode */
2923 pv = vnodeIdToBitNumber(vep->parent);
2924 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2925 vnodeInfo[vLarge].vnodes[pv].count++;
2929 continue; /* If no rootdir, can't attach orphaned files */
2931 /* Here we attach orphaned files and directories into the
2932 * root directory, LVVnode, making sure link counts stay correct.
2934 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2935 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
2936 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
2938 /* Update this orphaned vnode's info. Its parent info and
2939 * link count (do for orphaned directories and files).
2941 vep->parent = LFVnode; /* Parent is the root dir */
2942 vep->unique = LFUnique;
2945 vep->count--; /* Inc link count (root dir will pt to it) */
2947 /* If this orphaned vnode is a directory, change '..'.
2948 * The name of the orphaned dir/file is unknown, so we
2949 * build a unique name. No need to CopyOnWrite the directory
2950 * since it is not connected to tree in BK or RO volume and
2951 * won't be visible there.
2953 if (class == vLarge) {
2957 /* Remove and recreate the ".." entry in this orphaned directory */
2958 SetSalvageDirHandle(&dh, vid, fileSysDevice,
2959 vnodeInfo[class].inodes[v]);
2961 pa.Unique = LFUnique;
2962 assert(Delete(&dh, "..") == 0);
2963 assert(Create(&dh, "..", &pa) == 0);
2965 /* The original parent's link count was decremented above.
2966 * Here we increment the new parent's link count.
2968 pv = vnodeIdToBitNumber(LFVnode);
2969 vnodeInfo[vLarge].vnodes[pv].count--;
2973 /* Go to the root dir and add this entry. The link count of the
2974 * root dir was incremented when ".." was created. Try 10 times.
2976 for (j = 0; j < 10; j++) {
2977 pa.Vnode = ThisVnode;
2978 pa.Unique = ThisUnique;
2980 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2982 vLarge) ? "__ORPHANDIR__" :
2983 "__ORPHANFILE__"), ThisVnode,
2986 CopyOnWrite(&rootdir);
2987 code = Create(&rootdir.dirHandle, npath, &pa);
2991 ThisUnique += 50; /* Try creating a different file */
2994 Log("Attaching orphaned %s to volume's root dir as %s\n",
2995 ((class == vLarge) ? "directory" : "file"), npath);
2997 } /* for each vnode in the class */
2998 } /* for each class of vnode */
3000 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3002 if (!oldrootdir.copied && rootdir.copied) {
3004 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3007 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3010 DFlush(); /* Flush the changes */
3011 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3012 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3013 orphans = ORPH_IGNORE;
3016 /* Write out all changed vnodes. Orphaned files and directories
3017 * will get removed here also (if requested).
3019 for (class = 0; class < nVNODECLASSES; class++) {
3020 int nVnodes = vnodeInfo[class].nVnodes;
3021 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3022 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3023 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3024 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3025 for (i = 0; i < nVnodes; i++) {
3026 register struct VnodeEssence *vnp = &vnodes[i];
3027 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3029 /* If the vnode is good but is unclaimed (not listed in
3030 * any directory entries), then it is orphaned.
3033 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3034 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3038 if (vnp->changed || vnp->count) {
3042 IH_IREAD(vnodeInfo[class].handle,
3043 vnodeIndexOffset(vcp, vnodeNumber),
3044 (char *)&vnode, sizeof(vnode));
3045 assert(nBytes == sizeof(vnode));
3047 vnode.parent = vnp->parent;
3048 oldCount = vnode.linkCount;
3049 vnode.linkCount = vnode.linkCount - vnp->count;
3052 orphaned = IsVnodeOrphaned(vnodeNumber);
3054 if (!vnp->todelete) {
3055 /* Orphans should have already been attached (if requested) */
3056 assert(orphans != ORPH_ATTACH);
3057 oblocks += vnp->blockCount;
3060 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3062 BlocksInVolume -= vnp->blockCount;
3064 if (VNDISK_GET_INO(&vnode)) {
3066 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3069 memset(&vnode, 0, sizeof(vnode));
3071 } else if (vnp->count) {
3073 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3076 vnode.modeBits = vnp->modeBits;
3079 vnode.dataVersion++;
3082 IH_IWRITE(vnodeInfo[class].handle,
3083 vnodeIndexOffset(vcp, vnodeNumber),
3084 (char *)&vnode, sizeof(vnode));
3085 assert(nBytes == sizeof(vnode));
3091 if (!Showmode && ofiles) {
3092 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3094 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3098 for (class = 0; class < nVNODECLASSES; class++) {
3099 register struct VnodeInfo *vip = &vnodeInfo[class];
3100 for (i = 0; i < vip->nVnodes; i++)
3101 if (vip->vnodes[i].name)
3102 free(vip->vnodes[i].name);
3109 /* Set correct resource utilization statistics */
3110 volHeader.filecount = FilesInVolume;
3111 volHeader.diskused = BlocksInVolume;
3113 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3114 if (volHeader.uniquifier < (maxunique + 1)) {
3116 Log("Volume uniquifier is too low; fixed\n");
3117 /* Plus 2,000 in case there are workstations out there with
3118 * cached vnodes that have since been deleted
3120 volHeader.uniquifier = (maxunique + 1 + 2000);
3123 /* Turn off the inUse bit; the volume's been salvaged! */
3124 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3125 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3126 volHeader.inService = 1; /* allow service again */
3127 volHeader.needsCallback = (VolumeChanged != 0);
3128 volHeader.dontSalvage = DONT_SALVAGE;
3131 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3132 assert(nBytes == sizeof(volHeader));
3135 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3136 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3137 FilesInVolume, BlocksInVolume);
3139 IH_RELEASE(vnodeInfo[vSmall].handle);
3140 IH_RELEASE(vnodeInfo[vLarge].handle);
3146 ClearROInUseBit(struct VolumeSummary *summary)
3148 IHandle_t *h = summary->volumeInfoHandle;
3149 afs_sfsize_t nBytes;
3151 VolumeDiskData volHeader;
3153 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3154 assert(nBytes == sizeof(volHeader));
3155 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3156 volHeader.inUse = 0;
3157 volHeader.needsSalvaged = 0;
3158 volHeader.inService = 1;
3159 volHeader.dontSalvage = DONT_SALVAGE;
3161 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3162 assert(nBytes == sizeof(volHeader));
3167 * Possible delete the volume.
3169 * deleteMe - Always do so, only a partial volume.
3172 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3175 if (readOnly(isp) || deleteMe) {
3176 if (isp->volSummary && isp->volSummary->fileName) {
3179 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);
3181 Log("It will be deleted on this server (you may find it elsewhere)\n");
3184 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3186 Log("it will be deleted instead. It should be recloned.\n");
3189 unlink(isp->volSummary->fileName);
3191 } else if (!check) {
3192 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3194 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3200 AskOffline(VolumeId volumeId, char * partition)
3204 for (i = 0; i < 3; i++) {
3205 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3207 if (code == SYNC_OK) {
3209 } else if (code == SYNC_DENIED) {
3210 #ifdef DEMAND_ATTACH_ENABLE
3211 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3213 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3215 Abort("Salvage aborted\n");
3216 } else if (code == SYNC_BAD_COMMAND) {
3217 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3219 #ifdef DEMAND_ATTACH_ENABLE
3220 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3222 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3224 Abort("Salvage aborted\n");
3227 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3228 FSYNC_clientFinis();
3232 if (code != SYNC_OK) {
3233 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3234 Abort("Salvage aborted\n");
3239 AskOnline(VolumeId volumeId, char *partition)
3243 for (i = 0; i < 3; i++) {
3244 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3246 if (code == SYNC_OK) {
3248 } else if (code == SYNC_DENIED) {
3249 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3250 } else if (code == SYNC_BAD_COMMAND) {
3251 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3253 #ifdef DEMAND_ATTACH_ENABLE
3254 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3256 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3261 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3262 FSYNC_clientFinis();
3269 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3271 /* Volume parameter is passed in case iopen is upgraded in future to
3272 * require a volume Id to be passed
3275 IHandle_t *srcH, *destH;
3276 FdHandle_t *srcFdP, *destFdP;
3279 IH_INIT(srcH, device, rwvolume, inode1);
3280 srcFdP = IH_OPEN(srcH);
3281 assert(srcFdP != NULL);
3282 IH_INIT(destH, device, rwvolume, inode2);
3283 destFdP = IH_OPEN(destH);
3285 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3286 assert(FDH_WRITE(destFdP, buf, n) == n);
3288 FDH_REALLYCLOSE(srcFdP);
3289 FDH_REALLYCLOSE(destFdP);
3296 PrintInodeList(void)
3298 register struct ViceInodeInfo *ip;
3299 struct ViceInodeInfo *buf;
3300 struct afs_stat status;
3301 register int nInodes;
3303 assert(afs_fstat(inodeFd, &status) == 0);
3304 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3305 assert(buf != NULL);
3306 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3307 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3308 for (ip = buf; nInodes--; ip++) {
3309 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3310 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3311 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3312 ip->u.param[2], ip->u.param[3]);
3318 PrintInodeSummary(void)
3321 struct InodeSummary *isp;
3323 for (i = 0; i < nVolumesInInodeFile; i++) {
3324 isp = &inodeSummary[i];
3325 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);
3330 PrintVolumeSummary(void)
3333 struct VolumeSummary *vsp;
3335 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3336 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3346 assert(0); /* Fork is never executed in the NT code path */
3350 #ifdef AFS_DEMAND_ATTACH_FS
3351 if ((f == 0) && (programType == salvageServer)) {
3352 /* we are a salvageserver child */
3353 #ifdef FSSYNC_BUILD_CLIENT
3354 VChildProcReconnectFS_r();
3356 #ifdef SALVSYNC_BUILD_CLIENT
3360 #endif /* AFS_DEMAND_ATTACH_FS */
3361 #endif /* !AFS_NT40_ENV */
3371 #ifdef AFS_DEMAND_ATTACH_FS
3372 if (programType == salvageServer) {
3373 #ifdef SALVSYNC_BUILD_CLIENT
3376 #ifdef FSSYNC_BUILD_CLIENT
3380 #endif /* AFS_DEMAND_ATTACH_FS */
3383 if (main_thread != pthread_self())
3384 pthread_exit((void *)code);
3397 pid = wait(&status);
3399 if (WCOREDUMP(status))
3400 Log("\"%s\" core dumped!\n", prog);
3401 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3407 TimeStamp(time_t clock, int precision)
3410 static char timestamp[20];
3411 lt = localtime(&clock);
3413 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
3415 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3420 CheckLogFile(char * log_path)
3422 char oldSlvgLog[AFSDIR_PATH_MAX];
3424 #ifndef AFS_NT40_ENV
3431 strcpy(oldSlvgLog, log_path);
3432 strcat(oldSlvgLog, ".old");
3434 renamefile(log_path, oldSlvgLog);
3435 logFile = afs_fopen(log_path, "a");
3437 if (!logFile) { /* still nothing, use stdout */
3441 #ifndef AFS_NAMEI_ENV
3442 AFS_DEBUG_IOPS_LOG(logFile);
3447 #ifndef AFS_NT40_ENV
3449 TimeStampLogFile(char * log_path)
3451 char stampSlvgLog[AFSDIR_PATH_MAX];
3456 lt = localtime(&now);
3457 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3458 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3459 log_path, lt->tm_year + 1900,
3460 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3463 /* try to link the logfile to a timestamped filename */
3464 /* if it fails, oh well, nothing we can do */
3465 link(log_path, stampSlvgLog);
3474 #ifndef AFS_NT40_ENV
3476 printf("Can't show log since using syslog.\n");
3485 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3488 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3491 while (fgets(line, sizeof(line), logFile))
3498 Log(const char *format, ...)
3504 va_start(args, format);
3505 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3507 #ifndef AFS_NT40_ENV
3509 syslog(LOG_INFO, "%s", tmp);
3513 gettimeofday(&now, 0);
3514 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3520 Abort(const char *format, ...)
3525 va_start(args, format);
3526 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3528 #ifndef AFS_NT40_ENV
3530 syslog(LOG_INFO, "%s", tmp);
3534 fprintf(logFile, "%s", tmp);
3549 p = (char *)malloc(strlen(s) + 1);
3555 /* Remove the FORCESALVAGE file */
3557 RemoveTheForce(char *path)
3560 struct afs_stat force; /* so we can use afs_stat to find it */
3561 strcpy(target,path);
3562 strcat(target,"/FORCESALVAGE");
3563 if (!Testing && ForceSalvage) {
3564 if (afs_stat(target,&force) == 0) unlink(target);
3568 #ifndef AFS_AIX32_ENV
3570 * UseTheForceLuke - see if we can use the force
3573 UseTheForceLuke(char *path)
3575 struct afs_stat force;
3577 strcpy(target,path);
3578 strcat(target,"/FORCESALVAGE");
3580 return (afs_stat(target, &force) == 0);
3584 * UseTheForceLuke - see if we can use the force
3587 * The VRMIX fsck will not muck with the filesystem it is supposedly
3588 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
3589 * muck directly with the root inode, which is within the normal
3591 * ListViceInodes() has a side effect of setting ForceSalvage if
3592 * it detects a need, based on root inode examination.
3595 UseTheForceLuke(char *path)
3598 return 0; /* sorry OB1 */
3603 /* NT support routines */
3605 static char execpathname[MAX_PATH];
3607 nt_SalvagePartition(char *partName, int jobn)
3612 if (!*execpathname) {
3613 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3614 if (!n || n == 1023)
3617 job.cj_magic = SALVAGER_MAGIC;
3618 job.cj_number = jobn;
3619 (void)strcpy(job.cj_part, partName);
3620 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3625 nt_SetupPartitionSalvage(void *datap, int len)
3627 childJob_t *jobp = (childJob_t *) datap;
3628 char logname[AFSDIR_PATH_MAX];
3630 if (len != sizeof(childJob_t))
3632 if (jobp->cj_magic != SALVAGER_MAGIC)
3637 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3639 logFile = afs_fopen(logname, "w");
3647 #endif /* AFS_NT40_ENV */