2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #include <afsconfig.h>
87 #include <afs/param.h>
101 #include <WINNT/afsevent.h>
103 #include <sys/param.h>
104 #include <sys/file.h>
106 #include <sys/time.h>
107 #endif /* ITIMER_REAL */
109 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
110 #define WCOREDUMP(x) (x & 0200)
113 #include <afs/afsint.h>
114 #include <afs/assert.h>
115 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
116 #if defined(AFS_VFSINCL_ENV)
117 #include <sys/vnode.h>
119 #include <sys/fs/ufs_inode.h>
121 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
122 #include <ufs/ufs/dinode.h>
123 #include <ufs/ffs/fs.h>
125 #include <ufs/inode.h>
128 #else /* AFS_VFSINCL_ENV */
130 #include <ufs/inode.h>
131 #else /* AFS_OSF_ENV */
132 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
133 #include <sys/inode.h>
136 #endif /* AFS_VFSINCL_ENV */
137 #endif /* AFS_SGI_ENV */
140 #include <sys/lockf.h>
144 #include <checklist.h>
146 #if defined(AFS_SGI_ENV)
151 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
154 #include <sys/mnttab.h>
155 #include <sys/mntent.h>
160 #endif /* AFS_SGI_ENV */
161 #endif /* AFS_HPUX_ENV */
166 #include <afs/osi_inode.h>
169 #include <afs/afsutil.h>
170 #include <afs/fileutil.h>
171 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
179 #include <afs/afssyscalls.h>
183 #include "partition.h"
184 #include "daemon_com.h"
186 #include "salvsync.h"
187 #include "viceinode.h"
189 #include "volinodes.h" /* header magic number, etc. stuff */
190 #include "vol-salvage.h"
195 /*@+fcnmacros +macrofcndecl@*/
198 extern off64_t afs_lseek(int FD, off64_t O, int F);
199 #endif /*S_SPLINT_S */
200 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
201 #define afs_stat stat64
202 #define afs_fstat fstat64
203 #define afs_open open64
204 #define afs_fopen fopen64
205 #else /* !O_LARGEFILE */
207 extern off_t afs_lseek(int FD, off_t O, int F);
208 #endif /*S_SPLINT_S */
209 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
210 #define afs_stat stat
211 #define afs_fstat fstat
212 #define afs_open open
213 #define afs_fopen fopen
214 #endif /* !O_LARGEFILE */
215 /*@=fcnmacros =macrofcndecl@*/
218 extern void *calloc();
220 static char *TimeStamp(time_t clock, int precision);
223 int debug; /* -d flag */
224 int Testing = 0; /* -n flag */
225 int ListInodeOption; /* -i flag */
226 int ShowRootFiles; /* -r flag */
227 int RebuildDirs; /* -sal flag */
228 int Parallel = 4; /* -para X flag */
229 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
230 int forceR = 0; /* -b flag */
231 int ShowLog = 0; /* -showlog flag */
232 int ShowSuid = 0; /* -showsuid flag */
233 int ShowMounts = 0; /* -showmounts flag */
234 int orphans = ORPH_IGNORE; /* -orphans option */
238 int useSyslog = 0; /* -syslog flag */
239 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
242 #define MAXPARALLEL 32
244 int OKToZap; /* -o flag */
245 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
246 * in the volume header */
248 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
250 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
252 Device fileSysDevice; /* The device number of the current
253 * partition being salvaged */
257 char *fileSysPath; /* The path of the mounted partition currently
258 * being salvaged, i.e. the directory
259 * containing the volume headers */
261 char *fileSysPathName; /* NT needs this to make name pretty in log. */
262 IHandle_t *VGLinkH; /* Link handle for current volume group. */
263 int VGLinkH_cnt; /* # of references to lnk handle. */
264 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
266 char *fileSysDeviceName; /* The block device where the file system
267 * being salvaged was mounted */
268 char *filesysfulldev;
270 int VolumeChanged; /* Set by any routine which would change the volume in
271 * a way which would require callback is to be broken if the
272 * volume was put back on line by an active file server */
274 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
276 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
277 int inodeFd; /* File descriptor for inode file */
280 struct VnodeInfo vnodeInfo[nVNODECLASSES];
283 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
284 int nVolumes; /* Number of volumes (read-write and read-only)
285 * in volume summary */
287 extern char * tmpdir = 0;
291 /* Child job this process is running. */
292 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
293 #endif /* AFS_NT40_ENV */
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 */
328 char *save_args[MAX_ARGS];
330 pthread_t main_thread;
334 /* Get the salvage lock if not already held. Hold until process exits. */
336 ObtainSalvageLock(void)
342 (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
343 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
344 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
346 "salvager: There appears to be another salvager running! Aborted.\n");
351 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
352 if (salvageLock < 0) {
354 "salvager: can't open salvage lock file %s, aborting\n",
355 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
358 #ifdef AFS_DARWIN_ENV
359 if (flock(salvageLock, LOCK_EX) == -1) {
361 if (lockf(salvageLock, F_LOCK, 0) == -1) {
364 "salvager: There appears to be another salvager running! Aborted.\n");
371 #ifdef AFS_SGI_XFS_IOPS_ENV
372 /* Check if the given partition is mounted. For XFS, the root inode is not a
373 * constant. So we check the hard way.
376 IsPartitionMounted(char *part)
379 struct mntent *mntent;
381 assert(mntfp = setmntent(MOUNTED, "r"));
382 while (mntent = getmntent(mntfp)) {
383 if (!strcmp(part, mntent->mnt_dir))
388 return mntent ? 1 : 1;
391 /* Check if the given inode is the root of the filesystem. */
392 #ifndef AFS_SGI_XFS_IOPS_ENV
394 IsRootInode(struct afs_stat *status)
397 * The root inode is not a fixed value in XFS partitions. So we need to
398 * see if the partition is in the list of mounted partitions. This only
399 * affects the SalvageFileSys path, so we check there.
401 return (status->st_ino == ROOTINODE);
406 #ifndef AFS_NAMEI_ENV
407 /* We don't want to salvage big files filesystems, since we can't put volumes on
411 CheckIfBigFilesFS(char *mountPoint, char *devName)
413 struct superblock fs;
416 if (strncmp(devName, "/dev/", 5)) {
417 (void)sprintf(name, "/dev/%s", devName);
419 (void)strcpy(name, devName);
422 if (ReadSuper(&fs, name) < 0) {
423 Log("Unable to read superblock. Not salvaging partition %s.\n",
427 if (IsBigFilesFileSystem(&fs)) {
428 Log("Partition %s is a big files filesystem, not salvaging.\n",
438 #define HDSTR "\\Device\\Harddisk"
439 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
441 SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
446 static int dowarn = 1;
448 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
450 if (strncmp(res, HDSTR, HDLEN)) {
453 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
454 res, HDSTR, p1->devName);
458 d1 = atoi(&res[HDLEN]);
460 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
462 if (strncmp(res, HDSTR, HDLEN)) {
465 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
466 res, HDSTR, p2->devName);
470 d2 = atoi(&res[HDLEN]);
475 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
478 /* This assumes that two partitions with the same device number divided by
479 * PartsPerDisk are on the same disk.
482 SalvageFileSysParallel(struct DiskPartition *partP)
485 struct DiskPartition *partP;
486 int pid; /* Pid for this job */
487 int jobnumb; /* Log file job number */
488 struct job *nextjob; /* Next partition on disk to salvage */
490 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
491 struct job *thisjob = 0;
492 static int numjobs = 0;
493 static int jobcount = 0;
499 char logFileName[256];
503 /* We have a partition to salvage. Copy it into thisjob */
504 thisjob = (struct job *)malloc(sizeof(struct job));
506 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
509 memset(thisjob, 0, sizeof(struct job));
510 thisjob->partP = partP;
511 thisjob->jobnumb = jobcount;
513 } else if (jobcount == 0) {
514 /* We are asking to wait for all jobs (partp == 0), yet we never
517 Log("No file system partitions named %s* found; not salvaged\n",
518 VICE_PARTITION_PREFIX);
522 if (debug || Parallel == 1) {
524 SalvageFileSys(thisjob->partP, 0);
531 /* Check to see if thisjob is for a disk that we are already
532 * salvaging. If it is, link it in as the next job to do. The
533 * jobs array has 1 entry per disk being salvages. numjobs is
534 * the total number of disks currently being salvaged. In
535 * order to keep thejobs array compact, when a disk is
536 * completed, the hightest element in the jobs array is moved
537 * down to now open slot.
539 for (j = 0; j < numjobs; j++) {
540 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
541 /* On same disk, add it to this list and return */
542 thisjob->nextjob = jobs[j]->nextjob;
543 jobs[j]->nextjob = thisjob;
550 /* Loop until we start thisjob or until all existing jobs are finished */
551 while (thisjob || (!partP && (numjobs > 0))) {
552 startjob = -1; /* No new job to start */
554 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
555 /* Either the max jobs are running or we have to wait for all
556 * the jobs to finish. In either case, we wait for at least one
557 * job to finish. When it's done, clean up after it.
559 pid = wait(&wstatus);
561 for (j = 0; j < numjobs; j++) { /* Find which job it is */
562 if (pid == jobs[j]->pid)
566 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
567 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
570 numjobs--; /* job no longer running */
571 oldjob = jobs[j]; /* remember */
572 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
573 free(oldjob); /* free the old job */
575 /* If there is another partition on the disk to salvage, then
576 * say we will start it (startjob). If not, then put thisjob there
577 * and say we will start it.
579 if (jobs[j]) { /* Another partitions to salvage */
580 startjob = j; /* Will start it */
581 } else { /* There is not another partition to salvage */
583 jobs[j] = thisjob; /* Add thisjob */
585 startjob = j; /* Will start it */
587 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
588 startjob = -1; /* Don't start it - already running */
592 /* We don't have to wait for a job to complete */
594 jobs[numjobs] = thisjob; /* Add this job */
596 startjob = numjobs; /* Will start it */
600 /* Start up a new salvage job on a partition in job slot "startjob" */
601 if (startjob != -1) {
603 Log("Starting salvage of file system partition %s\n",
604 jobs[startjob]->partP->name);
606 /* For NT, we not only fork, but re-exec the salvager. Pass in the
607 * commands and pass the child job number via the data path.
610 nt_SalvagePartition(jobs[startjob]->partP->name,
611 jobs[startjob]->jobnumb);
612 jobs[startjob]->pid = pid;
617 jobs[startjob]->pid = pid;
623 for (fd = 0; fd < 16; fd++)
630 openlog("salvager", LOG_PID, useSyslogFacility);
634 (void)afs_snprintf(logFileName, sizeof logFileName,
636 AFSDIR_SERVER_SLVGLOG_FILEPATH,
637 jobs[startjob]->jobnumb);
638 logFile = afs_fopen(logFileName, "w");
643 SalvageFileSys1(jobs[startjob]->partP, 0);
648 } /* while ( thisjob || (!partP && numjobs > 0) ) */
650 /* If waited for all jobs to complete, now collect log files and return */
652 if (!useSyslog) /* if syslogging - no need to collect */
655 for (i = 0; i < jobcount; i++) {
656 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
657 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
658 if ((passLog = afs_fopen(logFileName, "r"))) {
659 while (fgets(buf, sizeof(buf), passLog)) {
664 (void)unlink(logFileName);
673 SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
675 if (!canfork || debug || Fork() == 0) {
676 SalvageFileSys1(partP, singleVolumeNumber);
677 if (canfork && !debug) {
682 Wait("SalvageFileSys");
686 get_DevName(char *pbuffer, char *wpath)
688 char pbuf[128], *ptr;
689 strcpy(pbuf, pbuffer);
690 ptr = (char *)strrchr(pbuf, '/');
696 ptr = (char *)strrchr(pbuffer, '/');
698 strcpy(pbuffer, ptr + 1);
705 SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
708 char inodeListPath[256];
709 static char tmpDevName[100];
710 static char wpath[100];
711 struct VolumeSummary *vsp, *esp;
714 fileSysPartition = partP;
715 fileSysDevice = fileSysPartition->device;
716 fileSysPathName = VPartitionPath(fileSysPartition);
719 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
720 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
721 name = partP->devName;
723 fileSysPath = fileSysPathName;
724 strcpy(tmpDevName, partP->devName);
725 name = get_DevName(tmpDevName, wpath);
726 fileSysDeviceName = name;
727 filesysfulldev = wpath;
730 VLockPartition(partP->name);
731 if (singleVolumeNumber || ForceSalvage)
734 ForceSalvage = UseTheForceLuke(fileSysPath);
736 if (singleVolumeNumber) {
737 /* salvageserver already setup fssync conn for us */
738 if ((programType != salvageServer) && !VConnectFS()) {
739 Abort("Couldn't connect to file server\n");
741 AskOffline(singleVolumeNumber);
744 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
745 partP->name, name, (Testing ? "(READONLY mode)" : ""));
747 Log("***Forced salvage of all volumes on this partition***\n");
752 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
759 assert((dirp = opendir(fileSysPath)) != NULL);
760 while ((dp = readdir(dirp))) {
761 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
762 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
764 Log("Removing old salvager temp files %s\n", dp->d_name);
765 strcpy(npath, fileSysPath);
767 strcat(npath, dp->d_name);
773 tdir = (tmpdir ? tmpdir : fileSysPath);
775 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
776 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
778 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
781 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
782 unlink(inodeListPath);
786 /* Using nt_unlink here since we're really using the delete on close
787 * semantics of unlink. In most places in the salvager, we really do
788 * mean to unlink the file at that point. Those places have been
789 * modified to actually do that so that the NT crt can be used there.
792 _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
793 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
795 inodeFd = afs_open(inodeListPath, O_RDONLY);
796 unlink(inodeListPath);
799 Abort("Temporary file %s is missing...\n", inodeListPath);
800 if (ListInodeOption) {
804 /* enumerate volumes in the partition.
805 * figure out sets of read-only + rw volumes.
806 * salvage each set, read-only volumes first, then read-write.
807 * Fix up inodes on last volume in set (whether it is read-write
810 GetVolumeSummary(singleVolumeNumber);
812 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
813 i < nVolumesInInodeFile; i = j) {
814 VolumeId rwvid = inodeSummary[i].RWvolumeId;
816 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
818 VolumeId vid = inodeSummary[j].volumeId;
819 struct VolumeSummary *tsp;
820 /* Scan volume list (from partition root directory) looking for the
821 * current rw volume number in the volume list from the inode scan.
822 * If there is one here that is not in the inode volume list,
824 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
826 DeleteExtraVolumeHeaderFile(vsp);
828 /* Now match up the volume summary info from the root directory with the
829 * entry in the volume list obtained from scanning inodes */
830 inodeSummary[j].volSummary = NULL;
831 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
832 if (tsp->header.id == vid) {
833 inodeSummary[j].volSummary = tsp;
839 /* Salvage the group of volumes (several read-only + 1 read/write)
840 * starting with the current read-only volume we're looking at.
842 SalvageVolumeGroup(&inodeSummary[i], j - i);
845 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
846 for (; vsp < esp; vsp++) {
848 DeleteExtraVolumeHeaderFile(vsp);
851 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
852 RemoveTheForce(fileSysPath);
854 if (!Testing && singleVolumeNumber) {
855 AskOnline(singleVolumeNumber, fileSysPartition->name);
857 /* Step through the volumeSummary list and set all volumes on-line.
858 * The volumes were taken off-line in GetVolumeSummary.
860 for (j = 0; j < nVolumes; j++) {
861 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
865 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
866 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
869 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
873 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
876 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
878 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 int volume = ip->u.vnode.volumeId;
983 int rwvolume = volume;
984 register 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, VolumeId 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 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1207 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1208 if (!singleVolumeNumber) {
1209 while ((dp = readdir(dirp))) {
1210 char *p = dp->d_name;
1211 p = strrchr(dp->d_name, '.');
1212 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1214 if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
1215 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1216 == sizeof(diskHeader)
1217 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1218 DiskToVolumeHeader(&vs.header, &diskHeader);
1226 dirp = opendir("."); /* No rewinddir for NT */
1233 (struct VolumeSummary *)malloc(nvols *
1234 sizeof(struct VolumeSummary));
1237 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1238 assert(volumeSummaryp != NULL);
1241 vsp = volumeSummaryp;
1242 while ((dp = readdir(dirp))) {
1243 char *p = dp->d_name;
1244 p = strrchr(dp->d_name, '.');
1245 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1248 if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
1249 || read(fd, &diskHeader, sizeof(diskHeader))
1250 != sizeof(diskHeader)
1251 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1256 if (!singleVolumeNumber) {
1258 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1263 char nameShouldBe[64];
1264 DiskToVolumeHeader(&vsp->header, &diskHeader);
1265 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1266 && vsp->header.parent != singleVolumeNumber) {
1267 Log("%u is a read-only volume; not salvaged\n",
1268 singleVolumeNumber);
1271 if (!singleVolumeNumber
1272 || (vsp->header.id == singleVolumeNumber
1273 || vsp->header.parent == singleVolumeNumber)) {
1274 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1275 VFORMAT, vsp->header.id);
1276 if (singleVolumeNumber)
1277 AskOffline(vsp->header.id);
1278 if (strcmp(nameShouldBe, dp->d_name)) {
1280 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 " : ""));
1284 vsp->fileName = ToString(dp->d_name);
1294 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1298 /* Find the link table. This should be associated with the RW volume or, if
1299 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1302 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1303 struct ViceInodeInfo *allInodes)
1306 struct ViceInodeInfo *ip;
1308 for (i = 0; i < nVols; i++) {
1309 ip = allInodes + isp[i].index;
1310 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1311 if (ip[j].u.special.type == VI_LINKTABLE)
1312 return ip[j].inodeNumber;
1319 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1321 struct versionStamp version;
1324 if (!VALID_INO(ino))
1326 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1327 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1328 if (!VALID_INO(ino))
1330 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1331 isp->RWvolumeId, errno);
1332 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1333 fdP = IH_OPEN(VGLinkH);
1335 Abort("Can't open link table for volume %u (error = %d)\n",
1336 isp->RWvolumeId, errno);
1338 if (FDH_TRUNC(fdP, 0) < 0)
1339 Abort("Can't truncate link table for volume %u (error = %d)\n",
1340 isp->RWvolumeId, errno);
1342 version.magic = LINKTABLEMAGIC;
1343 version.version = LINKTABLEVERSION;
1345 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1347 Abort("Can't truncate link table for volume %u (error = %d)\n",
1348 isp->RWvolumeId, errno);
1350 FDH_REALLYCLOSE(fdP);
1352 /* If the volume summary exits (i.e., the V*.vol header file exists),
1353 * then set this inode there as well.
1355 if (isp->volSummary)
1356 isp->volSummary->header.linkTable = ino;
1365 SVGParms_t *parms = (SVGParms_t *) arg;
1366 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1371 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1374 pthread_attr_t tattr;
1378 /* Initialize per volume global variables, even if later code does so */
1382 memset(&VolInfo, 0, sizeof(VolInfo));
1384 parms.svgp_inodeSummaryp = isp;
1385 parms.svgp_count = nVols;
1386 code = pthread_attr_init(&tattr);
1388 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1392 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1394 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1397 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1399 Log("Failed to create thread to salvage volume group %u\n",
1403 (void)pthread_join(tid, NULL);
1405 #endif /* AFS_NT40_ENV */
1408 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1410 struct ViceInodeInfo *inodes, *allInodes, *ip;
1411 int i, totalInodes, size, salvageTo;
1415 int dec_VGLinkH = 0;
1417 FdHandle_t *fdP = NULL;
1420 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1421 && isp->nSpecialInodes > 0);
1422 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1423 if (!ForceSalvage && QuickCheck(isp, nVols))
1426 if (ShowMounts && !haveRWvolume)
1428 if (canfork && !debug && Fork() != 0) {
1429 (void)Wait("Salvage volume group");
1432 for (i = 0, totalInodes = 0; i < nVols; i++)
1433 totalInodes += isp[i].nInodes;
1434 size = totalInodes * sizeof(struct ViceInodeInfo);
1435 inodes = (struct ViceInodeInfo *)malloc(size);
1436 allInodes = inodes - isp->index; /* this would the base of all the inodes
1437 * for the partition, if all the inodes
1438 * had been read into memory */
1440 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1442 assert(read(inodeFd, inodes, size) == size);
1444 /* Don't try to salvage a read write volume if there isn't one on this
1446 salvageTo = haveRWvolume ? 0 : 1;
1448 #ifdef AFS_NAMEI_ENV
1449 ino = FindLinkHandle(isp, nVols, allInodes);
1450 if (VALID_INO(ino)) {
1451 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1452 fdP = IH_OPEN(VGLinkH);
1454 if (!VALID_INO(ino) || fdP == NULL) {
1455 Log("%s link table for volume %u.\n",
1456 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1458 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1460 CreateLinkTable(isp, ino);
1464 FDH_REALLYCLOSE(fdP);
1466 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1469 /* Salvage in reverse order--read/write volume last; this way any
1470 * Inodes not referenced by the time we salvage the read/write volume
1471 * can be picked up by the read/write volume */
1472 /* ACTUALLY, that's not done right now--the inodes just vanish */
1473 for (i = nVols - 1; i >= salvageTo; i--) {
1475 struct InodeSummary *lisp = &isp[i];
1476 #ifdef AFS_NAMEI_ENV
1477 /* If only the RO is present on this partition, the link table
1478 * shows up as a RW volume special file. Need to make sure the
1479 * salvager doesn't try to salvage the non-existent RW.
1481 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1482 /* If this only special inode is the link table, continue */
1483 if (inodes->u.special.type == VI_LINKTABLE) {
1490 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1491 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1492 /* Check inodes twice. The second time do things seriously. This
1493 * way the whole RO volume can be deleted, below, if anything goes wrong */
1494 for (check = 1; check >= 0; check--) {
1496 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1498 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1499 if (rw && deleteMe) {
1500 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1501 * volume won't be called */
1507 if (rw && check == 1)
1509 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1510 MaybeZapVolume(lisp, "Vnode index", 0, check);
1516 /* Fix actual inode counts */
1518 Log("totalInodes %d\n",totalInodes);
1519 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1520 static int TraceBadLinkCounts = 0;
1521 #ifdef AFS_NAMEI_ENV
1522 if (VGLinkH->ih_ino == ip->inodeNumber) {
1523 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1524 VGLinkH_p1 = ip->u.param[0];
1525 continue; /* Deal with this last. */
1528 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1529 TraceBadLinkCounts--; /* Limit reports, per volume */
1530 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]);
1532 while (ip->linkCount > 0) {
1533 /* below used to assert, not break */
1535 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1536 Log("idec failed. inode %s errno %d\n",
1537 PrintInode(NULL, ip->inodeNumber), errno);
1543 while (ip->linkCount < 0) {
1544 /* these used to be asserts */
1546 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1547 Log("iinc failed. inode %s errno %d\n",
1548 PrintInode(NULL, ip->inodeNumber), errno);
1555 #ifdef AFS_NAMEI_ENV
1556 while (dec_VGLinkH > 0) {
1557 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1558 Log("idec failed on link table, errno = %d\n", errno);
1562 while (dec_VGLinkH < 0) {
1563 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1564 Log("iinc failed on link table, errno = %d\n", errno);
1571 /* Directory consistency checks on the rw volume */
1573 SalvageVolume(isp, VGLinkH);
1574 IH_RELEASE(VGLinkH);
1576 if (canfork && !debug) {
1583 QuickCheck(register struct InodeSummary *isp, int nVols)
1585 /* Check headers BEFORE forking */
1589 for (i = 0; i < nVols; i++) {
1590 struct VolumeSummary *vs = isp[i].volSummary;
1591 VolumeDiskData volHeader;
1593 /* Don't salvage just because phantom rw volume is there... */
1594 /* (If a read-only volume exists, read/write inodes must also exist) */
1595 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1599 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1600 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1601 == sizeof(volHeader)
1602 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1603 && volHeader.dontSalvage == DONT_SALVAGE
1604 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1605 if (volHeader.inUse == 1) {
1606 volHeader.inUse = 0;
1607 volHeader.inService = 1;
1609 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1610 != sizeof(volHeader)) {
1626 /* SalvageVolumeHeaderFile
1628 * Salvage the top level V*.vol header file. Make sure the special files
1629 * exist and that there are no duplicates.
1631 * Calls SalvageHeader for each possible type of volume special file.
1635 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1636 register struct ViceInodeInfo *inodes, int RW,
1637 int check, int *deleteMe)
1641 register struct ViceInodeInfo *ip;
1642 int allinodesobsolete = 1;
1643 struct VolumeDiskHeader diskHeader;
1647 memset(&tempHeader, 0, sizeof(tempHeader));
1648 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1649 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1650 tempHeader.id = isp->volumeId;
1651 tempHeader.parent = isp->RWvolumeId;
1652 /* Check for duplicates (inodes are sorted by type field) */
1653 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1654 ip = &inodes[isp->index + i];
1655 if (ip->u.special.type == (ip + 1)->u.special.type) {
1657 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1661 for (i = 0; i < isp->nSpecialInodes; i++) {
1662 ip = &inodes[isp->index + i];
1663 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1665 Log("Rubbish header inode\n");
1668 Log("Rubbish header inode; deleted\n");
1669 } else if (!stuff[ip->u.special.type - 1].obsolete) {
1670 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1671 if (!check && ip->u.special.type != VI_LINKTABLE)
1672 ip->linkCount--; /* Keep the inode around */
1673 allinodesobsolete = 0;
1677 if (allinodesobsolete) {
1684 VGLinkH_cnt++; /* one for every header. */
1686 if (!RW && !check && isp->volSummary) {
1687 ClearROInUseBit(isp->volSummary);
1691 for (i = 0; i < MAXINODETYPE; i++) {
1692 if (stuff[i].inodeType == VI_LINKTABLE) {
1693 /* Gross hack: SalvageHeader does a bcmp on the volume header.
1694 * And we may have recreated the link table earlier, so set the
1695 * RW header as well.
1697 if (VALID_INO(VGLinkH->ih_ino)) {
1698 *stuff[i].inode = VGLinkH->ih_ino;
1702 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1706 if (isp->volSummary == NULL) {
1708 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1710 Log("No header file for volume %u\n", isp->volumeId);
1714 Log("No header file for volume %u; %screating %s/%s\n",
1715 isp->volumeId, (Testing ? "it would have been " : ""),
1716 fileSysPathName, name);
1717 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
1718 assert(headerFd != -1);
1719 isp->volSummary = (struct VolumeSummary *)
1720 malloc(sizeof(struct VolumeSummary));
1721 isp->volSummary->fileName = ToString(name);
1724 /* hack: these two fields are obsolete... */
1725 isp->volSummary->header.volumeAcl = 0;
1726 isp->volSummary->header.volumeMountTable = 0;
1729 (&isp->volSummary->header, &tempHeader,
1730 sizeof(struct VolumeHeader))) {
1731 /* We often remove the name before calling us, so we make a fake one up */
1732 if (isp->volSummary->fileName) {
1733 strcpy(name, isp->volSummary->fileName);
1735 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1736 isp->volSummary->fileName = ToString(name);
1739 Log("Header file %s is damaged or no longer valid%s\n", name,
1740 (check ? "" : "; repairing"));
1744 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
1745 assert(headerFd != -1);
1749 memcpy(&isp->volSummary->header, &tempHeader,
1750 sizeof(struct VolumeHeader));
1753 Log("It would have written a new header file for volume %u\n",
1756 VolumeHeaderToDisk(&diskHeader, &tempHeader);
1757 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1758 != sizeof(struct VolumeDiskHeader)) {
1759 Log("Couldn't rewrite volume header file!\n");
1766 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1767 isp->volSummary->header.volumeInfo);
1772 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1776 VolumeDiskData volumeInfo;
1777 struct versionStamp fileHeader;
1786 #ifndef AFS_NAMEI_ENV
1787 if (sp->inodeType == VI_LINKTABLE)
1790 if (*(sp->inode) == 0) {
1792 Log("Missing inode in volume header (%s)\n", sp->description);
1796 Log("Missing inode in volume header (%s); %s\n", sp->description,
1797 (Testing ? "it would have recreated it" : "recreating"));
1800 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1801 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1802 if (!VALID_INO(*(sp->inode)))
1804 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1805 sp->description, errno);
1810 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1811 fdP = IH_OPEN(specH);
1812 if (OKToZap && (fdP == NULL) && BadError(errno)) {
1813 /* bail out early and destroy the volume */
1815 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1822 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1823 sp->description, errno);
1826 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1827 || header.fileHeader.magic != sp->stamp.magic)) {
1829 Log("Part of the header (%s) is corrupted\n", sp->description);
1830 FDH_REALLYCLOSE(fdP);
1834 Log("Part of the header (%s) is corrupted; recreating\n",
1838 if (sp->inodeType == VI_VOLINFO
1839 && header.volumeInfo.destroyMe == DESTROY_ME) {
1842 FDH_REALLYCLOSE(fdP);
1846 if (recreate && !Testing) {
1849 ("Internal error: recreating volume header (%s) in check mode\n",
1851 code = FDH_TRUNC(fdP, 0);
1853 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1854 sp->description, errno);
1856 /* The following code should be moved into vutil.c */
1857 if (sp->inodeType == VI_VOLINFO) {
1859 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1860 header.volumeInfo.stamp = sp->stamp;
1861 header.volumeInfo.id = isp->volumeId;
1862 header.volumeInfo.parentId = isp->RWvolumeId;
1863 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1864 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1865 isp->volumeId, isp->volumeId);
1866 header.volumeInfo.inService = 0;
1867 header.volumeInfo.blessed = 0;
1868 /* The + 1000 is a hack in case there are any files out in venus caches */
1869 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1870 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
1871 header.volumeInfo.needsCallback = 0;
1872 gettimeofday(&tp, 0);
1873 header.volumeInfo.creationDate = tp.tv_sec;
1874 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1876 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1877 sp->description, errno);
1880 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1881 sizeof(header.volumeInfo));
1882 if (code != sizeof(header.volumeInfo)) {
1885 ("Unable to write volume header file (%s) (errno = %d)\n",
1886 sp->description, errno);
1887 Abort("Unable to write entire volume header file (%s)\n",
1891 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1893 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1894 sp->description, errno);
1896 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1897 if (code != sizeof(sp->stamp)) {
1900 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1901 sp->description, errno);
1903 ("Unable to write entire version stamp in volume header file (%s)\n",
1908 FDH_REALLYCLOSE(fdP);
1910 if (sp->inodeType == VI_VOLINFO) {
1911 VolInfo = header.volumeInfo;
1914 if (VolInfo.updateDate) {
1915 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1917 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1918 (Testing ? "it would have been " : ""), update);
1920 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1922 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1923 VolInfo.id, update);
1933 SalvageVnodes(register struct InodeSummary *rwIsp,
1934 register struct InodeSummary *thisIsp,
1935 register struct ViceInodeInfo *inodes, int check)
1937 int ilarge, ismall, ioffset, RW, nInodes;
1938 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
1941 RW = (rwIsp == thisIsp);
1942 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1944 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1945 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1946 if (check && ismall == -1)
1949 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1950 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1951 return (ilarge == 0 && ismall == 0 ? 0 : -1);
1955 SalvageIndex(Inode ino, VnodeClass class, int RW,
1956 register struct ViceInodeInfo *ip, int nInodes,
1957 struct VolumeSummary *volSummary, int check)
1959 VolumeId volumeNumber;
1960 char buf[SIZEOF_LARGEDISKVNODE];
1961 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1963 StreamHandle_t *file;
1964 struct VnodeClassInfo *vcp;
1966 afs_fsize_t vnodeLength;
1967 int vnodeIndex, nVnodes;
1968 afs_ino_str_t stmp1, stmp2;
1972 volumeNumber = volSummary->header.id;
1973 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
1974 fdP = IH_OPEN(handle);
1975 assert(fdP != NULL);
1976 file = FDH_FDOPEN(fdP, "r+");
1977 assert(file != NULL);
1978 vcp = &VnodeClassInfo[class];
1979 size = OS_SIZE(fdP->fd_fd);
1981 nVnodes = (size / vcp->diskSize) - 1;
1983 assert((nVnodes + 1) * vcp->diskSize == size);
1984 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
1988 for (vnodeIndex = 0;
1989 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
1990 nVnodes--, vnodeIndex++) {
1991 if (vnode->type != vNull) {
1992 int vnodeChanged = 0;
1993 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
1994 /* Log programs that belong to root (potentially suid root);
1995 * don't bother for read-only or backup volumes */
1996 #ifdef notdef /* This is done elsewhere */
1997 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
1998 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);
2000 if (VNDISK_GET_INO(vnode) == 0) {
2002 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2003 memset(vnode, 0, vcp->diskSize);
2007 if (vcp->magic != vnode->vnodeMagic) {
2008 /* bad magic #, probably partially created vnode */
2009 Log("Partially allocated vnode %d deleted.\n",
2011 memset(vnode, 0, vcp->diskSize);
2015 /* ****** Should do a bit more salvage here: e.g. make sure
2016 * vnode type matches what it should be given the index */
2017 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2018 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2019 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2020 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2027 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2028 /* The following doesn't work, because the version number
2029 * is not maintained correctly by the file server */
2030 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2031 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2033 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2039 /* For RW volume, look for vnode with matching inode number;
2040 * if no such match, take the first determined by our sort
2042 register struct ViceInodeInfo *lip = ip;
2043 register int lnInodes = nInodes;
2045 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2046 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2055 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2056 /* "Matching" inode */
2060 vu = vnode->uniquifier;
2061 iu = ip->u.vnode.vnodeUniquifier;
2062 vd = vnode->dataVersion;
2063 id = ip->u.vnode.inodeDataVersion;
2065 * Because of the possibility of the uniquifier overflows (> 4M)
2066 * we compare them modulo the low 22-bits; we shouldn't worry
2067 * about mismatching since they shouldn't to many old
2068 * uniquifiers of the same vnode...
2070 if (IUnique(vu) != IUnique(iu)) {
2072 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2075 vnode->uniquifier = iu;
2076 #ifdef AFS_3DISPARES
2077 vnode->dataVersion = (id >= vd ?
2080 1887437 ? vd : id) :
2083 1887437 ? id : vd));
2085 #if defined(AFS_SGI_EXMAG)
2086 vnode->dataVersion = (id >= vd ?
2089 15099494 ? vd : id) :
2092 15099494 ? id : vd));
2094 vnode->dataVersion = (id > vd ? id : vd);
2095 #endif /* AFS_SGI_EXMAG */
2096 #endif /* AFS_3DISPARES */
2099 /* don't bother checking for vd > id any more, since
2100 * partial file transfers always result in this state,
2101 * and you can't do much else anyway (you've already
2102 * found the best data you can) */
2103 #ifdef AFS_3DISPARES
2104 if (!vnodeIsDirectory(vnodeNumber)
2105 && ((vd < id && (id - vd) < 1887437)
2106 || ((vd > id && (vd - id) > 1887437)))) {
2108 #if defined(AFS_SGI_EXMAG)
2109 if (!vnodeIsDirectory(vnodeNumber)
2110 && ((vd < id && (id - vd) < 15099494)
2111 || ((vd > id && (vd - id) > 15099494)))) {
2113 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2114 #endif /* AFS_SGI_EXMAG */
2117 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2118 vnode->dataVersion = id;
2123 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2126 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);
2128 VNDISK_SET_INO(vnode, ip->inodeNumber);
2133 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);
2135 VNDISK_SET_INO(vnode, ip->inodeNumber);
2138 VNDISK_GET_LEN(vnodeLength, vnode);
2139 if (ip->byteCount != vnodeLength) {
2142 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2147 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2148 VNDISK_SET_LEN(vnode, ip->byteCount);
2152 ip->linkCount--; /* Keep the inode around */
2155 } else { /* no matching inode */
2156 if (VNDISK_GET_INO(vnode) != 0
2157 || vnode->type == vDirectory) {
2158 /* No matching inode--get rid of the vnode */
2160 if (VNDISK_GET_INO(vnode)) {
2162 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2166 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2171 if (VNDISK_GET_INO(vnode)) {
2173 Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime((time_t *) & (vnode->serverModifyTime)));
2177 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime((time_t *) & (vnode->serverModifyTime)));
2179 memset(vnode, 0, vcp->diskSize);
2182 /* Should not reach here becuase we checked for
2183 * (inodeNumber == 0) above. And where we zero the vnode,
2184 * we also goto vnodeDone.
2188 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2192 } /* VNDISK_GET_INO(vnode) != 0 */
2194 assert(!(vnodeChanged && check));
2195 if (vnodeChanged && !Testing) {
2197 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2198 (char *)vnode, vcp->diskSize)
2200 VolumeChanged = 1; /* For break call back */
2211 struct VnodeEssence *
2212 CheckVnodeNumber(VnodeId vnodeNumber)
2215 struct VnodeInfo *vip;
2218 class = vnodeIdToClass(vnodeNumber);
2219 vip = &vnodeInfo[class];
2220 offset = vnodeIdToBitNumber(vnodeNumber);
2221 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2225 CopyOnWrite(register struct DirSummary *dir)
2227 /* Copy the directory unconditionally if we are going to change it:
2228 * not just if was cloned.
2230 struct VnodeDiskObject vnode;
2231 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2232 Inode oldinode, newinode;
2235 if (dir->copied || Testing)
2237 DFlush(); /* Well justified paranoia... */
2240 IH_IREAD(vnodeInfo[vLarge].handle,
2241 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2243 assert(code == sizeof(vnode));
2244 oldinode = VNDISK_GET_INO(&vnode);
2245 /* Increment the version number by a whole lot to avoid problems with
2246 * clients that were promised new version numbers--but the file server
2247 * crashed before the versions were written to disk.
2250 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2251 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2253 assert(VALID_INO(newinode));
2254 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2256 VNDISK_SET_INO(&vnode, newinode);
2258 IH_IWRITE(vnodeInfo[vLarge].handle,
2259 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2261 assert(code == sizeof(vnode));
2263 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2264 fileSysDevice, newinode);
2265 /* Don't delete the original inode right away, because the directory is
2266 * still being scanned.
2272 * This function should either successfully create a new dir, or give up
2273 * and leave things the way they were. In particular, if it fails to write
2274 * the new dir properly, it should return w/o changing the reference to the
2278 CopyAndSalvage(register struct DirSummary *dir)
2280 struct VnodeDiskObject vnode;
2281 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2282 Inode oldinode, newinode;
2284 register afs_int32 code;
2285 afs_int32 parentUnique = 1;
2286 struct VnodeEssence *vnodeEssence;
2290 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2292 IH_IREAD(vnodeInfo[vLarge].handle,
2293 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2295 assert(code == sizeof(vnode));
2296 oldinode = VNDISK_GET_INO(&vnode);
2297 /* Increment the version number by a whole lot to avoid problems with
2298 * clients that were promised new version numbers--but the file server
2299 * crashed before the versions were written to disk.
2302 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2303 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2305 assert(VALID_INO(newinode));
2306 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2308 /* Assign . and .. vnode numbers from dir and vnode.parent.
2309 * The uniquifier for . is in the vnode.
2310 * The uniquifier for .. might be set to a bogus value of 1 and
2311 * the salvager will later clean it up.
2313 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2314 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2317 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2319 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2324 /* didn't really build the new directory properly, let's just give up. */
2325 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2327 Log("Directory salvage returned code %d, continuing.\n", code);
2330 Log("Checking the results of the directory salvage...\n");
2331 if (!DirOK(&newdir)) {
2332 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2333 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2338 VNDISK_SET_INO(&vnode, newinode);
2339 VNDISK_SET_LEN(&vnode, Length(&newdir));
2341 IH_IWRITE(vnodeInfo[vLarge].handle,
2342 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2344 assert(code == sizeof(vnode));
2346 nt_sync(fileSysDevice);
2348 sync(); /* this is slow, but hopefully rarely called. We don't have
2349 * an open FD on the file itself to fsync.
2352 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2354 dir->dirHandle = newdir;
2358 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2361 struct VnodeEssence *vnodeEssence;
2362 afs_int32 dirOrphaned, todelete;
2364 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2366 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2367 if (vnodeEssence == NULL) {
2369 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2373 assert(Delete(&dir->dirHandle, name) == 0);
2378 #ifndef AFS_NAMEI_ENV
2379 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2380 * mount inode for the partition. If this inode were deleted, it would crash
2383 if (vnodeEssence->InodeNumber == 0) {
2384 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"));
2387 assert(Delete(&dir->dirHandle, name) == 0);
2394 if (!(vnodeNumber & 1) && !Showmode
2395 && !(vnodeEssence->count || vnodeEssence->unique
2396 || vnodeEssence->modeBits)) {
2397 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2398 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2399 vnodeNumber, unique,
2400 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2405 assert(Delete(&dir->dirHandle, name) == 0);
2411 /* Check if the Uniquifiers match. If not, change the directory entry
2412 * so its unique matches the vnode unique. Delete if the unique is zero
2413 * or if the directory is orphaned.
2415 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2416 if (!vnodeEssence->unique
2417 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2418 /* This is an orphaned directory. Don't delete the . or ..
2419 * entry. Otherwise, it will get created in the next
2420 * salvage and deleted again here. So Just skip it.
2425 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2428 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")));
2432 fid.Vnode = vnodeNumber;
2433 fid.Unique = vnodeEssence->unique;
2435 assert(Delete(&dir->dirHandle, name) == 0);
2437 assert(Create(&dir->dirHandle, name, &fid) == 0);
2440 return; /* no need to continue */
2443 if (strcmp(name, ".") == 0) {
2444 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2447 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2450 assert(Delete(&dir->dirHandle, ".") == 0);
2451 fid.Vnode = dir->vnodeNumber;
2452 fid.Unique = dir->unique;
2453 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2456 vnodeNumber = fid.Vnode; /* Get the new Essence */
2457 unique = fid.Unique;
2458 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2461 } else if (strcmp(name, "..") == 0) {
2464 struct VnodeEssence *dotdot;
2465 pa.Vnode = dir->parent;
2466 dotdot = CheckVnodeNumber(pa.Vnode);
2467 assert(dotdot != NULL); /* XXX Should not be assert */
2468 pa.Unique = dotdot->unique;
2470 pa.Vnode = dir->vnodeNumber;
2471 pa.Unique = dir->unique;
2473 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2475 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2478 assert(Delete(&dir->dirHandle, "..") == 0);
2479 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2482 vnodeNumber = pa.Vnode; /* Get the new Essence */
2484 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2486 dir->haveDotDot = 1;
2487 } else if (strncmp(name, ".__afs", 6) == 0) {
2489 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);
2493 assert(Delete(&dir->dirHandle, name) == 0);
2495 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2496 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2499 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2500 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);
2501 if (ShowMounts && (vnodeEssence->type == vSymlink)
2502 && !(vnodeEssence->modeBits & 0111)) {
2508 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2509 vnodeEssence->InodeNumber);
2511 assert(fdP != NULL);
2512 size = FDH_SIZE(fdP);
2514 memset(buf, 0, 1024);
2517 code = FDH_READ(fdP, buf, size);
2518 assert(code == size);
2519 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2520 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2521 dir->name ? dir->name : "??", name, buf);
2522 FDH_REALLYCLOSE(fdP);
2525 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2526 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);
2527 if (vnodeIdToClass(vnodeNumber) == vLarge
2528 && vnodeEssence->name == NULL) {
2530 if ((n = (char *)malloc(strlen(name) + 1)))
2532 vnodeEssence->name = n;
2535 /* The directory entry points to the vnode. Check to see if the
2536 * vnode points back to the directory. If not, then let the
2537 * directory claim it (else it might end up orphaned). Vnodes
2538 * already claimed by another directory are deleted from this
2539 * directory: hardlinks to the same vnode are not allowed
2540 * from different directories.
2542 if (vnodeEssence->parent != dir->vnodeNumber) {
2543 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2544 /* Vnode does not point back to this directory.
2545 * Orphaned dirs cannot claim a file (it may belong to
2546 * another non-orphaned dir).
2549 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);
2551 vnodeEssence->parent = dir->vnodeNumber;
2552 vnodeEssence->changed = 1;
2554 /* Vnode was claimed by another directory */
2557 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 " : ""));
2558 } else if (vnodeNumber == 1) {
2559 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 " : ""));
2561 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 " : ""));
2566 assert(Delete(&dir->dirHandle, name) == 0);
2571 /* This directory claims the vnode */
2572 vnodeEssence->claimed = 1;
2574 vnodeEssence->count--;
2578 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2580 register struct VnodeInfo *vip = &vnodeInfo[class];
2581 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2582 char buf[SIZEOF_LARGEDISKVNODE];
2583 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2585 StreamHandle_t *file;
2590 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2591 fdP = IH_OPEN(vip->handle);
2592 assert(fdP != NULL);
2593 file = FDH_FDOPEN(fdP, "r+");
2594 assert(file != NULL);
2595 size = OS_SIZE(fdP->fd_fd);
2597 vip->nVnodes = (size / vcp->diskSize) - 1;
2598 if (vip->nVnodes > 0) {
2599 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2600 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2601 assert((vip->vnodes = (struct VnodeEssence *)
2602 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2603 if (class == vLarge) {
2604 assert((vip->inodes = (Inode *)
2605 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2614 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2615 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2616 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2617 nVnodes--, vnodeIndex++) {
2618 if (vnode->type != vNull) {
2619 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2620 afs_fsize_t vnodeLength;
2621 vip->nAllocatedVnodes++;
2622 vep->count = vnode->linkCount;
2623 VNDISK_GET_LEN(vnodeLength, vnode);
2624 vep->blockCount = nBlocks(vnodeLength);
2625 vip->volumeBlockCount += vep->blockCount;
2626 vep->parent = vnode->parent;
2627 vep->unique = vnode->uniquifier;
2628 if (*maxu < vnode->uniquifier)
2629 *maxu = vnode->uniquifier;
2630 vep->modeBits = vnode->modeBits;
2631 vep->InodeNumber = VNDISK_GET_INO(vnode);
2632 vep->type = vnode->type;
2633 vep->author = vnode->author;
2634 vep->owner = vnode->owner;
2635 vep->group = vnode->group;
2636 if (vnode->type == vDirectory) {
2637 assert(class == vLarge);
2638 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2647 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2649 struct VnodeEssence *parentvp;
2655 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2656 && GetDirName(vp->parent, parentvp, path)) {
2658 strcat(path, vp->name);
2664 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2665 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2668 IsVnodeOrphaned(VnodeId vnode)
2670 struct VnodeEssence *vep;
2673 return (1); /* Vnode zero does not exist */
2675 return (0); /* The root dir vnode is always claimed */
2676 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2677 if (!vep || !vep->claimed)
2678 return (1); /* Vnode is not claimed - it is orphaned */
2680 return (IsVnodeOrphaned(vep->parent));
2684 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2685 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2688 static struct DirSummary dir;
2689 static struct DirHandle dirHandle;
2690 struct VnodeEssence *parent;
2691 static char path[MAXPATHLEN];
2694 if (dirVnodeInfo->vnodes[i].salvaged)
2695 return; /* already salvaged */
2698 dirVnodeInfo->vnodes[i].salvaged = 1;
2700 if (dirVnodeInfo->inodes[i] == 0)
2701 return; /* Not allocated to a directory */
2703 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2704 if (dirVnodeInfo->vnodes[i].parent) {
2705 Log("Bad parent, vnode 1; %s...\n",
2706 (Testing ? "skipping" : "salvaging"));
2707 dirVnodeInfo->vnodes[i].parent = 0;
2708 dirVnodeInfo->vnodes[i].changed = 1;
2711 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2712 if (parent && parent->salvaged == 0)
2713 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2714 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2715 rootdir, rootdirfound);
2718 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2719 dir.unique = dirVnodeInfo->vnodes[i].unique;
2722 dir.parent = dirVnodeInfo->vnodes[i].parent;
2723 dir.haveDot = dir.haveDotDot = 0;
2724 dir.ds_linkH = alinkH;
2725 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2726 dirVnodeInfo->inodes[i]);
2728 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2731 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2732 (Testing ? "skipping" : "salvaging"));
2735 CopyAndSalvage(&dir);
2739 dirHandle = dir.dirHandle;
2742 GetDirName(bitNumberToVnodeNumber(i, vLarge),
2743 &dirVnodeInfo->vnodes[i], path);
2746 /* If enumeration failed for random reasons, we will probably delete
2747 * too much stuff, so we guard against this instead.
2749 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2752 /* Delete the old directory if it was copied in order to salvage.
2753 * CopyOnWrite has written the new inode # to the disk, but we still
2754 * have the old one in our local structure here. Thus, we idec the
2758 if (dir.copied && !Testing) {
2759 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2761 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2764 /* Remember rootdir DirSummary _after_ it has been judged */
2765 if (dir.vnodeNumber == 1 && dir.unique == 1) {
2766 memcpy(rootdir, &dir, sizeof(struct DirSummary));
2774 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2776 /* This routine, for now, will only be called for read-write volumes */
2778 int BlocksInVolume = 0, FilesInVolume = 0;
2779 register VnodeClass class;
2780 struct DirSummary rootdir, oldrootdir;
2781 struct VnodeInfo *dirVnodeInfo;
2782 struct VnodeDiskObject vnode;
2783 VolumeDiskData volHeader;
2785 int orphaned, rootdirfound = 0;
2786 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
2787 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
2788 struct VnodeEssence *vep;
2793 VnodeId LFVnode, ThisVnode;
2794 Unique LFUnique, ThisUnique;
2797 vid = rwIsp->volSummary->header.id;
2798 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2799 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2800 assert(nBytes == sizeof(volHeader));
2801 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2802 assert(volHeader.destroyMe != DESTROY_ME);
2803 /* (should not have gotten this far with DESTROY_ME flag still set!) */
2805 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2807 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2810 dirVnodeInfo = &vnodeInfo[vLarge];
2811 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2812 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2820 /* Parse each vnode looking for orphaned vnodes and
2821 * connect them to the tree as orphaned (if requested).
2823 oldrootdir = rootdir;
2824 for (class = 0; class < nVNODECLASSES; class++) {
2825 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2826 vep = &(vnodeInfo[class].vnodes[v]);
2827 ThisVnode = bitNumberToVnodeNumber(v, class);
2828 ThisUnique = vep->unique;
2830 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2831 continue; /* Ignore unused, claimed, and root vnodes */
2833 /* This vnode is orphaned. If it is a directory vnode, then the '..'
2834 * entry in this vnode had incremented the parent link count (In
2835 * JudgeEntry()). We need to go to the parent and decrement that
2836 * link count. But if the parent's unique is zero, then the parent
2837 * link count was not incremented in JudgeEntry().
2839 if (class == vLarge) { /* directory vnode */
2840 pv = vnodeIdToBitNumber(vep->parent);
2841 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2842 vnodeInfo[vLarge].vnodes[pv].count++;
2846 continue; /* If no rootdir, can't attach orphaned files */
2848 /* Here we attach orphaned files and directories into the
2849 * root directory, LVVnode, making sure link counts stay correct.
2851 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2852 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
2853 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
2855 /* Update this orphaned vnode's info. Its parent info and
2856 * link count (do for orphaned directories and files).
2858 vep->parent = LFVnode; /* Parent is the root dir */
2859 vep->unique = LFUnique;
2862 vep->count--; /* Inc link count (root dir will pt to it) */
2864 /* If this orphaned vnode is a directory, change '..'.
2865 * The name of the orphaned dir/file is unknown, so we
2866 * build a unique name. No need to CopyOnWrite the directory
2867 * since it is not connected to tree in BK or RO volume and
2868 * won't be visible there.
2870 if (class == vLarge) {
2874 /* Remove and recreate the ".." entry in this orphaned directory */
2875 SetSalvageDirHandle(&dh, vid, fileSysDevice,
2876 vnodeInfo[class].inodes[v]);
2878 pa.Unique = LFUnique;
2879 assert(Delete(&dh, "..") == 0);
2880 assert(Create(&dh, "..", &pa) == 0);
2882 /* The original parent's link count was decremented above.
2883 * Here we increment the new parent's link count.
2885 pv = vnodeIdToBitNumber(LFVnode);
2886 vnodeInfo[vLarge].vnodes[pv].count--;
2890 /* Go to the root dir and add this entry. The link count of the
2891 * root dir was incremented when ".." was created. Try 10 times.
2893 for (j = 0; j < 10; j++) {
2894 pa.Vnode = ThisVnode;
2895 pa.Unique = ThisUnique;
2897 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2899 vLarge) ? "__ORPHANDIR__" :
2900 "__ORPHANFILE__"), ThisVnode,
2903 CopyOnWrite(&rootdir);
2904 code = Create(&rootdir.dirHandle, npath, &pa);
2908 ThisUnique += 50; /* Try creating a different file */
2911 Log("Attaching orphaned %s to volume's root dir as %s\n",
2912 ((class == vLarge) ? "directory" : "file"), npath);
2914 } /* for each vnode in the class */
2915 } /* for each class of vnode */
2917 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2919 if (!oldrootdir.copied && rootdir.copied) {
2921 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2924 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
2927 DFlush(); /* Flush the changes */
2928 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
2929 Log("Cannot attach orphaned files and directories: Root directory not found\n");
2930 orphans = ORPH_IGNORE;
2933 /* Write out all changed vnodes. Orphaned files and directories
2934 * will get removed here also (if requested).
2936 for (class = 0; class < nVNODECLASSES; class++) {
2937 int nVnodes = vnodeInfo[class].nVnodes;
2938 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2939 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
2940 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
2941 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
2942 for (i = 0; i < nVnodes; i++) {
2943 register struct VnodeEssence *vnp = &vnodes[i];
2944 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
2946 /* If the vnode is good but is unclaimed (not listed in
2947 * any directory entries), then it is orphaned.
2950 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
2951 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
2955 if (vnp->changed || vnp->count) {
2959 IH_IREAD(vnodeInfo[class].handle,
2960 vnodeIndexOffset(vcp, vnodeNumber),
2961 (char *)&vnode, sizeof(vnode));
2962 assert(nBytes == sizeof(vnode));
2964 vnode.parent = vnp->parent;
2965 oldCount = vnode.linkCount;
2966 vnode.linkCount = vnode.linkCount - vnp->count;
2969 orphaned = IsVnodeOrphaned(vnodeNumber);
2971 if (!vnp->todelete) {
2972 /* Orphans should have already been attached (if requested) */
2973 assert(orphans != ORPH_ATTACH);
2974 oblocks += vnp->blockCount;
2977 if (((orphans == ORPH_REMOVE) || vnp->todelete)
2979 BlocksInVolume -= vnp->blockCount;
2981 if (VNDISK_GET_INO(&vnode)) {
2983 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
2986 memset(&vnode, 0, sizeof(vnode));
2988 } else if (vnp->count) {
2990 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
2994 vnode.dataVersion++;
2997 IH_IWRITE(vnodeInfo[class].handle,
2998 vnodeIndexOffset(vcp, vnodeNumber),
2999 (char *)&vnode, sizeof(vnode));
3000 assert(nBytes == sizeof(vnode));
3006 if (!Showmode && ofiles) {
3007 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3009 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3013 for (class = 0; class < nVNODECLASSES; class++) {
3014 register struct VnodeInfo *vip = &vnodeInfo[class];
3015 for (i = 0; i < vip->nVnodes; i++)
3016 if (vip->vnodes[i].name)
3017 free(vip->vnodes[i].name);
3024 /* Set correct resource utilization statistics */
3025 volHeader.filecount = FilesInVolume;
3026 volHeader.diskused = BlocksInVolume;
3028 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3029 if (volHeader.uniquifier < (maxunique + 1)) {
3031 Log("Volume uniquifier is too low; fixed\n");
3032 /* Plus 2,000 in case there are workstations out there with
3033 * cached vnodes that have since been deleted
3035 volHeader.uniquifier = (maxunique + 1 + 2000);
3038 /* Turn off the inUse bit; the volume's been salvaged! */
3039 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3040 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3041 volHeader.inService = 1; /* allow service again */
3042 volHeader.needsCallback = (VolumeChanged != 0);
3043 volHeader.dontSalvage = DONT_SALVAGE;
3046 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3047 assert(nBytes == sizeof(volHeader));
3050 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3051 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3052 FilesInVolume, BlocksInVolume);
3054 IH_RELEASE(vnodeInfo[vSmall].handle);
3055 IH_RELEASE(vnodeInfo[vLarge].handle);
3061 ClearROInUseBit(struct VolumeSummary *summary)
3063 IHandle_t *h = summary->volumeInfoHandle;
3066 VolumeDiskData volHeader;
3068 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3069 assert(nBytes == sizeof(volHeader));
3070 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3071 volHeader.inUse = 0;
3072 volHeader.needsSalvaged = 0;
3073 volHeader.inService = 1;
3074 volHeader.dontSalvage = DONT_SALVAGE;
3076 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3077 assert(nBytes == sizeof(volHeader));
3082 * Possible delete the volume.
3084 * deleteMe - Always do so, only a partial volume.
3087 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3090 if (readOnly(isp) || deleteMe) {
3091 if (isp->volSummary && isp->volSummary->fileName) {
3094 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);
3096 Log("It will be deleted on this server (you may find it elsewhere)\n");
3099 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3101 Log("it will be deleted instead. It should be recloned.\n");
3104 unlink(isp->volSummary->fileName);
3106 } else if (!check) {
3107 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3109 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3115 AskOffline(VolumeId volumeId)
3119 for (i = 0; i < 3; i++) {
3120 code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3122 if (code == SYNC_OK) {
3124 } else if (code == SYNC_DENIED) {
3125 #ifdef DEMAND_ATTACH_ENABLE
3126 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3128 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3130 Abort("Salvage aborted\n");
3131 } else if (code == SYNC_BAD_COMMAND) {
3132 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3134 #ifdef DEMAND_ATTACH_ENABLE
3135 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3137 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3139 Abort("Salvage aborted\n");
3142 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3143 FSYNC_clientFinis();
3147 if (code != SYNC_OK) {
3148 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3149 Abort("Salvage aborted\n");
3154 AskOnline(VolumeId volumeId, char *partition)
3158 for (i = 0; i < 3; i++) {
3159 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3161 if (code == SYNC_OK) {
3163 } else if (code == SYNC_DENIED) {
3164 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3165 } else if (code == SYNC_BAD_COMMAND) {
3166 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3168 #ifdef DEMAND_ATTACH_ENABLE
3169 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3171 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3176 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3177 FSYNC_clientFinis();
3184 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3186 /* Volume parameter is passed in case iopen is upgraded in future to
3187 * require a volume Id to be passed
3190 IHandle_t *srcH, *destH;
3191 FdHandle_t *srcFdP, *destFdP;
3194 IH_INIT(srcH, device, rwvolume, inode1);
3195 srcFdP = IH_OPEN(srcH);
3196 assert(srcFdP != NULL);
3197 IH_INIT(destH, device, rwvolume, inode2);
3198 destFdP = IH_OPEN(destH);
3200 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3201 assert(FDH_WRITE(destFdP, buf, n) == n);
3203 FDH_REALLYCLOSE(srcFdP);
3204 FDH_REALLYCLOSE(destFdP);
3211 PrintInodeList(void)
3213 register struct ViceInodeInfo *ip;
3214 struct ViceInodeInfo *buf;
3215 struct afs_stat status;
3218 assert(afs_fstat(inodeFd, &status) == 0);
3219 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3220 assert(buf != NULL);
3221 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3222 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3223 for (ip = buf; nInodes--; ip++) {
3224 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3225 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3226 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3227 ip->u.param[2], ip->u.param[3]);
3233 PrintInodeSummary(void)
3236 struct InodeSummary *isp;
3238 for (i = 0; i < nVolumesInInodeFile; i++) {
3239 isp = &inodeSummary[i];
3240 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);
3245 PrintVolumeSummary(void)
3248 struct VolumeSummary *vsp;
3250 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3251 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3261 assert(0); /* Fork is never executed in the NT code path */
3276 if (main_thread != pthread_self())
3277 pthread_exit((void *)code);
3290 pid = wait(&status);
3292 if (WCOREDUMP(status))
3293 Log("\"%s\" core dumped!\n", prog);
3294 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3300 TimeStamp(time_t clock, int precision)
3303 static char timestamp[20];
3304 lt = localtime(&clock);
3306 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3308 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3313 CheckLogFile(char * log_path)
3315 char oldSlvgLog[AFSDIR_PATH_MAX];
3317 #ifndef AFS_NT40_ENV
3324 strcpy(oldSlvgLog, log_path);
3325 strcat(oldSlvgLog, ".old");
3327 renamefile(log_path, oldSlvgLog);
3328 logFile = afs_fopen(log_path, "a");
3330 if (!logFile) { /* still nothing, use stdout */
3334 #ifndef AFS_NAMEI_ENV
3335 AFS_DEBUG_IOPS_LOG(logFile);
3340 #ifndef AFS_NT40_ENV
3342 TimeStampLogFile(char * log_path)
3344 char stampSlvgLog[AFSDIR_PATH_MAX];
3349 lt = localtime(&now);
3350 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3351 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3352 log_path, lt->tm_year + 1900,
3353 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3356 /* try to link the logfile to a timestamped filename */
3357 /* if it fails, oh well, nothing we can do */
3358 link(log_path, stampSlvgLog);
3367 #ifndef AFS_NT40_ENV
3369 printf("Can't show log since using syslog.\n");
3378 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3381 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3384 while (fgets(line, sizeof(line), logFile))
3391 Log(const char *format, ...)
3397 va_start(args, format);
3398 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3400 #ifndef AFS_NT40_ENV
3402 syslog(LOG_INFO, "%s", tmp);
3406 gettimeofday(&now, 0);
3407 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3413 Abort(const char *format, ...)
3418 va_start(args, format);
3419 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3421 #ifndef AFS_NT40_ENV
3423 syslog(LOG_INFO, "%s", tmp);
3427 fprintf(logFile, "%s", tmp);
3442 p = (char *)malloc(strlen(s) + 1);
3449 /* Remove the FORCESALVAGE file */
3451 RemoveTheForce(char *path)
3453 if (!Testing && ForceSalvage) {
3454 if (chdir(path) == 0)
3455 unlink("FORCESALVAGE");
3459 #ifndef AFS_AIX32_ENV
3461 * UseTheForceLuke - see if we can use the force
3464 UseTheForceLuke(char *path)
3466 struct afs_stat force;
3468 assert(chdir(path) != -1);
3470 return (afs_stat("FORCESALVAGE", &force) == 0);
3474 * UseTheForceLuke - see if we can use the force
3477 * The VRMIX fsck will not muck with the filesystem it is supposedly
3478 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
3479 * muck directly with the root inode, which is within the normal
3481 * ListViceInodes() has a side effect of setting ForceSalvage if
3482 * it detects a need, based on root inode examination.
3485 UseTheForceLuke(char *path)
3488 return 0; /* sorry OB1 */
3493 /* NT support routines */
3495 static char execpathname[MAX_PATH];
3497 nt_SalvagePartition(char *partName, int jobn)
3502 if (!*execpathname) {
3503 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3504 if (!n || n == 1023)
3507 job.cj_magic = SALVAGER_MAGIC;
3508 job.cj_number = jobn;
3509 (void)strcpy(job.cj_part, partName);
3510 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3515 nt_SetupPartitionSalvage(void *datap, int len)
3517 childJob_t *jobp = (childJob_t *) datap;
3518 char logname[AFSDIR_PATH_MAX];
3520 if (len != sizeof(childJob_t))
3522 if (jobp->cj_magic != SALVAGER_MAGIC)
3527 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3529 logFile = afs_fopen(logname, "w");
3537 #endif /* AFS_NT40_ENV */