2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #include <afsconfig.h>
87 #include <afs/param.h>
91 #include <sys/param.h>
95 #endif /* ITIMER_REAL */
101 #include <sys/stat.h>
106 #include <WINNT/afsevent.h>
109 #define WCOREDUMP(x) ((x) & 0200)
112 #include <afs/afsint.h>
113 #include <afs/assert.h>
114 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
115 #if defined(AFS_VFSINCL_ENV)
116 #include <sys/vnode.h>
118 #include <sys/fs/ufs_inode.h>
120 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
121 #include <ufs/ufs/dinode.h>
122 #include <ufs/ffs/fs.h>
124 #include <ufs/inode.h>
127 #else /* AFS_VFSINCL_ENV */
129 #include <ufs/inode.h>
130 #else /* AFS_OSF_ENV */
131 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
132 #include <sys/inode.h>
135 #endif /* AFS_VFSINCL_ENV */
136 #endif /* AFS_SGI_ENV */
139 #include <sys/lockf.h>
143 #include <checklist.h>
145 #if defined(AFS_SGI_ENV)
150 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
153 #include <sys/mnttab.h>
154 #include <sys/mntent.h>
159 #endif /* AFS_SGI_ENV */
160 #endif /* AFS_HPUX_ENV */
165 #include <afs/osi_inode.h>
169 #include <afs/afsutil.h>
170 #include <afs/fileutil.h>
171 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
179 #include <afs/afssyscalls.h>
183 #include "partition.h"
184 #include "daemon_com.h"
186 #include "salvsync.h"
187 #include "viceinode.h"
189 #include "volinodes.h" /* header magic number, etc. stuff */
190 #include "vol-salvage.h"
191 #include "vol_internal.h"
193 #ifdef FSSYNC_BUILD_CLIENT
194 #include "vg_cache.h"
201 /*@+fcnmacros +macrofcndecl@*/
204 extern off64_t afs_lseek(int FD, off64_t O, int F);
205 #endif /*S_SPLINT_S */
206 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
207 #define afs_stat stat64
208 #define afs_fstat fstat64
209 #define afs_open open64
210 #define afs_fopen fopen64
211 #else /* !O_LARGEFILE */
213 extern off_t afs_lseek(int FD, off_t O, int F);
214 #endif /*S_SPLINT_S */
215 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
216 #define afs_stat stat
217 #define afs_fstat fstat
218 #define afs_open open
219 #define afs_fopen fopen
220 #endif /* !O_LARGEFILE */
221 /*@=fcnmacros =macrofcndecl@*/
224 extern void *calloc();
226 static char *TimeStamp(time_t clock, int precision);
229 int debug; /* -d flag */
230 extern int Testing; /* -n flag */
231 int ListInodeOption; /* -i flag */
232 int ShowRootFiles; /* -r flag */
233 int RebuildDirs; /* -sal flag */
234 int Parallel = 4; /* -para X flag */
235 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
236 int forceR = 0; /* -b flag */
237 int ShowLog = 0; /* -showlog flag */
238 int ShowSuid = 0; /* -showsuid flag */
239 int ShowMounts = 0; /* -showmounts flag */
240 int orphans = ORPH_IGNORE; /* -orphans option */
245 int useSyslog = 0; /* -syslog flag */
246 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
255 #define MAXPARALLEL 32
257 int OKToZap; /* -o flag */
258 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
259 * in the volume header */
261 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
263 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
265 Device fileSysDevice; /* The device number of the current
266 * partition being salvaged */
270 char *fileSysPath; /* The path of the mounted partition currently
271 * being salvaged, i.e. the directory
272 * containing the volume headers */
274 char *fileSysPathName; /* NT needs this to make name pretty in log. */
275 IHandle_t *VGLinkH; /* Link handle for current volume group. */
276 int VGLinkH_cnt; /* # of references to lnk handle. */
277 struct DiskPartition64 *fileSysPartition; /* Partition being salvaged */
279 char *fileSysDeviceName; /* The block device where the file system
280 * being salvaged was mounted */
281 char *filesysfulldev;
283 int VolumeChanged; /* Set by any routine which would change the volume in
284 * a way which would require callback is to be broken if the
285 * volume was put back on line by an active file server */
287 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
289 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
290 int inodeFd; /* File descriptor for inode file */
293 struct VnodeInfo vnodeInfo[nVNODECLASSES];
296 struct VolumeSummary *volumeSummaryp = NULL; /* Holds all the volumes in a part */
297 int nVolumes; /* Number of volumes (read-write and read-only)
298 * in volume summary */
304 /* Forward declarations */
305 /*@printflike@*/ void Log(const char *format, ...);
306 /*@printflike@*/ void Abort(const char *format, ...);
307 static int IsVnodeOrphaned(VnodeId vnode);
308 static int AskVolumeSummary(VolumeId singleVolumeNumber);
310 /* Uniquifier stored in the Inode */
315 return (u & 0x3fffff);
317 #if defined(AFS_SGI_EXMAG)
318 return (u & SGI_UNIQMASK);
321 #endif /* AFS_SGI_EXMAG */
326 BadError(register int aerror)
328 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
330 return 0; /* otherwise may be transient, e.g. EMFILE */
335 char *save_args[MAX_ARGS];
337 extern pthread_t main_thread;
338 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
341 /* Get the salvage lock if not already held. Hold until process exits. */
343 ObtainSalvageLock(void)
349 (FD_t)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
350 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
351 if (salvageLock == INVALID_FD) {
353 "salvager: There appears to be another salvager running! Aborted.\n");
358 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
359 if (salvageLock < 0) {
361 "salvager: can't open salvage lock file %s, aborting\n",
362 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
365 #ifdef AFS_DARWIN_ENV
366 if (flock(salvageLock, LOCK_EX) == -1) {
368 if (lockf(salvageLock, F_LOCK, 0) == -1) {
371 "salvager: There appears to be another salvager running! Aborted.\n");
378 #ifdef AFS_SGI_XFS_IOPS_ENV
379 /* Check if the given partition is mounted. For XFS, the root inode is not a
380 * constant. So we check the hard way.
383 IsPartitionMounted(char *part)
386 struct mntent *mntent;
388 assert(mntfp = setmntent(MOUNTED, "r"));
389 while (mntent = getmntent(mntfp)) {
390 if (!strcmp(part, mntent->mnt_dir))
395 return mntent ? 1 : 1;
398 /* Check if the given inode is the root of the filesystem. */
399 #ifndef AFS_SGI_XFS_IOPS_ENV
401 IsRootInode(struct afs_stat *status)
404 * The root inode is not a fixed value in XFS partitions. So we need to
405 * see if the partition is in the list of mounted partitions. This only
406 * affects the SalvageFileSys path, so we check there.
408 return (status->st_ino == ROOTINODE);
413 #ifndef AFS_NAMEI_ENV
414 /* We don't want to salvage big files filesystems, since we can't put volumes on
418 CheckIfBigFilesFS(char *mountPoint, char *devName)
420 struct superblock fs;
423 if (strncmp(devName, "/dev/", 5)) {
424 (void)sprintf(name, "/dev/%s", devName);
426 (void)strcpy(name, devName);
429 if (ReadSuper(&fs, name) < 0) {
430 Log("Unable to read superblock. Not salvaging partition %s.\n",
434 if (IsBigFilesFileSystem(&fs)) {
435 Log("Partition %s is a big files filesystem, not salvaging.\n",
445 #define HDSTR "\\Device\\Harddisk"
446 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
448 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
453 static int dowarn = 1;
455 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
457 if (strncmp(res, HDSTR, HDLEN)) {
460 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
461 res, HDSTR, p1->devName);
465 d1 = atoi(&res[HDLEN]);
467 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
469 if (strncmp(res, HDSTR, HDLEN)) {
472 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
473 res, HDSTR, p2->devName);
477 d2 = atoi(&res[HDLEN]);
482 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
485 /* This assumes that two partitions with the same device number divided by
486 * PartsPerDisk are on the same disk.
489 SalvageFileSysParallel(struct DiskPartition64 *partP)
492 struct DiskPartition64 *partP;
493 int pid; /* Pid for this job */
494 int jobnumb; /* Log file job number */
495 struct job *nextjob; /* Next partition on disk to salvage */
497 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
498 struct job *thisjob = 0;
499 static int numjobs = 0;
500 static int jobcount = 0;
506 char logFileName[256];
510 /* We have a partition to salvage. Copy it into thisjob */
511 thisjob = (struct job *)malloc(sizeof(struct job));
513 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
516 memset(thisjob, 0, sizeof(struct job));
517 thisjob->partP = partP;
518 thisjob->jobnumb = jobcount;
520 } else if (jobcount == 0) {
521 /* We are asking to wait for all jobs (partp == 0), yet we never
524 Log("No file system partitions named %s* found; not salvaged\n",
525 VICE_PARTITION_PREFIX);
529 if (debug || Parallel == 1) {
531 SalvageFileSys(thisjob->partP, 0);
538 /* Check to see if thisjob is for a disk that we are already
539 * salvaging. If it is, link it in as the next job to do. The
540 * jobs array has 1 entry per disk being salvages. numjobs is
541 * the total number of disks currently being salvaged. In
542 * order to keep thejobs array compact, when a disk is
543 * completed, the hightest element in the jobs array is moved
544 * down to now open slot.
546 for (j = 0; j < numjobs; j++) {
547 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
548 /* On same disk, add it to this list and return */
549 thisjob->nextjob = jobs[j]->nextjob;
550 jobs[j]->nextjob = thisjob;
557 /* Loop until we start thisjob or until all existing jobs are finished */
558 while (thisjob || (!partP && (numjobs > 0))) {
559 startjob = -1; /* No new job to start */
561 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
562 /* Either the max jobs are running or we have to wait for all
563 * the jobs to finish. In either case, we wait for at least one
564 * job to finish. When it's done, clean up after it.
566 pid = wait(&wstatus);
568 for (j = 0; j < numjobs; j++) { /* Find which job it is */
569 if (pid == jobs[j]->pid)
573 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
574 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
577 numjobs--; /* job no longer running */
578 oldjob = jobs[j]; /* remember */
579 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
580 free(oldjob); /* free the old job */
582 /* If there is another partition on the disk to salvage, then
583 * say we will start it (startjob). If not, then put thisjob there
584 * and say we will start it.
586 if (jobs[j]) { /* Another partitions to salvage */
587 startjob = j; /* Will start it */
588 } else { /* There is not another partition to salvage */
590 jobs[j] = thisjob; /* Add thisjob */
592 startjob = j; /* Will start it */
594 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
595 startjob = -1; /* Don't start it - already running */
599 /* We don't have to wait for a job to complete */
601 jobs[numjobs] = thisjob; /* Add this job */
603 startjob = numjobs; /* Will start it */
607 /* Start up a new salvage job on a partition in job slot "startjob" */
608 if (startjob != -1) {
610 Log("Starting salvage of file system partition %s\n",
611 jobs[startjob]->partP->name);
613 /* For NT, we not only fork, but re-exec the salvager. Pass in the
614 * commands and pass the child job number via the data path.
617 nt_SalvagePartition(jobs[startjob]->partP->name,
618 jobs[startjob]->jobnumb);
619 jobs[startjob]->pid = pid;
624 jobs[startjob]->pid = pid;
630 for (fd = 0; fd < 16; fd++)
637 openlog("salvager", LOG_PID, useSyslogFacility);
641 (void)afs_snprintf(logFileName, sizeof logFileName,
643 AFSDIR_SERVER_SLVGLOG_FILEPATH,
644 jobs[startjob]->jobnumb);
645 logFile = afs_fopen(logFileName, "w");
650 SalvageFileSys1(jobs[startjob]->partP, 0);
655 } /* while ( thisjob || (!partP && numjobs > 0) ) */
657 /* If waited for all jobs to complete, now collect log files and return */
659 if (!useSyslog) /* if syslogging - no need to collect */
662 for (i = 0; i < jobcount; i++) {
663 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
664 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
665 if ((passLog = afs_fopen(logFileName, "r"))) {
666 while (fgets(buf, sizeof(buf), passLog)) {
671 (void)unlink(logFileName);
680 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
682 if (!canfork || debug || Fork() == 0) {
683 SalvageFileSys1(partP, singleVolumeNumber);
684 if (canfork && !debug) {
689 Wait("SalvageFileSys");
693 get_DevName(char *pbuffer, char *wpath)
695 char pbuf[128], *ptr;
696 strcpy(pbuf, pbuffer);
697 ptr = (char *)strrchr(pbuf, '/');
703 ptr = (char *)strrchr(pbuffer, '/');
705 strcpy(pbuffer, ptr + 1);
712 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
715 char inodeListPath[256];
717 static char tmpDevName[100];
718 static char wpath[100];
719 struct VolumeSummary *vsp, *esp;
723 fileSysPartition = partP;
724 fileSysDevice = fileSysPartition->device;
725 fileSysPathName = VPartitionPath(fileSysPartition);
728 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
729 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
730 name = partP->devName;
732 fileSysPath = fileSysPathName;
733 strcpy(tmpDevName, partP->devName);
734 name = get_DevName(tmpDevName, wpath);
735 fileSysDeviceName = name;
736 filesysfulldev = wpath;
739 VLockPartition(partP->name);
740 if (singleVolumeNumber || ForceSalvage)
743 ForceSalvage = UseTheForceLuke(fileSysPath);
745 if (singleVolumeNumber) {
746 /* salvageserver already setup fssync conn for us */
747 if ((programType != salvageServer) && !VConnectFS()) {
748 Abort("Couldn't connect to file server\n");
750 AskOffline(singleVolumeNumber, partP->name);
753 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
754 partP->name, name, (Testing ? "(READONLY mode)" : ""));
756 Log("***Forced salvage of all volumes on this partition***\n");
761 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
768 assert((dirp = opendir(fileSysPath)) != NULL);
769 while ((dp = readdir(dirp))) {
770 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
771 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
773 Log("Removing old salvager temp files %s\n", dp->d_name);
774 strcpy(npath, fileSysPath);
776 strcat(npath, dp->d_name);
782 tdir = (tmpdir ? tmpdir : fileSysPath);
784 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
785 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
787 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
791 inodeFile = fopen(inodeListPath, "w+b");
793 Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
796 /* Using nt_unlink here since we're really using the delete on close
797 * semantics of unlink. In most places in the salvager, we really do
798 * mean to unlink the file at that point. Those places have been
799 * modified to actually do that so that the NT crt can be used there.
801 code = nt_unlink(inodeListPath);
803 code = unlink(inodeListPath);
806 Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
809 if (GetInodeSummary(inodeFile, singleVolumeNumber) < 0) {
813 inodeFd = fileno(inodeFile);
815 Abort("Temporary file %s is missing...\n", inodeListPath);
816 afs_lseek(inodeFd, 0L, SEEK_SET);
817 if (ListInodeOption) {
821 /* enumerate volumes in the partition.
822 * figure out sets of read-only + rw volumes.
823 * salvage each set, read-only volumes first, then read-write.
824 * Fix up inodes on last volume in set (whether it is read-write
827 GetVolumeSummary(singleVolumeNumber);
829 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
830 i < nVolumesInInodeFile; i = j) {
831 VolumeId rwvid = inodeSummary[i].RWvolumeId;
833 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
835 VolumeId vid = inodeSummary[j].volumeId;
836 struct VolumeSummary *tsp;
837 /* Scan volume list (from partition root directory) looking for the
838 * current rw volume number in the volume list from the inode scan.
839 * If there is one here that is not in the inode volume list,
841 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
843 DeleteExtraVolumeHeaderFile(vsp);
845 /* Now match up the volume summary info from the root directory with the
846 * entry in the volume list obtained from scanning inodes */
847 inodeSummary[j].volSummary = NULL;
848 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
849 if (tsp->header.id == vid) {
850 inodeSummary[j].volSummary = tsp;
856 /* Salvage the group of volumes (several read-only + 1 read/write)
857 * starting with the current read-only volume we're looking at.
859 SalvageVolumeGroup(&inodeSummary[i], j - i);
862 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
863 for (; vsp < esp; vsp++) {
865 DeleteExtraVolumeHeaderFile(vsp);
868 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
869 RemoveTheForce(fileSysPath);
871 if (!Testing && singleVolumeNumber) {
872 AskOnline(singleVolumeNumber, fileSysPartition->name);
874 /* Step through the volumeSummary list and set all volumes on-line.
875 * The volumes were taken off-line in GetVolumeSummary.
877 for (j = 0; j < nVolumes; j++) {
878 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
882 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
883 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
886 fclose(inodeFile); /* SalvageVolumeGroup was the last which needed it. */
890 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
893 sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
896 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
899 code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
901 Log("Error %ld destroying volume disk header for volume %lu\n",
902 afs_printable_int32_ld(code),
903 afs_printable_uint32_lu(vsp->header.id));
906 /* make sure we actually delete the fileName file; ENOENT
907 * is fine, since VDestroyVolumeDiskHeader probably already
909 if (unlink(path) && errno != ENOENT) {
910 Log("Unable to unlink %s (errno = %d)\n", path, errno);
917 CompareInodes(const void *_p1, const void *_p2)
919 register const struct ViceInodeInfo *p1 = _p1;
920 register const struct ViceInodeInfo *p2 = _p2;
921 if (p1->u.vnode.vnodeNumber == INODESPECIAL
922 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
923 VolumeId p1rwid, p2rwid;
925 (p1->u.vnode.vnodeNumber ==
926 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
928 (p2->u.vnode.vnodeNumber ==
929 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
934 if (p1->u.vnode.vnodeNumber == INODESPECIAL
935 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
936 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
937 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
938 if (p1->u.vnode.volumeId == p1rwid)
940 if (p2->u.vnode.volumeId == p2rwid)
942 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
944 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
945 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
946 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
948 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
950 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
952 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
954 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
956 /* The following tests are reversed, so that the most desirable
957 * of several similar inodes comes first */
958 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
960 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
961 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
965 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
966 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
971 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
973 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
974 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
978 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
979 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
984 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
986 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
987 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
991 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
992 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
997 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
999 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1000 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1003 #ifdef AFS_SGI_EXMAG
1004 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1005 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1014 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1015 register struct InodeSummary *summary)
1017 VolumeId volume = ip->u.vnode.volumeId;
1018 VolumeId rwvolume = volume;
1019 register int n, nSpecial;
1020 register Unique maxunique;
1023 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1025 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1027 rwvolume = ip->u.special.parentId;
1028 /* This isn't quite right, as there could (in error) be different
1029 * parent inodes in different special vnodes */
1031 if (maxunique < ip->u.vnode.vnodeUniquifier)
1032 maxunique = ip->u.vnode.vnodeUniquifier;
1036 summary->volumeId = volume;
1037 summary->RWvolumeId = rwvolume;
1038 summary->nInodes = n;
1039 summary->nSpecialInodes = nSpecial;
1040 summary->maxUniquifier = maxunique;
1044 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1046 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1047 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1048 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1053 * Collect list of inodes in file named by path. If a truly fatal error,
1054 * unlink the file and abort. For lessor errors, return -1. The file will
1055 * be unlinked by the caller.
1058 GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
1060 struct afs_stat status;
1063 struct ViceInodeInfo *ip;
1064 struct InodeSummary summary;
1065 char summaryFileName[50];
1068 char *dev = fileSysPath;
1069 char *wpath = fileSysPath;
1071 char *dev = fileSysDeviceName;
1072 char *wpath = filesysfulldev;
1074 char *part = fileSysPath;
1077 /* This file used to come from vfsck; cobble it up ourselves now... */
1079 ListViceInodes(dev, fileSysPath, inodeFile,
1080 singleVolumeNumber ? OnlyOneVolume : 0,
1081 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1083 Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1086 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1088 if (forceSal && !ForceSalvage) {
1089 Log("***Forced salvage of all volumes on this partition***\n");
1092 fseek(inodeFile, 0L, SEEK_SET);
1093 inodeFd = fileno(inodeFile);
1094 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1095 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1097 tdir = (tmpdir ? tmpdir : part);
1099 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1100 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1102 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1103 "%s/salvage.temp.%d", tdir, getpid());
1105 summaryFile = afs_fopen(summaryFileName, "a+");
1106 if (summaryFile == NULL) {
1107 Abort("Unable to create inode summary file\n");
1111 /* Using nt_unlink here since we're really using the delete on close
1112 * semantics of unlink. In most places in the salvager, we really do
1113 * mean to unlink the file at that point. Those places have been
1114 * modified to actually do that so that the NT crt can be used there.
1116 code = nt_unlink(summaryFileName);
1118 code = unlink(summaryFileName);
1121 Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1124 if (!canfork || debug || Fork() == 0) {
1126 unsigned long st_size=(unsigned long) status.st_size;
1127 nInodes = st_size / sizeof(struct ViceInodeInfo);
1129 fclose(summaryFile);
1130 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1131 RemoveTheForce(fileSysPath);
1133 struct VolumeSummary *vsp;
1136 GetVolumeSummary(singleVolumeNumber);
1138 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1140 DeleteExtraVolumeHeaderFile(vsp);
1143 Log("%s vice inodes on %s; not salvaged\n",
1144 singleVolumeNumber ? "No applicable" : "No", dev);
1147 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1149 fclose(summaryFile);
1151 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1154 if (read(inodeFd, ip, st_size) != st_size) {
1155 fclose(summaryFile);
1156 Abort("Unable to read inode table; %s not salvaged\n", dev);
1158 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1159 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1160 || write(inodeFd, ip, st_size) != st_size) {
1161 fclose(summaryFile);
1162 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1166 CountVolumeInodes(ip, nInodes, &summary);
1167 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1168 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1169 fclose(summaryFile);
1172 summary.index += (summary.nInodes);
1173 nInodes -= summary.nInodes;
1174 ip += summary.nInodes;
1176 /* Following fflush is not fclose, because if it was debug mode would not work */
1177 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1178 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1179 fclose(summaryFile);
1182 if (canfork && !debug) {
1187 if (Wait("Inode summary") == -1) {
1188 fclose(summaryFile);
1189 Exit(1); /* salvage of this partition aborted */
1192 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1193 if (status.st_size != 0) {
1195 unsigned long st_status=(unsigned long)status.st_size;
1196 inodeSummary = (struct InodeSummary *)malloc(st_status);
1197 assert(inodeSummary != NULL);
1198 /* For GNU we need to do lseek to get the file pointer moved. */
1199 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1200 ret = read(fileno(summaryFile), inodeSummary, st_status);
1201 assert(ret == st_status);
1203 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1204 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1205 fclose(summaryFile);
1209 /* Comparison routine for volume sort.
1210 This is setup so that a read-write volume comes immediately before
1211 any read-only clones of that volume */
1213 CompareVolumes(const void *_p1, const void *_p2)
1215 register const struct VolumeSummary *p1 = _p1;
1216 register const struct VolumeSummary *p2 = _p2;
1217 if (p1->header.parent != p2->header.parent)
1218 return p1->header.parent < p2->header.parent ? -1 : 1;
1219 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1221 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1223 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1227 * Gleans volumeSummary information by asking the fileserver
1229 * @param[in] singleVolumeNumber the volume we're salvaging. 0 if we're
1230 * salvaging a whole partition
1232 * @return whether we obtained the volume summary information or not
1233 * @retval 0 success; we obtained the volume summary information
1234 * @retval nonzero we did not get the volume summary information; either the
1235 * fileserver responded with an error, or we are not supposed to
1236 * ask the fileserver for the information (e.g. we are salvaging
1237 * the entire partition or we are not the salvageserver)
1239 * @note for non-DAFS, always returns 1
1242 AskVolumeSummary(VolumeId singleVolumeNumber)
1245 #ifdef FSSYNC_BUILD_CLIENT
1246 if (programType == salvageServer) {
1247 if (singleVolumeNumber) {
1248 FSSYNC_VGQry_response_t q_res;
1250 struct VolumeSummary *vsp;
1252 struct VolumeDiskHeader diskHdr;
1254 memset(&res, 0, sizeof(res));
1256 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1259 * We must wait for the partition to finish scanning before
1260 * can continue, since we will not know if we got the entire
1261 * VG membership unless the partition is fully scanned.
1262 * We could, in theory, just scan the partition ourselves if
1263 * the VG cache is not ready, but we would be doing the exact
1264 * same scan the fileserver is doing; it will almost always
1265 * be faster to wait for the fileserver. The only exceptions
1266 * are if the partition does not take very long to scan, and
1267 * in that case it's fast either way, so who cares?
1269 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1270 Log("waiting for fileserver to finish scanning partition %s...\n",
1271 fileSysPartition->name);
1273 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1274 /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1275 * just so small partitions don't need to wait over 10
1276 * seconds every time, and large partitions are generally
1277 * polled only once every ten seconds. */
1278 sleep((i > 10) ? (i = 10) : i);
1280 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1284 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1285 /* This can happen if there's no header for the volume
1286 * we're salvaging, or no headers exist for the VG (if
1287 * we're salvaging an RW). Act as if we got a response
1288 * with no VG members. The headers may be created during
1289 * salvaging, if there are inodes in this VG. */
1291 memset(&q_res, 0, sizeof(q_res));
1292 q_res.rw = singleVolumeNumber;
1296 Log("fileserver refused VGCQuery request for volume %lu on "
1297 "partition %s, code %ld reason %ld\n",
1298 afs_printable_uint32_lu(singleVolumeNumber),
1299 fileSysPartition->name,
1300 afs_printable_int32_ld(code),
1301 afs_printable_int32_ld(res.hdr.reason));
1305 if (q_res.rw != singleVolumeNumber) {
1306 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1307 afs_printable_uint32_lu(singleVolumeNumber),
1308 afs_printable_uint32_lu(q_res.rw));
1309 #ifdef SALVSYNC_BUILD_CLIENT
1310 if (SALVSYNC_LinkVolume(q_res.rw,
1312 fileSysPartition->name,
1314 Log("schedule request failed\n");
1316 #endif /* SALVSYNC_BUILD_CLIENT */
1317 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1320 volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
1321 assert(volumeSummaryp != NULL);
1324 vsp = volumeSummaryp;
1326 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1327 char name[VMAXPATHLEN];
1329 if (!q_res.children[i]) {
1333 if (q_res.children[i] != singleVolumeNumber) {
1334 AskOffline(q_res.children[i], fileSysPartition->name);
1336 code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
1338 Log("Cannot read header for %lu; trying to salvage group anyway\n",
1339 afs_printable_uint32_lu(q_res.children[i]));
1344 DiskToVolumeHeader(&vsp->header, &diskHdr);
1345 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1346 vsp->fileName = ToString(name);
1351 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1356 Log("Cannot get volume summary from fileserver; falling back to scanning "
1357 "entire partition\n");
1360 #endif /* FSSYNC_BUILD_CLIENT */
1365 * count how many volume headers are found by VWalkVolumeHeaders.
1367 * @param[in] dp the disk partition (unused)
1368 * @param[in] name full path to the .vol header (unused)
1369 * @param[in] hdr the header data (unused)
1370 * @param[in] last whether this is the last try or not (unused)
1371 * @param[in] rock actually an afs_int32*; the running count of how many
1372 * volumes we have found
1377 CountHeader(struct DiskPartition64 *dp, const char *name,
1378 struct VolumeDiskHeader *hdr, int last, void *rock)
1380 afs_int32 *nvols = (afs_int32 *)rock;
1386 * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1389 struct SalvageScanParams {
1390 VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1391 * vol id of the VG we're salvaging */
1392 struct VolumeSummary *vsp; /**< ptr to the current volume summary object
1393 * we're filling in */
1394 afs_int32 nVolumes; /**< # of vols we've encountered */
1395 afs_int32 totalVolumes; /**< max # of vols we should encounter (the
1396 * # of vols we've alloc'd memory for) */
1400 * records volume summary info found from VWalkVolumeHeaders.
1402 * Found volumes are also taken offline if they are in the specific volume
1403 * group we are looking for.
1405 * @param[in] dp the disk partition
1406 * @param[in] name full path to the .vol header
1407 * @param[in] hdr the header data
1408 * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1409 * @param[in] rock actually a struct SalvageScanParams*, containing the
1410 * information needed to record the volume summary data
1412 * @return operation status
1414 * @retval 1 volume header is mis-named and should be deleted
1417 RecordHeader(struct DiskPartition64 *dp, const char *name,
1418 struct VolumeDiskHeader *hdr, int last, void *rock)
1420 char nameShouldBe[64];
1421 struct SalvageScanParams *params;
1422 struct VolumeSummary summary;
1423 VolumeId singleVolumeNumber;
1425 params = (struct SalvageScanParams *)rock;
1427 singleVolumeNumber = params->singleVolumeNumber;
1429 DiskToVolumeHeader(&summary.header, hdr);
1431 if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1432 && summary.header.parent != singleVolumeNumber) {
1434 if (programType == salvageServer) {
1435 #ifdef SALVSYNC_BUILD_CLIENT
1436 Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1437 summary.header.id, summary.header.parent);
1438 if (SALVSYNC_LinkVolume(summary.header.parent,
1442 Log("schedule request failed\n");
1445 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1448 Log("%u is a read-only volume; not salvaged\n",
1449 singleVolumeNumber);
1454 if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1455 || summary.header.parent == singleVolumeNumber) {
1457 /* check if the header file is incorrectly named */
1459 const char *base = strrchr(name, '/');
1466 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1467 VFORMAT, afs_printable_uint32_lu(summary.header.id));
1470 if (strcmp(nameShouldBe, base)) {
1471 /* .vol file has wrong name; retry/delete */
1475 if (!badname || last) {
1476 /* only offline the volume if the header is good, or if this is
1477 * the last try looking at it; avoid AskOffline'ing the same vol
1480 if (singleVolumeNumber
1481 && summary.header.id != singleVolumeNumber) {
1482 /* don't offline singleVolumeNumber; we already did that
1485 AskOffline(summary.header.id, fileSysPartition->name);
1489 if (last && !Showmode) {
1490 Log("Volume header file %s is incorrectly named (should be %s "
1491 "not %s); %sdeleted (it will be recreated later, if "
1492 "necessary)\n", name, nameShouldBe, base,
1493 (Testing ? "it would have been " : ""));
1498 summary.fileName = ToString(base);
1501 if (params->nVolumes > params->totalVolumes) {
1502 /* We found more volumes than we found on the first partition walk;
1503 * apparently something created a volume while we were
1504 * partition-salvaging, or we found more than 20 vols when salvaging a
1505 * particular volume. Abort if we detect this, since other programs
1506 * supposed to not touch the partition while it is partition-salvaging,
1507 * and we shouldn't find more than 20 vols in a VG.
1509 Abort("Found %ld vol headers, but should have found at most %ld! "
1510 "Make sure the volserver/fileserver are not running at the "
1511 "same time as a partition salvage\n",
1512 afs_printable_int32_ld(params->nVolumes),
1513 afs_printable_int32_ld(params->totalVolumes));
1516 memcpy(params->vsp, &summary, sizeof(summary));
1524 * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1526 * If the header could not be read in at all, the header is always unlinked.
1527 * If instead RecordHeader said the header was bad (that is, the header file
1528 * is mis-named), we only unlink if we are doing a partition salvage, as
1529 * opposed to salvaging a specific volume group.
1531 * @param[in] dp the disk partition
1532 * @param[in] name full path to the .vol header
1533 * @param[in] hdr header data, or NULL if the header could not be read
1534 * @param[in] rock actually a struct SalvageScanParams*, with some information
1538 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1539 struct VolumeDiskHeader *hdr, void *rock)
1541 struct SalvageScanParams *params;
1544 params = (struct SalvageScanParams *)rock;
1547 /* no header; header is too bogus to read in at all */
1549 Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1555 } else if (!params->singleVolumeNumber) {
1556 /* We were able to read in a header, but RecordHeader said something
1557 * was wrong with it. We only unlink those if we are doing a partition
1564 if (dounlink && unlink(name)) {
1565 Log("Error %d while trying to unlink %s\n", errno, name);
1570 GetVolumeSummary(VolumeId singleVolumeNumber)
1572 afs_int32 nvols = 0;
1573 struct SalvageScanParams params;
1576 if (AskVolumeSummary(singleVolumeNumber) == 0) {
1577 /* we successfully got the vol information from the fileserver; no
1578 * need to scan the partition */
1582 if (!singleVolumeNumber) {
1583 /* Count how many volumes we have in /vicepX */
1584 code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
1587 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1592 nvols = VOL_VG_MAX_VOLS;
1595 volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
1596 assert(volumeSummaryp != NULL);
1598 params.singleVolumeNumber = singleVolumeNumber;
1599 params.vsp = volumeSummaryp;
1600 params.nVolumes = 0;
1601 params.totalVolumes = nvols;
1603 /* walk the partition directory of volume headers and record the info
1604 * about them; unlinking invalid headers */
1605 code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
1606 UnlinkHeader, ¶ms);
1608 Abort("Failed to get volume header summary\n");
1610 nVolumes = params.nVolumes;
1612 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1616 /* Find the link table. This should be associated with the RW volume or, if
1617 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1620 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1621 struct ViceInodeInfo *allInodes)
1624 struct ViceInodeInfo *ip;
1626 for (i = 0; i < nVols; i++) {
1627 ip = allInodes + isp[i].index;
1628 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1629 if (ip[j].u.special.type == VI_LINKTABLE)
1630 return ip[j].inodeNumber;
1637 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1639 struct versionStamp version;
1642 if (!VALID_INO(ino))
1644 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1645 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1646 if (!VALID_INO(ino))
1648 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1649 isp->RWvolumeId, errno);
1650 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1651 fdP = IH_OPEN(VGLinkH);
1653 Abort("Can't open link table for volume %u (error = %d)\n",
1654 isp->RWvolumeId, errno);
1656 if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1657 Abort("Can't truncate link table for volume %u (error = %d)\n",
1658 isp->RWvolumeId, errno);
1660 version.magic = LINKTABLEMAGIC;
1661 version.version = LINKTABLEVERSION;
1663 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1665 Abort("Can't truncate link table for volume %u (error = %d)\n",
1666 isp->RWvolumeId, errno);
1668 FDH_REALLYCLOSE(fdP);
1670 /* If the volume summary exits (i.e., the V*.vol header file exists),
1671 * then set this inode there as well.
1673 if (isp->volSummary)
1674 isp->volSummary->header.linkTable = ino;
1683 SVGParms_t *parms = (SVGParms_t *) arg;
1684 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1689 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1692 pthread_attr_t tattr;
1696 /* Initialize per volume global variables, even if later code does so */
1700 memset(&VolInfo, 0, sizeof(VolInfo));
1702 parms.svgp_inodeSummaryp = isp;
1703 parms.svgp_count = nVols;
1704 code = pthread_attr_init(&tattr);
1706 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1710 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1712 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1715 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1717 Log("Failed to create thread to salvage volume group %u\n",
1721 (void)pthread_join(tid, NULL);
1723 #endif /* AFS_NT40_ENV */
1726 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1728 struct ViceInodeInfo *inodes, *allInodes, *ip;
1729 int i, totalInodes, size, salvageTo;
1733 int dec_VGLinkH = 0;
1735 FdHandle_t *fdP = NULL;
1738 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1739 && isp->nSpecialInodes > 0);
1740 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1741 if (!ForceSalvage && QuickCheck(isp, nVols))
1744 if (ShowMounts && !haveRWvolume)
1746 if (canfork && !debug && Fork() != 0) {
1747 (void)Wait("Salvage volume group");
1750 for (i = 0, totalInodes = 0; i < nVols; i++)
1751 totalInodes += isp[i].nInodes;
1752 size = totalInodes * sizeof(struct ViceInodeInfo);
1753 inodes = (struct ViceInodeInfo *)malloc(size);
1754 allInodes = inodes - isp->index; /* this would the base of all the inodes
1755 * for the partition, if all the inodes
1756 * had been read into memory */
1758 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1760 assert(read(inodeFd, inodes, size) == size);
1762 /* Don't try to salvage a read write volume if there isn't one on this
1764 salvageTo = haveRWvolume ? 0 : 1;
1766 #ifdef AFS_NAMEI_ENV
1767 ino = FindLinkHandle(isp, nVols, allInodes);
1768 if (VALID_INO(ino)) {
1769 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1770 fdP = IH_OPEN(VGLinkH);
1772 if (!VALID_INO(ino) || fdP == NULL) {
1773 Log("%s link table for volume %u.\n",
1774 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1776 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1779 struct ViceInodeInfo *ip;
1780 CreateLinkTable(isp, ino);
1781 fdP = IH_OPEN(VGLinkH);
1782 /* Sync fake 1 link counts to the link table, now that it exists */
1784 for (i = 0; i < nVols; i++) {
1785 ip = allInodes + isp[i].index;
1786 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1788 nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1790 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1798 FDH_REALLYCLOSE(fdP);
1800 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1803 /* Salvage in reverse order--read/write volume last; this way any
1804 * Inodes not referenced by the time we salvage the read/write volume
1805 * can be picked up by the read/write volume */
1806 /* ACTUALLY, that's not done right now--the inodes just vanish */
1807 for (i = nVols - 1; i >= salvageTo; i--) {
1809 struct InodeSummary *lisp = &isp[i];
1810 #ifdef AFS_NAMEI_ENV
1811 /* If only the RO is present on this partition, the link table
1812 * shows up as a RW volume special file. Need to make sure the
1813 * salvager doesn't try to salvage the non-existent RW.
1815 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1816 /* If this only special inode is the link table, continue */
1817 if (inodes->u.special.type == VI_LINKTABLE) {
1824 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1825 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1826 /* Check inodes twice. The second time do things seriously. This
1827 * way the whole RO volume can be deleted, below, if anything goes wrong */
1828 for (check = 1; check >= 0; check--) {
1830 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1832 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1833 if (rw && deleteMe) {
1834 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1835 * volume won't be called */
1841 if (rw && check == 1)
1843 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1844 MaybeZapVolume(lisp, "Vnode index", 0, check);
1850 /* Fix actual inode counts */
1852 Log("totalInodes %d\n",totalInodes);
1853 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1854 static int TraceBadLinkCounts = 0;
1855 #ifdef AFS_NAMEI_ENV
1856 if (VGLinkH->ih_ino == ip->inodeNumber) {
1857 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1858 VGLinkH_p1 = ip->u.param[0];
1859 continue; /* Deal with this last. */
1862 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1863 TraceBadLinkCounts--; /* Limit reports, per volume */
1864 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]);
1866 while (ip->linkCount > 0) {
1867 /* below used to assert, not break */
1869 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1870 Log("idec failed. inode %s errno %d\n",
1871 PrintInode(NULL, ip->inodeNumber), errno);
1877 while (ip->linkCount < 0) {
1878 /* these used to be asserts */
1880 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1881 Log("iinc failed. inode %s errno %d\n",
1882 PrintInode(NULL, ip->inodeNumber), errno);
1889 #ifdef AFS_NAMEI_ENV
1890 while (dec_VGLinkH > 0) {
1891 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1892 Log("idec failed on link table, errno = %d\n", errno);
1896 while (dec_VGLinkH < 0) {
1897 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1898 Log("iinc failed on link table, errno = %d\n", errno);
1905 /* Directory consistency checks on the rw volume */
1907 SalvageVolume(isp, VGLinkH);
1908 IH_RELEASE(VGLinkH);
1910 if (canfork && !debug) {
1917 QuickCheck(register struct InodeSummary *isp, int nVols)
1919 /* Check headers BEFORE forking */
1923 for (i = 0; i < nVols; i++) {
1924 struct VolumeSummary *vs = isp[i].volSummary;
1925 VolumeDiskData volHeader;
1927 /* Don't salvage just because phantom rw volume is there... */
1928 /* (If a read-only volume exists, read/write inodes must also exist) */
1929 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1933 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1934 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1935 == sizeof(volHeader)
1936 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1937 && volHeader.dontSalvage == DONT_SALVAGE
1938 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1939 if (volHeader.inUse != 0) {
1940 volHeader.inUse = 0;
1941 volHeader.inService = 1;
1943 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1944 != sizeof(volHeader)) {
1960 /* SalvageVolumeHeaderFile
1962 * Salvage the top level V*.vol header file. Make sure the special files
1963 * exist and that there are no duplicates.
1965 * Calls SalvageHeader for each possible type of volume special file.
1969 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1970 register struct ViceInodeInfo *inodes, int RW,
1971 int check, int *deleteMe)
1974 register struct ViceInodeInfo *ip;
1975 int allinodesobsolete = 1;
1976 struct VolumeDiskHeader diskHeader;
1977 afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
1980 /* keeps track of special inodes that are probably 'good'; they are
1981 * referenced in the vol header, and are included in the given inodes
1986 } goodspecial[MAXINODETYPE];
1991 memset(goodspecial, 0, sizeof(goodspecial));
1993 skip = malloc(isp->nSpecialInodes * sizeof(*skip));
1995 memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
1997 Log("cannot allocate memory for inode skip array when salvaging "
1998 "volume %lu; not performing duplicate special inode recovery\n",
1999 afs_printable_uint32_lu(isp->volumeId));
2000 /* still try to perform the salvage; the skip array only does anything
2001 * if we detect duplicate special inodes */
2005 * First, look at the special inodes and see if any are referenced by
2006 * the existing volume header. If we find duplicate special inodes, we
2007 * can use this information to use the referenced inode (it's more
2008 * likely to be the 'good' one), and throw away the duplicates.
2010 if (isp->volSummary && skip) {
2011 /* use tempHeader, so we can use the stuff[] array to easily index
2012 * into the isp->volSummary special inodes */
2013 memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2015 for (i = 0; i < isp->nSpecialInodes; i++) {
2016 ip = &inodes[isp->index + i];
2017 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2018 /* will get taken care of in a later loop */
2021 if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2022 goodspecial[ip->u.special.type-1].valid = 1;
2023 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2028 memset(&tempHeader, 0, sizeof(tempHeader));
2029 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2030 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2031 tempHeader.id = isp->volumeId;
2032 tempHeader.parent = isp->RWvolumeId;
2034 /* Check for duplicates (inodes are sorted by type field) */
2035 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2036 ip = &inodes[isp->index + i];
2037 if (ip->u.special.type == (ip + 1)->u.special.type) {
2038 afs_ino_str_t stmp1, stmp2;
2040 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2041 /* Will be caught in the loop below */
2045 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2046 ip->u.special.type, isp->volumeId,
2047 PrintInode(stmp1, ip->inodeNumber),
2048 PrintInode(stmp2, (ip+1)->inodeNumber));
2050 if (skip && goodspecial[ip->u.special.type-1].valid) {
2051 Inode gi = goodspecial[ip->u.special.type-1].inode;
2054 Log("using special inode referenced by vol header (%s)\n",
2055 PrintInode(stmp1, gi));
2058 /* the volume header references some special inode of
2059 * this type in the inodes array; are we it? */
2060 if (ip->inodeNumber != gi) {
2062 } else if ((ip+1)->inodeNumber != gi) {
2063 /* in case this is the last iteration; we need to
2064 * make sure we check ip+1, too */
2069 Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2077 for (i = 0; i < isp->nSpecialInodes; i++) {
2078 ip = &inodes[isp->index + i];
2079 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2081 Log("Rubbish header inode %s of type %d\n",
2082 PrintInode(NULL, ip->inodeNumber),
2083 ip->u.special.type);
2089 Log("Rubbish header inode %s of type %d; deleted\n",
2090 PrintInode(NULL, ip->inodeNumber),
2091 ip->u.special.type);
2092 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2093 if (skip && skip[i]) {
2094 if (orphans == ORPH_REMOVE) {
2095 Log("Removing orphan special inode %s of type %d\n",
2096 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2099 Log("Ignoring orphan special inode %s of type %d\n",
2100 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2101 /* fall through to the ip->linkCount--; line below */
2104 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2105 allinodesobsolete = 0;
2107 if (!check && ip->u.special.type != VI_LINKTABLE)
2108 ip->linkCount--; /* Keep the inode around */
2116 if (allinodesobsolete) {
2123 VGLinkH_cnt++; /* one for every header. */
2125 if (!RW && !check && isp->volSummary) {
2126 ClearROInUseBit(isp->volSummary);
2130 for (i = 0; i < MAXINODETYPE; i++) {
2131 if (stuff[i].inodeType == VI_LINKTABLE) {
2132 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2133 * And we may have recreated the link table earlier, so set the
2134 * RW header as well.
2136 if (VALID_INO(VGLinkH->ih_ino)) {
2137 *stuff[i].inode = VGLinkH->ih_ino;
2141 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2145 if (isp->volSummary == NULL) {
2147 char headerName[64];
2148 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2149 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2151 Log("No header file for volume %u\n", isp->volumeId);
2155 Log("No header file for volume %u; %screating %s\n",
2156 isp->volumeId, (Testing ? "it would have been " : ""),
2158 isp->volSummary = (struct VolumeSummary *)
2159 malloc(sizeof(struct VolumeSummary));
2160 isp->volSummary->fileName = ToString(headerName);
2162 writefunc = VCreateVolumeDiskHeader;
2165 char headerName[64];
2166 /* hack: these two fields are obsolete... */
2167 isp->volSummary->header.volumeAcl = 0;
2168 isp->volSummary->header.volumeMountTable = 0;
2171 (&isp->volSummary->header, &tempHeader,
2172 sizeof(struct VolumeHeader))) {
2173 /* We often remove the name before calling us, so we make a fake one up */
2174 if (isp->volSummary->fileName) {
2175 strcpy(headerName, isp->volSummary->fileName);
2177 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2178 isp->volSummary->fileName = ToString(headerName);
2180 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2182 Log("Header file %s is damaged or no longer valid%s\n", path,
2183 (check ? "" : "; repairing"));
2187 writefunc = VWriteVolumeDiskHeader;
2191 memcpy(&isp->volSummary->header, &tempHeader,
2192 sizeof(struct VolumeHeader));
2195 Log("It would have written a new header file for volume %u\n",
2199 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2200 code = (*writefunc)(&diskHeader, fileSysPartition);
2202 Log("Error %ld writing volume header file for volume %lu\n",
2203 afs_printable_int32_ld(code),
2204 afs_printable_uint32_lu(diskHeader.id));
2209 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2210 isp->volSummary->header.volumeInfo);
2215 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2219 VolumeDiskData volumeInfo;
2220 struct versionStamp fileHeader;
2229 #ifndef AFS_NAMEI_ENV
2230 if (sp->inodeType == VI_LINKTABLE)
2233 if (*(sp->inode) == 0) {
2235 Log("Missing inode in volume header (%s)\n", sp->description);
2239 Log("Missing inode in volume header (%s); %s\n", sp->description,
2240 (Testing ? "it would have recreated it" : "recreating"));
2243 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2244 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2245 if (!VALID_INO(*(sp->inode)))
2247 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2248 sp->description, errno);
2253 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2254 fdP = IH_OPEN(specH);
2255 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2256 /* bail out early and destroy the volume */
2258 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2265 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2266 sp->description, errno);
2269 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2270 || header.fileHeader.magic != sp->stamp.magic)) {
2272 Log("Part of the header (%s) is corrupted\n", sp->description);
2273 FDH_REALLYCLOSE(fdP);
2277 Log("Part of the header (%s) is corrupted; recreating\n",
2281 if (sp->inodeType == VI_VOLINFO
2282 && header.volumeInfo.destroyMe == DESTROY_ME) {
2285 FDH_REALLYCLOSE(fdP);
2289 if (recreate && !Testing) {
2292 ("Internal error: recreating volume header (%s) in check mode\n",
2294 code = FDH_TRUNC(fdP, 0);
2296 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2297 sp->description, errno);
2299 /* The following code should be moved into vutil.c */
2300 if (sp->inodeType == VI_VOLINFO) {
2302 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2303 header.volumeInfo.stamp = sp->stamp;
2304 header.volumeInfo.id = isp->volumeId;
2305 header.volumeInfo.parentId = isp->RWvolumeId;
2306 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2307 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2308 isp->volumeId, isp->volumeId);
2309 header.volumeInfo.inService = 0;
2310 header.volumeInfo.blessed = 0;
2311 /* The + 1000 is a hack in case there are any files out in venus caches */
2312 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2313 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2314 header.volumeInfo.needsCallback = 0;
2315 gettimeofday(&tp, 0);
2316 header.volumeInfo.creationDate = tp.tv_sec;
2317 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2319 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2320 sp->description, errno);
2323 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2324 sizeof(header.volumeInfo));
2325 if (code != sizeof(header.volumeInfo)) {
2328 ("Unable to write volume header file (%s) (errno = %d)\n",
2329 sp->description, errno);
2330 Abort("Unable to write entire volume header file (%s)\n",
2334 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2336 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2337 sp->description, errno);
2339 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2340 if (code != sizeof(sp->stamp)) {
2343 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2344 sp->description, errno);
2346 ("Unable to write entire version stamp in volume header file (%s)\n",
2351 FDH_REALLYCLOSE(fdP);
2353 if (sp->inodeType == VI_VOLINFO) {
2354 VolInfo = header.volumeInfo;
2357 if (VolInfo.updateDate) {
2358 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2360 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2361 (Testing ? "it would have been " : ""), update);
2363 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2365 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2366 VolInfo.id, update);
2376 SalvageVnodes(register struct InodeSummary *rwIsp,
2377 register struct InodeSummary *thisIsp,
2378 register struct ViceInodeInfo *inodes, int check)
2380 int ilarge, ismall, ioffset, RW, nInodes;
2381 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2384 RW = (rwIsp == thisIsp);
2385 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2387 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2388 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2389 if (check && ismall == -1)
2392 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2393 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2394 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2398 SalvageIndex(Inode ino, VnodeClass class, int RW,
2399 register struct ViceInodeInfo *ip, int nInodes,
2400 struct VolumeSummary *volSummary, int check)
2402 VolumeId volumeNumber;
2403 char buf[SIZEOF_LARGEDISKVNODE];
2404 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2406 StreamHandle_t *file;
2407 struct VnodeClassInfo *vcp;
2409 afs_fsize_t vnodeLength;
2410 int vnodeIndex, nVnodes;
2411 afs_ino_str_t stmp1, stmp2;
2415 volumeNumber = volSummary->header.id;
2416 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2417 fdP = IH_OPEN(handle);
2418 assert(fdP != NULL);
2419 file = FDH_FDOPEN(fdP, "r+");
2420 assert(file != NULL);
2421 vcp = &VnodeClassInfo[class];
2422 size = OS_SIZE(fdP->fd_fd);
2424 nVnodes = (size / vcp->diskSize) - 1;
2426 assert((nVnodes + 1) * vcp->diskSize == size);
2427 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2431 for (vnodeIndex = 0;
2432 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2433 nVnodes--, vnodeIndex++) {
2434 if (vnode->type != vNull) {
2435 int vnodeChanged = 0;
2436 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2437 /* Log programs that belong to root (potentially suid root);
2438 * don't bother for read-only or backup volumes */
2439 #ifdef notdef /* This is done elsewhere */
2440 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2441 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);
2443 if (VNDISK_GET_INO(vnode) == 0) {
2445 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2446 memset(vnode, 0, vcp->diskSize);
2450 if (vcp->magic != vnode->vnodeMagic) {
2451 /* bad magic #, probably partially created vnode */
2452 Log("Partially allocated vnode %d deleted.\n",
2454 memset(vnode, 0, vcp->diskSize);
2458 /* ****** Should do a bit more salvage here: e.g. make sure
2459 * vnode type matches what it should be given the index */
2460 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2461 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2462 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2463 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2470 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2471 /* The following doesn't work, because the version number
2472 * is not maintained correctly by the file server */
2473 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2474 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2476 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2482 /* For RW volume, look for vnode with matching inode number;
2483 * if no such match, take the first determined by our sort
2485 register struct ViceInodeInfo *lip = ip;
2486 register int lnInodes = nInodes;
2488 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2489 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2498 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2499 /* "Matching" inode */
2503 vu = vnode->uniquifier;
2504 iu = ip->u.vnode.vnodeUniquifier;
2505 vd = vnode->dataVersion;
2506 id = ip->u.vnode.inodeDataVersion;
2508 * Because of the possibility of the uniquifier overflows (> 4M)
2509 * we compare them modulo the low 22-bits; we shouldn't worry
2510 * about mismatching since they shouldn't to many old
2511 * uniquifiers of the same vnode...
2513 if (IUnique(vu) != IUnique(iu)) {
2515 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2518 vnode->uniquifier = iu;
2519 #ifdef AFS_3DISPARES
2520 vnode->dataVersion = (id >= vd ?
2523 1887437 ? vd : id) :
2526 1887437 ? id : vd));
2528 #if defined(AFS_SGI_EXMAG)
2529 vnode->dataVersion = (id >= vd ?
2532 15099494 ? vd : id) :
2535 15099494 ? id : vd));
2537 vnode->dataVersion = (id > vd ? id : vd);
2538 #endif /* AFS_SGI_EXMAG */
2539 #endif /* AFS_3DISPARES */
2542 /* don't bother checking for vd > id any more, since
2543 * partial file transfers always result in this state,
2544 * and you can't do much else anyway (you've already
2545 * found the best data you can) */
2546 #ifdef AFS_3DISPARES
2547 if (!vnodeIsDirectory(vnodeNumber)
2548 && ((vd < id && (id - vd) < 1887437)
2549 || ((vd > id && (vd - id) > 1887437)))) {
2551 #if defined(AFS_SGI_EXMAG)
2552 if (!vnodeIsDirectory(vnodeNumber)
2553 && ((vd < id && (id - vd) < 15099494)
2554 || ((vd > id && (vd - id) > 15099494)))) {
2556 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2557 #endif /* AFS_SGI_EXMAG */
2560 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2561 vnode->dataVersion = id;
2566 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2569 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);
2571 VNDISK_SET_INO(vnode, ip->inodeNumber);
2576 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);
2578 VNDISK_SET_INO(vnode, ip->inodeNumber);
2581 VNDISK_GET_LEN(vnodeLength, vnode);
2582 if (ip->byteCount != vnodeLength) {
2585 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2590 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2591 VNDISK_SET_LEN(vnode, ip->byteCount);
2595 ip->linkCount--; /* Keep the inode around */
2598 } else { /* no matching inode */
2599 if (VNDISK_GET_INO(vnode) != 0
2600 || vnode->type == vDirectory) {
2601 /* No matching inode--get rid of the vnode */
2603 if (VNDISK_GET_INO(vnode)) {
2605 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2609 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2614 if (VNDISK_GET_INO(vnode)) {
2616 time_t serverModifyTime = vnode->serverModifyTime;
2617 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));
2621 time_t serverModifyTime = vnode->serverModifyTime;
2622 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2625 memset(vnode, 0, vcp->diskSize);
2628 /* Should not reach here becuase we checked for
2629 * (inodeNumber == 0) above. And where we zero the vnode,
2630 * we also goto vnodeDone.
2634 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2638 } /* VNDISK_GET_INO(vnode) != 0 */
2640 assert(!(vnodeChanged && check));
2641 if (vnodeChanged && !Testing) {
2643 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2644 (char *)vnode, vcp->diskSize)
2646 VolumeChanged = 1; /* For break call back */
2657 struct VnodeEssence *
2658 CheckVnodeNumber(VnodeId vnodeNumber)
2661 struct VnodeInfo *vip;
2664 class = vnodeIdToClass(vnodeNumber);
2665 vip = &vnodeInfo[class];
2666 offset = vnodeIdToBitNumber(vnodeNumber);
2667 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2671 CopyOnWrite(register struct DirSummary *dir)
2673 /* Copy the directory unconditionally if we are going to change it:
2674 * not just if was cloned.
2676 struct VnodeDiskObject vnode;
2677 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2678 Inode oldinode, newinode;
2681 if (dir->copied || Testing)
2683 DFlush(); /* Well justified paranoia... */
2686 IH_IREAD(vnodeInfo[vLarge].handle,
2687 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2689 assert(code == sizeof(vnode));
2690 oldinode = VNDISK_GET_INO(&vnode);
2691 /* Increment the version number by a whole lot to avoid problems with
2692 * clients that were promised new version numbers--but the file server
2693 * crashed before the versions were written to disk.
2696 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2697 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2699 assert(VALID_INO(newinode));
2700 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2702 VNDISK_SET_INO(&vnode, newinode);
2704 IH_IWRITE(vnodeInfo[vLarge].handle,
2705 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2707 assert(code == sizeof(vnode));
2709 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2710 fileSysDevice, newinode);
2711 /* Don't delete the original inode right away, because the directory is
2712 * still being scanned.
2718 * This function should either successfully create a new dir, or give up
2719 * and leave things the way they were. In particular, if it fails to write
2720 * the new dir properly, it should return w/o changing the reference to the
2724 CopyAndSalvage(register struct DirSummary *dir)
2726 struct VnodeDiskObject vnode;
2727 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2728 Inode oldinode, newinode;
2733 afs_int32 parentUnique = 1;
2734 struct VnodeEssence *vnodeEssence;
2739 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2741 IH_IREAD(vnodeInfo[vLarge].handle,
2742 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2744 assert(lcode == sizeof(vnode));
2745 oldinode = VNDISK_GET_INO(&vnode);
2746 /* Increment the version number by a whole lot to avoid problems with
2747 * clients that were promised new version numbers--but the file server
2748 * crashed before the versions were written to disk.
2751 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2752 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2754 assert(VALID_INO(newinode));
2755 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2757 /* Assign . and .. vnode numbers from dir and vnode.parent.
2758 * The uniquifier for . is in the vnode.
2759 * The uniquifier for .. might be set to a bogus value of 1 and
2760 * the salvager will later clean it up.
2762 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2763 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2766 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2768 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2773 /* didn't really build the new directory properly, let's just give up. */
2774 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2775 Log("Directory salvage returned code %d, continuing.\n", code);
2777 Log("also failed to decrement link count on new inode");
2781 Log("Checking the results of the directory salvage...\n");
2782 if (!DirOK(&newdir)) {
2783 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2784 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2789 VNDISK_SET_INO(&vnode, newinode);
2790 length = Length(&newdir);
2791 VNDISK_SET_LEN(&vnode, length);
2793 IH_IWRITE(vnodeInfo[vLarge].handle,
2794 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2796 assert(lcode == sizeof(vnode));
2799 nt_sync(fileSysDevice);
2801 sync(); /* this is slow, but hopefully rarely called. We don't have
2802 * an open FD on the file itself to fsync.
2806 vnodeInfo[vLarge].handle->ih_synced = 1;
2808 /* make sure old directory file is really closed */
2809 fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2810 FDH_REALLYCLOSE(fdP);
2812 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2814 dir->dirHandle = newdir;
2818 JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
2821 struct DirSummary *dir = (struct DirSummary *)dirVal;
2822 struct VnodeEssence *vnodeEssence;
2823 afs_int32 dirOrphaned, todelete;
2825 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2827 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2828 if (vnodeEssence == NULL) {
2830 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2834 assert(Delete(&dir->dirHandle, name) == 0);
2839 #ifndef AFS_NAMEI_ENV
2840 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2841 * mount inode for the partition. If this inode were deleted, it would crash
2844 if (vnodeEssence->InodeNumber == 0) {
2845 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"));
2848 assert(Delete(&dir->dirHandle, name) == 0);
2855 if (!(vnodeNumber & 1) && !Showmode
2856 && !(vnodeEssence->count || vnodeEssence->unique
2857 || vnodeEssence->modeBits)) {
2858 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2859 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2860 vnodeNumber, unique,
2861 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2866 assert(Delete(&dir->dirHandle, name) == 0);
2872 /* Check if the Uniquifiers match. If not, change the directory entry
2873 * so its unique matches the vnode unique. Delete if the unique is zero
2874 * or if the directory is orphaned.
2876 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2877 if (!vnodeEssence->unique
2878 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2879 /* This is an orphaned directory. Don't delete the . or ..
2880 * entry. Otherwise, it will get created in the next
2881 * salvage and deleted again here. So Just skip it.
2886 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2889 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")));
2893 fid.Vnode = vnodeNumber;
2894 fid.Unique = vnodeEssence->unique;
2896 assert(Delete(&dir->dirHandle, name) == 0);
2898 assert(Create(&dir->dirHandle, name, &fid) == 0);
2901 return 0; /* no need to continue */
2904 if (strcmp(name, ".") == 0) {
2905 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2908 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2911 assert(Delete(&dir->dirHandle, ".") == 0);
2912 fid.Vnode = dir->vnodeNumber;
2913 fid.Unique = dir->unique;
2914 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2917 vnodeNumber = fid.Vnode; /* Get the new Essence */
2918 unique = fid.Unique;
2919 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2922 } else if (strcmp(name, "..") == 0) {
2925 struct VnodeEssence *dotdot;
2926 pa.Vnode = dir->parent;
2927 dotdot = CheckVnodeNumber(pa.Vnode);
2928 assert(dotdot != NULL); /* XXX Should not be assert */
2929 pa.Unique = dotdot->unique;
2931 pa.Vnode = dir->vnodeNumber;
2932 pa.Unique = dir->unique;
2934 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2936 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2939 assert(Delete(&dir->dirHandle, "..") == 0);
2940 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2943 vnodeNumber = pa.Vnode; /* Get the new Essence */
2945 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2947 dir->haveDotDot = 1;
2948 } else if (strncmp(name, ".__afs", 6) == 0) {
2950 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);
2954 assert(Delete(&dir->dirHandle, name) == 0);
2956 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2957 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2960 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2961 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);
2962 if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
2963 && !(vnodeEssence->modeBits & 0111)) {
2969 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2970 vnodeEssence->InodeNumber);
2973 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
2977 size = FDH_SIZE(fdP);
2979 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
2980 FDH_REALLYCLOSE(fdP);
2987 code = FDH_READ(fdP, buf, size);
2990 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
2991 Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
2992 dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
2993 Testing ? "would convert" : "converted");
2994 vnodeEssence->modeBits |= 0111;
2995 vnodeEssence->changed = 1;
2996 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2997 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2998 dir->name ? dir->name : "??", name, buf);
3000 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3001 dir->vname, vnodeNumber, size, code);
3003 FDH_REALLYCLOSE(fdP);
3006 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3007 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);
3008 if (vnodeIdToClass(vnodeNumber) == vLarge
3009 && vnodeEssence->name == NULL) {
3011 if ((n = (char *)malloc(strlen(name) + 1)))
3013 vnodeEssence->name = n;
3016 /* The directory entry points to the vnode. Check to see if the
3017 * vnode points back to the directory. If not, then let the
3018 * directory claim it (else it might end up orphaned). Vnodes
3019 * already claimed by another directory are deleted from this
3020 * directory: hardlinks to the same vnode are not allowed
3021 * from different directories.
3023 if (vnodeEssence->parent != dir->vnodeNumber) {
3024 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3025 /* Vnode does not point back to this directory.
3026 * Orphaned dirs cannot claim a file (it may belong to
3027 * another non-orphaned dir).
3030 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);
3032 vnodeEssence->parent = dir->vnodeNumber;
3033 vnodeEssence->changed = 1;
3035 /* Vnode was claimed by another directory */
3038 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 " : ""));
3039 } else if (vnodeNumber == 1) {
3040 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 " : ""));
3042 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 " : ""));
3047 assert(Delete(&dir->dirHandle, name) == 0);
3052 /* This directory claims the vnode */
3053 vnodeEssence->claimed = 1;
3055 vnodeEssence->count--;
3060 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3062 register struct VnodeInfo *vip = &vnodeInfo[class];
3063 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3064 char buf[SIZEOF_LARGEDISKVNODE];
3065 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3067 StreamHandle_t *file;
3072 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3073 fdP = IH_OPEN(vip->handle);
3074 assert(fdP != NULL);
3075 file = FDH_FDOPEN(fdP, "r+");
3076 assert(file != NULL);
3077 size = OS_SIZE(fdP->fd_fd);
3079 vip->nVnodes = (size / vcp->diskSize) - 1;
3080 if (vip->nVnodes > 0) {
3081 assert((vip->nVnodes + 1) * vcp->diskSize == size);
3082 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3083 assert((vip->vnodes = (struct VnodeEssence *)
3084 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3085 if (class == vLarge) {
3086 assert((vip->inodes = (Inode *)
3087 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3096 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3097 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3098 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3099 nVnodes--, vnodeIndex++) {
3100 if (vnode->type != vNull) {
3101 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3102 afs_fsize_t vnodeLength;
3103 vip->nAllocatedVnodes++;
3104 vep->count = vnode->linkCount;
3105 VNDISK_GET_LEN(vnodeLength, vnode);
3106 vep->blockCount = nBlocks(vnodeLength);
3107 vip->volumeBlockCount += vep->blockCount;
3108 vep->parent = vnode->parent;
3109 vep->unique = vnode->uniquifier;
3110 if (*maxu < vnode->uniquifier)
3111 *maxu = vnode->uniquifier;
3112 vep->modeBits = vnode->modeBits;
3113 vep->InodeNumber = VNDISK_GET_INO(vnode);
3114 vep->type = vnode->type;
3115 vep->author = vnode->author;
3116 vep->owner = vnode->owner;
3117 vep->group = vnode->group;
3118 if (vnode->type == vDirectory) {
3119 if (class != vLarge) {
3120 VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3121 vip->nAllocatedVnodes--;
3122 memset(vnode, 0, sizeof(vnode));
3123 IH_IWRITE(vnodeInfo[vSmall].handle,
3124 vnodeIndexOffset(vcp, vnodeNumber),
3125 (char *)&vnode, sizeof(vnode));
3128 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3137 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3139 struct VnodeEssence *parentvp;
3145 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3146 && GetDirName(vp->parent, parentvp, path)) {
3148 strcat(path, vp->name);
3154 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3155 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3158 IsVnodeOrphaned(VnodeId vnode)
3160 struct VnodeEssence *vep;
3163 return (1); /* Vnode zero does not exist */
3165 return (0); /* The root dir vnode is always claimed */
3166 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3167 if (!vep || !vep->claimed)
3168 return (1); /* Vnode is not claimed - it is orphaned */
3170 return (IsVnodeOrphaned(vep->parent));
3174 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3175 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3178 static struct DirSummary dir;
3179 static struct DirHandle dirHandle;
3180 struct VnodeEssence *parent;
3181 static char path[MAXPATHLEN];
3184 if (dirVnodeInfo->vnodes[i].salvaged)
3185 return; /* already salvaged */
3188 dirVnodeInfo->vnodes[i].salvaged = 1;
3190 if (dirVnodeInfo->inodes[i] == 0)
3191 return; /* Not allocated to a directory */
3193 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3194 if (dirVnodeInfo->vnodes[i].parent) {
3195 Log("Bad parent, vnode 1; %s...\n",
3196 (Testing ? "skipping" : "salvaging"));
3197 dirVnodeInfo->vnodes[i].parent = 0;
3198 dirVnodeInfo->vnodes[i].changed = 1;
3201 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3202 if (parent && parent->salvaged == 0)
3203 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3204 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3205 rootdir, rootdirfound);
3208 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3209 dir.unique = dirVnodeInfo->vnodes[i].unique;
3212 dir.parent = dirVnodeInfo->vnodes[i].parent;
3213 dir.haveDot = dir.haveDotDot = 0;
3214 dir.ds_linkH = alinkH;
3215 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3216 dirVnodeInfo->inodes[i]);
3218 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3221 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3222 (Testing ? "skipping" : "salvaging"));
3225 CopyAndSalvage(&dir);
3229 dirHandle = dir.dirHandle;
3232 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3233 &dirVnodeInfo->vnodes[i], path);
3236 /* If enumeration failed for random reasons, we will probably delete
3237 * too much stuff, so we guard against this instead.
3239 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3242 /* Delete the old directory if it was copied in order to salvage.
3243 * CopyOnWrite has written the new inode # to the disk, but we still
3244 * have the old one in our local structure here. Thus, we idec the
3248 if (dir.copied && !Testing) {
3249 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3251 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3254 /* Remember rootdir DirSummary _after_ it has been judged */
3255 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3256 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3264 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3266 /* This routine, for now, will only be called for read-write volumes */
3268 int BlocksInVolume = 0, FilesInVolume = 0;
3269 register VnodeClass class;
3270 struct DirSummary rootdir, oldrootdir;
3271 struct VnodeInfo *dirVnodeInfo;
3272 struct VnodeDiskObject vnode;
3273 VolumeDiskData volHeader;
3275 int orphaned, rootdirfound = 0;
3276 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3277 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3278 struct VnodeEssence *vep;
3281 afs_sfsize_t nBytes;
3283 VnodeId LFVnode, ThisVnode;
3284 Unique LFUnique, ThisUnique;
3287 vid = rwIsp->volSummary->header.id;
3288 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3289 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3290 assert(nBytes == sizeof(volHeader));
3291 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3292 assert(volHeader.destroyMe != DESTROY_ME);
3293 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3295 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3297 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3300 dirVnodeInfo = &vnodeInfo[vLarge];
3301 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3302 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3306 nt_sync(fileSysDevice);
3308 sync(); /* This used to be done lower level, for every dir */
3315 /* Parse each vnode looking for orphaned vnodes and
3316 * connect them to the tree as orphaned (if requested).
3318 oldrootdir = rootdir;
3319 for (class = 0; class < nVNODECLASSES; class++) {
3320 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3321 vep = &(vnodeInfo[class].vnodes[v]);
3322 ThisVnode = bitNumberToVnodeNumber(v, class);
3323 ThisUnique = vep->unique;
3325 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3326 continue; /* Ignore unused, claimed, and root vnodes */
3328 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3329 * entry in this vnode had incremented the parent link count (In
3330 * JudgeEntry()). We need to go to the parent and decrement that
3331 * link count. But if the parent's unique is zero, then the parent
3332 * link count was not incremented in JudgeEntry().
3334 if (class == vLarge) { /* directory vnode */
3335 pv = vnodeIdToBitNumber(vep->parent);
3336 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3337 vnodeInfo[vLarge].vnodes[pv].count++;
3341 continue; /* If no rootdir, can't attach orphaned files */
3343 /* Here we attach orphaned files and directories into the
3344 * root directory, LVVnode, making sure link counts stay correct.
3346 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3347 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3348 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3350 /* Update this orphaned vnode's info. Its parent info and
3351 * link count (do for orphaned directories and files).
3353 vep->parent = LFVnode; /* Parent is the root dir */
3354 vep->unique = LFUnique;
3357 vep->count--; /* Inc link count (root dir will pt to it) */
3359 /* If this orphaned vnode is a directory, change '..'.
3360 * The name of the orphaned dir/file is unknown, so we
3361 * build a unique name. No need to CopyOnWrite the directory
3362 * since it is not connected to tree in BK or RO volume and
3363 * won't be visible there.
3365 if (class == vLarge) {
3369 /* Remove and recreate the ".." entry in this orphaned directory */
3370 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3371 vnodeInfo[class].inodes[v]);
3373 pa.Unique = LFUnique;
3374 assert(Delete(&dh, "..") == 0);
3375 assert(Create(&dh, "..", &pa) == 0);
3377 /* The original parent's link count was decremented above.
3378 * Here we increment the new parent's link count.
3380 pv = vnodeIdToBitNumber(LFVnode);
3381 vnodeInfo[vLarge].vnodes[pv].count--;
3385 /* Go to the root dir and add this entry. The link count of the
3386 * root dir was incremented when ".." was created. Try 10 times.
3388 for (j = 0; j < 10; j++) {
3389 pa.Vnode = ThisVnode;
3390 pa.Unique = ThisUnique;
3392 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3394 vLarge) ? "__ORPHANDIR__" :
3395 "__ORPHANFILE__"), ThisVnode,
3398 CopyOnWrite(&rootdir);
3399 code = Create(&rootdir.dirHandle, npath, &pa);
3403 ThisUnique += 50; /* Try creating a different file */
3406 Log("Attaching orphaned %s to volume's root dir as %s\n",
3407 ((class == vLarge) ? "directory" : "file"), npath);
3409 } /* for each vnode in the class */
3410 } /* for each class of vnode */
3412 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3414 if (!oldrootdir.copied && rootdir.copied) {
3416 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3419 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3422 DFlush(); /* Flush the changes */
3423 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3424 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3425 orphans = ORPH_IGNORE;
3428 /* Write out all changed vnodes. Orphaned files and directories
3429 * will get removed here also (if requested).
3431 for (class = 0; class < nVNODECLASSES; class++) {
3432 int nVnodes = vnodeInfo[class].nVnodes;
3433 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3434 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3435 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3436 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3437 for (i = 0; i < nVnodes; i++) {
3438 register struct VnodeEssence *vnp = &vnodes[i];
3439 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3441 /* If the vnode is good but is unclaimed (not listed in
3442 * any directory entries), then it is orphaned.
3445 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3446 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3450 if (vnp->changed || vnp->count) {
3454 IH_IREAD(vnodeInfo[class].handle,
3455 vnodeIndexOffset(vcp, vnodeNumber),
3456 (char *)&vnode, sizeof(vnode));
3457 assert(nBytes == sizeof(vnode));
3459 vnode.parent = vnp->parent;
3460 oldCount = vnode.linkCount;
3461 vnode.linkCount = vnode.linkCount - vnp->count;
3464 orphaned = IsVnodeOrphaned(vnodeNumber);
3466 if (!vnp->todelete) {
3467 /* Orphans should have already been attached (if requested) */
3468 assert(orphans != ORPH_ATTACH);
3469 oblocks += vnp->blockCount;
3472 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3474 BlocksInVolume -= vnp->blockCount;
3476 if (VNDISK_GET_INO(&vnode)) {
3478 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3481 memset(&vnode, 0, sizeof(vnode));
3483 } else if (vnp->count) {
3485 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3488 vnode.modeBits = vnp->modeBits;
3491 vnode.dataVersion++;
3494 IH_IWRITE(vnodeInfo[class].handle,
3495 vnodeIndexOffset(vcp, vnodeNumber),
3496 (char *)&vnode, sizeof(vnode));
3497 assert(nBytes == sizeof(vnode));
3503 if (!Showmode && ofiles) {
3504 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3506 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3510 for (class = 0; class < nVNODECLASSES; class++) {
3511 register struct VnodeInfo *vip = &vnodeInfo[class];
3512 for (i = 0; i < vip->nVnodes; i++)
3513 if (vip->vnodes[i].name)
3514 free(vip->vnodes[i].name);
3521 /* Set correct resource utilization statistics */
3522 volHeader.filecount = FilesInVolume;
3523 volHeader.diskused = BlocksInVolume;
3525 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3526 if (volHeader.uniquifier < (maxunique + 1)) {
3528 Log("Volume uniquifier is too low; fixed\n");
3529 /* Plus 2,000 in case there are workstations out there with
3530 * cached vnodes that have since been deleted
3532 volHeader.uniquifier = (maxunique + 1 + 2000);
3535 /* Turn off the inUse bit; the volume's been salvaged! */
3536 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3537 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3538 volHeader.inService = 1; /* allow service again */
3539 volHeader.needsCallback = (VolumeChanged != 0);
3540 volHeader.dontSalvage = DONT_SALVAGE;
3543 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3544 assert(nBytes == sizeof(volHeader));
3547 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3548 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3549 FilesInVolume, BlocksInVolume);
3551 IH_RELEASE(vnodeInfo[vSmall].handle);
3552 IH_RELEASE(vnodeInfo[vLarge].handle);
3558 ClearROInUseBit(struct VolumeSummary *summary)
3560 IHandle_t *h = summary->volumeInfoHandle;
3561 afs_sfsize_t nBytes;
3563 VolumeDiskData volHeader;
3565 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3566 assert(nBytes == sizeof(volHeader));
3567 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3568 volHeader.inUse = 0;
3569 volHeader.needsSalvaged = 0;
3570 volHeader.inService = 1;
3571 volHeader.dontSalvage = DONT_SALVAGE;
3573 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3574 assert(nBytes == sizeof(volHeader));
3579 * Possible delete the volume.
3581 * deleteMe - Always do so, only a partial volume.
3584 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3587 if (readOnly(isp) || deleteMe) {
3588 if (isp->volSummary && isp->volSummary->fileName) {
3591 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);
3593 Log("It will be deleted on this server (you may find it elsewhere)\n");
3596 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3598 Log("it will be deleted instead. It should be recloned.\n");
3603 sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
3605 code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
3607 Log("Error %ld destroying volume disk header for volume %lu\n",
3608 afs_printable_int32_ld(code),
3609 afs_printable_uint32_lu(isp->volumeId));
3612 /* make sure we actually delete the fileName file; ENOENT
3613 * is fine, since VDestroyVolumeDiskHeader probably already
3615 if (unlink(path) && errno != ENOENT) {
3616 Log("Unable to unlink %s (errno = %d)\n", path, errno);
3620 } else if (!check) {
3621 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3623 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3629 AskOffline(VolumeId volumeId, char * partition)
3634 memset(&res, 0, sizeof(res));
3636 for (i = 0; i < 3; i++) {
3637 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
3639 if (code == SYNC_OK) {
3641 } else if (code == SYNC_DENIED) {
3642 #ifdef DEMAND_ATTACH_ENABLE
3643 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3645 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3647 Abort("Salvage aborted\n");
3648 } else if (code == SYNC_BAD_COMMAND) {
3649 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3651 #ifdef DEMAND_ATTACH_ENABLE
3652 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3654 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3656 Abort("Salvage aborted\n");
3659 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3660 FSYNC_clientFinis();
3664 if (code != SYNC_OK) {
3665 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3666 Abort("Salvage aborted\n");
3669 #ifdef AFS_DEMAND_ATTACH_FS
3670 /* set inUse = programType in the volume header. We do this in case
3671 * the fileserver restarts/crashes while we are salvaging.
3672 * Otherwise, the fileserver could attach the volume again on
3673 * startup while we are salvaging, which would be very bad, or
3674 * schedule another salvage while we are salvaging, which would be
3678 struct VolumeHeader header;
3679 struct VolumeDiskHeader diskHeader;
3680 struct VolumeDiskData volHeader;
3682 code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
3687 DiskToVolumeHeader(&header, &diskHeader);
3689 IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
3690 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
3691 volHeader.stamp.magic != VOLUMEINFOMAGIC) {
3697 volHeader.inUse = programType;
3699 /* If we can't re-write the header, bail out and error. We don't
3700 * assert when reading the header, since it's possible the
3701 * header isn't really there (when there's no data associated
3702 * with the volume; we just delete the vol header file in that
3703 * case). But if it's there enough that we can read it, but
3704 * somehow we cannot write to it to signify we're salvaging it,
3705 * we've got a big problem and we cannot continue. */
3706 assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
3710 #endif /* AFS_DEMAND_ATTACH_FS */
3714 AskOnline(VolumeId volumeId, char *partition)
3718 for (i = 0; i < 3; i++) {
3719 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3721 if (code == SYNC_OK) {
3723 } else if (code == SYNC_DENIED) {
3724 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3725 } else if (code == SYNC_BAD_COMMAND) {
3726 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3728 #ifdef DEMAND_ATTACH_ENABLE
3729 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3731 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3736 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3737 FSYNC_clientFinis();
3744 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3746 /* Volume parameter is passed in case iopen is upgraded in future to
3747 * require a volume Id to be passed
3750 IHandle_t *srcH, *destH;
3751 FdHandle_t *srcFdP, *destFdP;
3754 IH_INIT(srcH, device, rwvolume, inode1);
3755 srcFdP = IH_OPEN(srcH);
3756 assert(srcFdP != NULL);
3757 IH_INIT(destH, device, rwvolume, inode2);
3758 destFdP = IH_OPEN(destH);
3760 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3761 assert(FDH_WRITE(destFdP, buf, n) == n);
3763 FDH_REALLYCLOSE(srcFdP);
3764 FDH_REALLYCLOSE(destFdP);
3771 PrintInodeList(void)
3773 register struct ViceInodeInfo *ip;
3774 struct ViceInodeInfo *buf;
3775 struct afs_stat status;
3776 register int nInodes;
3778 assert(afs_fstat(inodeFd, &status) == 0);
3779 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3780 assert(buf != NULL);
3781 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3782 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3783 for (ip = buf; nInodes--; ip++) {
3784 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3785 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3786 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3787 ip->u.param[2], ip->u.param[3]);
3793 PrintInodeSummary(void)
3796 struct InodeSummary *isp;
3798 for (i = 0; i < nVolumesInInodeFile; i++) {
3799 isp = &inodeSummary[i];
3800 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);
3805 PrintVolumeSummary(void)
3808 struct VolumeSummary *vsp;
3810 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3811 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3821 assert(0); /* Fork is never executed in the NT code path */
3825 #ifdef AFS_DEMAND_ATTACH_FS
3826 if ((f == 0) && (programType == salvageServer)) {
3827 /* we are a salvageserver child */
3828 #ifdef FSSYNC_BUILD_CLIENT
3829 VChildProcReconnectFS_r();
3831 #ifdef SALVSYNC_BUILD_CLIENT
3835 #endif /* AFS_DEMAND_ATTACH_FS */
3836 #endif /* !AFS_NT40_ENV */
3846 #ifdef AFS_DEMAND_ATTACH_FS
3847 if (programType == salvageServer) {
3848 #ifdef SALVSYNC_BUILD_CLIENT
3851 #ifdef FSSYNC_BUILD_CLIENT
3855 #endif /* AFS_DEMAND_ATTACH_FS */
3858 if (main_thread != pthread_self())
3859 pthread_exit((void *)code);
3872 pid = wait(&status);
3874 if (WCOREDUMP(status))
3875 Log("\"%s\" core dumped!\n", prog);
3876 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3882 TimeStamp(time_t clock, int precision)
3885 static char timestamp[20];
3886 lt = localtime(&clock);
3888 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
3890 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3895 CheckLogFile(char * log_path)
3897 char oldSlvgLog[AFSDIR_PATH_MAX];
3899 #ifndef AFS_NT40_ENV
3906 strcpy(oldSlvgLog, log_path);
3907 strcat(oldSlvgLog, ".old");
3909 renamefile(log_path, oldSlvgLog);
3910 logFile = afs_fopen(log_path, "a");
3912 if (!logFile) { /* still nothing, use stdout */
3916 #ifndef AFS_NAMEI_ENV
3917 AFS_DEBUG_IOPS_LOG(logFile);
3922 #ifndef AFS_NT40_ENV
3924 TimeStampLogFile(char * log_path)
3926 char stampSlvgLog[AFSDIR_PATH_MAX];
3931 lt = localtime(&now);
3932 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3933 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3934 log_path, lt->tm_year + 1900,
3935 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3938 /* try to link the logfile to a timestamped filename */
3939 /* if it fails, oh well, nothing we can do */
3940 link(log_path, stampSlvgLog);
3949 #ifndef AFS_NT40_ENV
3951 printf("Can't show log since using syslog.\n");
3962 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3965 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3968 while (fgets(line, sizeof(line), logFile))
3975 Log(const char *format, ...)
3981 va_start(args, format);
3982 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3984 #ifndef AFS_NT40_ENV
3986 syslog(LOG_INFO, "%s", tmp);
3990 gettimeofday(&now, 0);
3991 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3997 Abort(const char *format, ...)
4002 va_start(args, format);
4003 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4005 #ifndef AFS_NT40_ENV
4007 syslog(LOG_INFO, "%s", tmp);
4011 fprintf(logFile, "%s", tmp);
4023 ToString(const char *s)
4026 p = (char *)malloc(strlen(s) + 1);
4032 /* Remove the FORCESALVAGE file */
4034 RemoveTheForce(char *path)
4037 struct afs_stat force; /* so we can use afs_stat to find it */
4038 strcpy(target,path);
4039 strcat(target,"/FORCESALVAGE");
4040 if (!Testing && ForceSalvage) {
4041 if (afs_stat(target,&force) == 0) unlink(target);
4045 #ifndef AFS_AIX32_ENV
4047 * UseTheForceLuke - see if we can use the force
4050 UseTheForceLuke(char *path)
4052 struct afs_stat force;
4054 strcpy(target,path);
4055 strcat(target,"/FORCESALVAGE");
4057 return (afs_stat(target, &force) == 0);
4061 * UseTheForceLuke - see if we can use the force
4064 * The VRMIX fsck will not muck with the filesystem it is supposedly
4065 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
4066 * muck directly with the root inode, which is within the normal
4068 * ListViceInodes() has a side effect of setting ForceSalvage if
4069 * it detects a need, based on root inode examination.
4072 UseTheForceLuke(char *path)
4075 return 0; /* sorry OB1 */
4080 /* NT support routines */
4082 static char execpathname[MAX_PATH];
4084 nt_SalvagePartition(char *partName, int jobn)
4089 if (!*execpathname) {
4090 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
4091 if (!n || n == 1023)
4094 job.cj_magic = SALVAGER_MAGIC;
4095 job.cj_number = jobn;
4096 (void)strcpy(job.cj_part, partName);
4097 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
4102 nt_SetupPartitionSalvage(void *datap, int len)
4104 childJob_t *jobp = (childJob_t *) datap;
4105 char logname[AFSDIR_PATH_MAX];
4107 if (len != sizeof(childJob_t))
4109 if (jobp->cj_magic != SALVAGER_MAGIC)
4114 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
4116 logFile = afs_fopen(logname, "w");
4124 #endif /* AFS_NT40_ENV */