2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #include <afsconfig.h>
87 #include <afs/param.h>
93 #include <sys/param.h>
97 #endif /* ITIMER_REAL */
103 #include <sys/stat.h>
108 #include <WINNT/afsevent.h>
110 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
111 #define WCOREDUMP(x) (x & 0200)
114 #include <afs/afsint.h>
115 #include <afs/assert.h>
116 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
117 #if defined(AFS_VFSINCL_ENV)
118 #include <sys/vnode.h>
120 #include <sys/fs/ufs_inode.h>
122 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
123 #include <ufs/ufs/dinode.h>
124 #include <ufs/ffs/fs.h>
126 #include <ufs/inode.h>
129 #else /* AFS_VFSINCL_ENV */
131 #include <ufs/inode.h>
132 #else /* AFS_OSF_ENV */
133 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
134 #include <sys/inode.h>
137 #endif /* AFS_VFSINCL_ENV */
138 #endif /* AFS_SGI_ENV */
141 #include <sys/lockf.h>
145 #include <checklist.h>
147 #if defined(AFS_SGI_ENV)
152 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
155 #include <sys/mnttab.h>
156 #include <sys/mntent.h>
161 #endif /* AFS_SGI_ENV */
162 #endif /* AFS_HPUX_ENV */
167 #include <afs/osi_inode.h>
170 #include <afs/afsutil.h>
171 #include <afs/fileutil.h>
172 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
180 #include <afs/afssyscalls.h>
184 #include "partition.h"
185 #include "daemon_com.h"
187 #include "salvsync.h"
188 #include "viceinode.h"
190 #include "volinodes.h" /* header magic number, etc. stuff */
191 #include "vol-salvage.h"
196 /*@+fcnmacros +macrofcndecl@*/
199 extern off64_t afs_lseek(int FD, off64_t O, int F);
200 #endif /*S_SPLINT_S */
201 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
202 #define afs_stat stat64
203 #define afs_fstat fstat64
204 #define afs_open open64
205 #define afs_fopen fopen64
206 #else /* !O_LARGEFILE */
208 extern off_t afs_lseek(int FD, off_t O, int F);
209 #endif /*S_SPLINT_S */
210 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
211 #define afs_stat stat
212 #define afs_fstat fstat
213 #define afs_open open
214 #define afs_fopen fopen
215 #endif /* !O_LARGEFILE */
216 /*@=fcnmacros =macrofcndecl@*/
219 extern void *calloc();
221 static char *TimeStamp(time_t clock, int precision);
224 int debug; /* -d flag */
225 int Testing = 0; /* -n flag */
226 int ListInodeOption; /* -i flag */
227 int ShowRootFiles; /* -r flag */
228 int RebuildDirs; /* -sal flag */
229 int Parallel = 4; /* -para X flag */
230 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
231 int forceR = 0; /* -b flag */
232 int ShowLog = 0; /* -showlog flag */
233 int ShowSuid = 0; /* -showsuid flag */
234 int ShowMounts = 0; /* -showmounts flag */
235 int orphans = ORPH_IGNORE; /* -orphans option */
239 int useSyslog = 0; /* -syslog flag */
240 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
243 #define MAXPARALLEL 32
245 int OKToZap; /* -o flag */
246 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
247 * in the volume header */
249 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
251 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
253 Device fileSysDevice; /* The device number of the current
254 * partition being salvaged */
258 char *fileSysPath; /* The path of the mounted partition currently
259 * being salvaged, i.e. the directory
260 * containing the volume headers */
262 char *fileSysPathName; /* NT needs this to make name pretty in log. */
263 IHandle_t *VGLinkH; /* Link handle for current volume group. */
264 int VGLinkH_cnt; /* # of references to lnk handle. */
265 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
267 char *fileSysDeviceName; /* The block device where the file system
268 * being salvaged was mounted */
269 char *filesysfulldev;
271 int VolumeChanged; /* Set by any routine which would change the volume in
272 * a way which would require callback is to be broken if the
273 * volume was put back on line by an active file server */
275 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
277 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
278 int inodeFd; /* File descriptor for inode file */
281 struct VnodeInfo vnodeInfo[nVNODECLASSES];
284 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
285 int nVolumes; /* Number of volumes (read-write and read-only)
286 * in volume summary */
292 /* Forward declarations */
293 /*@printflike@*/ void Log(const char *format, ...);
294 /*@printflike@*/ void Abort(const char *format, ...);
295 static int IsVnodeOrphaned(VnodeId vnode);
297 /* Uniquifier stored in the Inode */
302 return (u & 0x3fffff);
304 #if defined(AFS_SGI_EXMAG)
305 return (u & SGI_UNIQMASK);
308 #endif /* AFS_SGI_EXMAG */
313 BadError(register int aerror)
315 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
317 return 0; /* otherwise may be transient, e.g. EMFILE */
323 char *save_args[MAX_ARGS];
325 pthread_t main_thread;
329 /* Get the salvage lock if not already held. Hold until process exits. */
331 ObtainSalvageLock(void)
337 (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
338 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
339 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
341 "salvager: There appears to be another salvager running! Aborted.\n");
346 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
347 if (salvageLock < 0) {
349 "salvager: can't open salvage lock file %s, aborting\n",
350 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
353 #ifdef AFS_DARWIN_ENV
354 if (flock(salvageLock, LOCK_EX) == -1) {
356 if (lockf(salvageLock, F_LOCK, 0) == -1) {
359 "salvager: There appears to be another salvager running! Aborted.\n");
366 #ifdef AFS_SGI_XFS_IOPS_ENV
367 /* Check if the given partition is mounted. For XFS, the root inode is not a
368 * constant. So we check the hard way.
371 IsPartitionMounted(char *part)
374 struct mntent *mntent;
376 assert(mntfp = setmntent(MOUNTED, "r"));
377 while (mntent = getmntent(mntfp)) {
378 if (!strcmp(part, mntent->mnt_dir))
383 return mntent ? 1 : 1;
386 /* Check if the given inode is the root of the filesystem. */
387 #ifndef AFS_SGI_XFS_IOPS_ENV
389 IsRootInode(struct afs_stat *status)
392 * The root inode is not a fixed value in XFS partitions. So we need to
393 * see if the partition is in the list of mounted partitions. This only
394 * affects the SalvageFileSys path, so we check there.
396 return (status->st_ino == ROOTINODE);
401 #ifndef AFS_NAMEI_ENV
402 /* We don't want to salvage big files filesystems, since we can't put volumes on
406 CheckIfBigFilesFS(char *mountPoint, char *devName)
408 struct superblock fs;
411 if (strncmp(devName, "/dev/", 5)) {
412 (void)sprintf(name, "/dev/%s", devName);
414 (void)strcpy(name, devName);
417 if (ReadSuper(&fs, name) < 0) {
418 Log("Unable to read superblock. Not salvaging partition %s.\n",
422 if (IsBigFilesFileSystem(&fs)) {
423 Log("Partition %s is a big files filesystem, not salvaging.\n",
433 #define HDSTR "\\Device\\Harddisk"
434 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
436 SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
441 static int dowarn = 1;
443 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
445 if (strncmp(res, HDSTR, HDLEN)) {
448 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
449 res, HDSTR, p1->devName);
453 d1 = atoi(&res[HDLEN]);
455 if (!QueryDosDevice(p2->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, p2->devName);
465 d2 = atoi(&res[HDLEN]);
470 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
473 /* This assumes that two partitions with the same device number divided by
474 * PartsPerDisk are on the same disk.
477 SalvageFileSysParallel(struct DiskPartition *partP)
480 struct DiskPartition *partP;
481 int pid; /* Pid for this job */
482 int jobnumb; /* Log file job number */
483 struct job *nextjob; /* Next partition on disk to salvage */
485 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
486 struct job *thisjob = 0;
487 static int numjobs = 0;
488 static int jobcount = 0;
494 char logFileName[256];
498 /* We have a partition to salvage. Copy it into thisjob */
499 thisjob = (struct job *)malloc(sizeof(struct job));
501 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
504 memset(thisjob, 0, sizeof(struct job));
505 thisjob->partP = partP;
506 thisjob->jobnumb = jobcount;
508 } else if (jobcount == 0) {
509 /* We are asking to wait for all jobs (partp == 0), yet we never
512 Log("No file system partitions named %s* found; not salvaged\n",
513 VICE_PARTITION_PREFIX);
517 if (debug || Parallel == 1) {
519 SalvageFileSys(thisjob->partP, 0);
526 /* Check to see if thisjob is for a disk that we are already
527 * salvaging. If it is, link it in as the next job to do. The
528 * jobs array has 1 entry per disk being salvages. numjobs is
529 * the total number of disks currently being salvaged. In
530 * order to keep thejobs array compact, when a disk is
531 * completed, the hightest element in the jobs array is moved
532 * down to now open slot.
534 for (j = 0; j < numjobs; j++) {
535 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
536 /* On same disk, add it to this list and return */
537 thisjob->nextjob = jobs[j]->nextjob;
538 jobs[j]->nextjob = thisjob;
545 /* Loop until we start thisjob or until all existing jobs are finished */
546 while (thisjob || (!partP && (numjobs > 0))) {
547 startjob = -1; /* No new job to start */
549 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
550 /* Either the max jobs are running or we have to wait for all
551 * the jobs to finish. In either case, we wait for at least one
552 * job to finish. When it's done, clean up after it.
554 pid = wait(&wstatus);
556 for (j = 0; j < numjobs; j++) { /* Find which job it is */
557 if (pid == jobs[j]->pid)
561 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
562 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
565 numjobs--; /* job no longer running */
566 oldjob = jobs[j]; /* remember */
567 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
568 free(oldjob); /* free the old job */
570 /* If there is another partition on the disk to salvage, then
571 * say we will start it (startjob). If not, then put thisjob there
572 * and say we will start it.
574 if (jobs[j]) { /* Another partitions to salvage */
575 startjob = j; /* Will start it */
576 } else { /* There is not another partition to salvage */
578 jobs[j] = thisjob; /* Add thisjob */
580 startjob = j; /* Will start it */
582 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
583 startjob = -1; /* Don't start it - already running */
587 /* We don't have to wait for a job to complete */
589 jobs[numjobs] = thisjob; /* Add this job */
591 startjob = numjobs; /* Will start it */
595 /* Start up a new salvage job on a partition in job slot "startjob" */
596 if (startjob != -1) {
598 Log("Starting salvage of file system partition %s\n",
599 jobs[startjob]->partP->name);
601 /* For NT, we not only fork, but re-exec the salvager. Pass in the
602 * commands and pass the child job number via the data path.
605 nt_SalvagePartition(jobs[startjob]->partP->name,
606 jobs[startjob]->jobnumb);
607 jobs[startjob]->pid = pid;
612 jobs[startjob]->pid = pid;
618 for (fd = 0; fd < 16; fd++)
625 openlog("salvager", LOG_PID, useSyslogFacility);
629 (void)afs_snprintf(logFileName, sizeof logFileName,
631 AFSDIR_SERVER_SLVGLOG_FILEPATH,
632 jobs[startjob]->jobnumb);
633 logFile = afs_fopen(logFileName, "w");
638 SalvageFileSys1(jobs[startjob]->partP, 0);
643 } /* while ( thisjob || (!partP && numjobs > 0) ) */
645 /* If waited for all jobs to complete, now collect log files and return */
647 if (!useSyslog) /* if syslogging - no need to collect */
650 for (i = 0; i < jobcount; i++) {
651 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
652 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
653 if ((passLog = afs_fopen(logFileName, "r"))) {
654 while (fgets(buf, sizeof(buf), passLog)) {
659 (void)unlink(logFileName);
668 SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
670 if (!canfork || debug || Fork() == 0) {
671 SalvageFileSys1(partP, singleVolumeNumber);
672 if (canfork && !debug) {
677 Wait("SalvageFileSys");
681 get_DevName(char *pbuffer, char *wpath)
683 char pbuf[128], *ptr;
684 strcpy(pbuf, pbuffer);
685 ptr = (char *)strrchr(pbuf, '/');
691 ptr = (char *)strrchr(pbuffer, '/');
693 strcpy(pbuffer, ptr + 1);
700 SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
703 char inodeListPath[256];
704 static char tmpDevName[100];
705 static char wpath[100];
706 struct VolumeSummary *vsp, *esp;
709 fileSysPartition = partP;
710 fileSysDevice = fileSysPartition->device;
711 fileSysPathName = VPartitionPath(fileSysPartition);
714 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
715 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
716 name = partP->devName;
718 fileSysPath = fileSysPathName;
719 strcpy(tmpDevName, partP->devName);
720 name = get_DevName(tmpDevName, wpath);
721 fileSysDeviceName = name;
722 filesysfulldev = wpath;
725 VLockPartition(partP->name);
726 if (singleVolumeNumber || ForceSalvage)
729 ForceSalvage = UseTheForceLuke(fileSysPath);
731 if (singleVolumeNumber) {
732 /* salvageserver already setup fssync conn for us */
733 if ((programType != salvageServer) && !VConnectFS()) {
734 Abort("Couldn't connect to file server\n");
736 AskOffline(singleVolumeNumber);
739 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
740 partP->name, name, (Testing ? "(READONLY mode)" : ""));
742 Log("***Forced salvage of all volumes on this partition***\n");
747 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
754 assert((dirp = opendir(fileSysPath)) != NULL);
755 while ((dp = readdir(dirp))) {
756 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
757 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
759 Log("Removing old salvager temp files %s\n", dp->d_name);
760 strcpy(npath, fileSysPath);
762 strcat(npath, dp->d_name);
768 tdir = (tmpdir ? tmpdir : fileSysPath);
770 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
771 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
773 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
776 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
777 unlink(inodeListPath);
781 /* Using nt_unlink here since we're really using the delete on close
782 * semantics of unlink. In most places in the salvager, we really do
783 * mean to unlink the file at that point. Those places have been
784 * modified to actually do that so that the NT crt can be used there.
787 _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
788 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
790 inodeFd = afs_open(inodeListPath, O_RDONLY);
791 unlink(inodeListPath);
794 Abort("Temporary file %s is missing...\n", inodeListPath);
795 if (ListInodeOption) {
799 /* enumerate volumes in the partition.
800 * figure out sets of read-only + rw volumes.
801 * salvage each set, read-only volumes first, then read-write.
802 * Fix up inodes on last volume in set (whether it is read-write
805 GetVolumeSummary(singleVolumeNumber);
807 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
808 i < nVolumesInInodeFile; i = j) {
809 VolumeId rwvid = inodeSummary[i].RWvolumeId;
811 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
813 VolumeId vid = inodeSummary[j].volumeId;
814 struct VolumeSummary *tsp;
815 /* Scan volume list (from partition root directory) looking for the
816 * current rw volume number in the volume list from the inode scan.
817 * If there is one here that is not in the inode volume list,
819 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
821 DeleteExtraVolumeHeaderFile(vsp);
823 /* Now match up the volume summary info from the root directory with the
824 * entry in the volume list obtained from scanning inodes */
825 inodeSummary[j].volSummary = NULL;
826 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
827 if (tsp->header.id == vid) {
828 inodeSummary[j].volSummary = tsp;
834 /* Salvage the group of volumes (several read-only + 1 read/write)
835 * starting with the current read-only volume we're looking at.
837 SalvageVolumeGroup(&inodeSummary[i], j - i);
840 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
841 for (; vsp < esp; vsp++) {
843 DeleteExtraVolumeHeaderFile(vsp);
846 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
847 RemoveTheForce(fileSysPath);
849 if (!Testing && singleVolumeNumber) {
850 AskOnline(singleVolumeNumber, fileSysPartition->name);
852 /* Step through the volumeSummary list and set all volumes on-line.
853 * The volumes were taken off-line in GetVolumeSummary.
855 for (j = 0; j < nVolumes; j++) {
856 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
860 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
861 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
864 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
868 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
871 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
873 unlink(vsp->fileName);
877 CompareInodes(const void *_p1, const void *_p2)
879 register const struct ViceInodeInfo *p1 = _p1;
880 register const struct ViceInodeInfo *p2 = _p2;
881 if (p1->u.vnode.vnodeNumber == INODESPECIAL
882 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
883 VolumeId p1rwid, p2rwid;
885 (p1->u.vnode.vnodeNumber ==
886 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
888 (p2->u.vnode.vnodeNumber ==
889 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
894 if (p1->u.vnode.vnodeNumber == INODESPECIAL
895 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
896 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
897 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
898 if (p1->u.vnode.volumeId == p1rwid)
900 if (p2->u.vnode.volumeId == p2rwid)
902 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
904 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
905 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
906 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
908 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
910 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
912 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
914 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
916 /* The following tests are reversed, so that the most desirable
917 * of several similar inodes comes first */
918 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
920 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
921 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
925 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
926 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
931 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
933 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
934 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
938 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
939 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
944 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
946 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
947 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
951 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
952 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
957 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
959 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
960 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
964 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
965 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
974 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
975 register struct InodeSummary *summary)
977 int volume = ip->u.vnode.volumeId;
978 int rwvolume = volume;
979 register n, nSpecial;
980 register Unique maxunique;
983 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
985 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
987 rwvolume = ip->u.special.parentId;
988 /* This isn't quite right, as there could (in error) be different
989 * parent inodes in different special vnodes */
991 if (maxunique < ip->u.vnode.vnodeUniquifier)
992 maxunique = ip->u.vnode.vnodeUniquifier;
996 summary->volumeId = volume;
997 summary->RWvolumeId = rwvolume;
998 summary->nInodes = n;
999 summary->nSpecialInodes = nSpecial;
1000 summary->maxUniquifier = maxunique;
1004 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, int singleVolumeNumber, void *rock)
1006 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1007 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1008 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1013 * Collect list of inodes in file named by path. If a truly fatal error,
1014 * unlink the file and abort. For lessor errors, return -1. The file will
1015 * be unlinked by the caller.
1018 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1020 struct afs_stat status;
1022 struct ViceInodeInfo *ip;
1023 struct InodeSummary summary;
1024 char summaryFileName[50];
1027 char *dev = fileSysPath;
1028 char *wpath = fileSysPath;
1030 char *dev = fileSysDeviceName;
1031 char *wpath = filesysfulldev;
1033 char *part = fileSysPath;
1036 /* This file used to come from vfsck; cobble it up ourselves now... */
1038 ListViceInodes(dev, fileSysPath, path,
1039 singleVolumeNumber ? OnlyOneVolume : 0,
1040 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1042 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, path, dev);
1046 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1048 if (forceSal && !ForceSalvage) {
1049 Log("***Forced salvage of all volumes on this partition***\n");
1052 inodeFd = afs_open(path, O_RDWR);
1053 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1055 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1057 tdir = (tmpdir ? tmpdir : part);
1059 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1060 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1062 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1063 "%s/salvage.temp.%d", tdir, getpid());
1065 summaryFile = afs_fopen(summaryFileName, "a+");
1066 if (summaryFile == NULL) {
1069 Abort("Unable to create inode summary file\n");
1071 if (!canfork || debug || Fork() == 0) {
1073 unsigned long st_size=(unsigned long) status.st_size;
1074 nInodes = st_size / sizeof(struct ViceInodeInfo);
1076 fclose(summaryFile);
1078 unlink(summaryFileName);
1079 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1080 RemoveTheForce(fileSysPath);
1082 struct VolumeSummary *vsp;
1085 GetVolumeSummary(singleVolumeNumber);
1087 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1089 DeleteExtraVolumeHeaderFile(vsp);
1092 Log("%s vice inodes on %s; not salvaged\n",
1093 singleVolumeNumber ? "No applicable" : "No", dev);
1096 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1098 fclose(summaryFile);
1101 unlink(summaryFileName);
1103 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1106 if (read(inodeFd, ip, st_size) != st_size) {
1107 fclose(summaryFile);
1110 unlink(summaryFileName);
1111 Abort("Unable to read inode table; %s not salvaged\n", dev);
1113 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1114 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1115 || write(inodeFd, ip, st_size) != st_size) {
1116 fclose(summaryFile);
1119 unlink(summaryFileName);
1120 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1124 CountVolumeInodes(ip, nInodes, &summary);
1125 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1126 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1127 fclose(summaryFile);
1131 summary.index += (summary.nInodes);
1132 nInodes -= summary.nInodes;
1133 ip += summary.nInodes;
1135 /* Following fflush is not fclose, because if it was debug mode would not work */
1136 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1137 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1138 fclose(summaryFile);
1142 if (canfork && !debug) {
1147 if (Wait("Inode summary") == -1) {
1148 fclose(summaryFile);
1151 unlink(summaryFileName);
1152 Exit(1); /* salvage of this partition aborted */
1155 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1156 if (status.st_size != 0) {
1158 unsigned long st_status=(unsigned long)status.st_size;
1159 inodeSummary = (struct InodeSummary *)malloc(st_status);
1160 assert(inodeSummary != NULL);
1161 /* For GNU we need to do lseek to get the file pointer moved. */
1162 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1163 ret = read(fileno(summaryFile), inodeSummary, st_status);
1164 assert(ret == st_status);
1166 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1167 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1168 fclose(summaryFile);
1170 unlink(summaryFileName);
1174 /* Comparison routine for volume sort.
1175 This is setup so that a read-write volume comes immediately before
1176 any read-only clones of that volume */
1178 CompareVolumes(const void *_p1, const void *_p2)
1180 register const struct VolumeSummary *p1 = _p1;
1181 register const struct VolumeSummary *p2 = _p2;
1182 if (p1->header.parent != p2->header.parent)
1183 return p1->header.parent < p2->header.parent ? -1 : 1;
1184 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1186 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1188 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1192 GetVolumeSummary(VolumeId singleVolumeNumber)
1195 afs_int32 nvols = 0;
1196 struct VolumeSummary *vsp, vs;
1197 struct VolumeDiskHeader diskHeader;
1200 /* Get headers from volume directory */
1201 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1202 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1203 if (!singleVolumeNumber) {
1204 while ((dp = readdir(dirp))) {
1205 char *p = dp->d_name;
1206 p = strrchr(dp->d_name, '.');
1207 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1209 if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
1210 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1211 == sizeof(diskHeader)
1212 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1213 DiskToVolumeHeader(&vs.header, &diskHeader);
1221 dirp = opendir("."); /* No rewinddir for NT */
1228 (struct VolumeSummary *)malloc(nvols *
1229 sizeof(struct VolumeSummary));
1232 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1233 assert(volumeSummaryp != NULL);
1236 vsp = volumeSummaryp;
1237 while ((dp = readdir(dirp))) {
1238 char *p = dp->d_name;
1239 p = strrchr(dp->d_name, '.');
1240 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1243 if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
1244 || read(fd, &diskHeader, sizeof(diskHeader))
1245 != sizeof(diskHeader)
1246 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1251 if (!singleVolumeNumber) {
1253 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1258 char nameShouldBe[64];
1259 DiskToVolumeHeader(&vsp->header, &diskHeader);
1260 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1261 && vsp->header.parent != singleVolumeNumber) {
1262 Log("%u is a read-only volume; not salvaged\n",
1263 singleVolumeNumber);
1266 if (!singleVolumeNumber
1267 || (vsp->header.id == singleVolumeNumber
1268 || vsp->header.parent == singleVolumeNumber)) {
1269 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1270 VFORMAT, vsp->header.id);
1271 if (singleVolumeNumber)
1272 AskOffline(vsp->header.id);
1273 if (strcmp(nameShouldBe, dp->d_name)) {
1275 Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", dp->d_name, (Testing ? "it would have been " : ""));
1279 vsp->fileName = ToString(dp->d_name);
1289 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1293 /* Find the link table. This should be associated with the RW volume or, if
1294 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1297 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1298 struct ViceInodeInfo *allInodes)
1301 struct ViceInodeInfo *ip;
1303 for (i = 0; i < nVols; i++) {
1304 ip = allInodes + isp[i].index;
1305 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1306 if (ip[j].u.special.type == VI_LINKTABLE)
1307 return ip[j].inodeNumber;
1314 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1316 struct versionStamp version;
1319 if (!VALID_INO(ino))
1321 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1322 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1323 if (!VALID_INO(ino))
1325 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1326 isp->RWvolumeId, errno);
1327 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1328 fdP = IH_OPEN(VGLinkH);
1330 Abort("Can't open link table for volume %u (error = %d)\n",
1331 isp->RWvolumeId, errno);
1333 if (FDH_TRUNC(fdP, 0) < 0)
1334 Abort("Can't truncate link table for volume %u (error = %d)\n",
1335 isp->RWvolumeId, errno);
1337 version.magic = LINKTABLEMAGIC;
1338 version.version = LINKTABLEVERSION;
1340 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1342 Abort("Can't truncate link table for volume %u (error = %d)\n",
1343 isp->RWvolumeId, errno);
1345 FDH_REALLYCLOSE(fdP);
1347 /* If the volume summary exits (i.e., the V*.vol header file exists),
1348 * then set this inode there as well.
1350 if (isp->volSummary)
1351 isp->volSummary->header.linkTable = ino;
1360 SVGParms_t *parms = (SVGParms_t *) arg;
1361 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1366 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1369 pthread_attr_t tattr;
1373 /* Initialize per volume global variables, even if later code does so */
1377 memset(&VolInfo, 0, sizeof(VolInfo));
1379 parms.svgp_inodeSummaryp = isp;
1380 parms.svgp_count = nVols;
1381 code = pthread_attr_init(&tattr);
1383 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1387 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1389 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1392 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1394 Log("Failed to create thread to salvage volume group %u\n",
1398 (void)pthread_join(tid, NULL);
1400 #endif /* AFS_NT40_ENV */
1403 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1405 struct ViceInodeInfo *inodes, *allInodes, *ip;
1406 int i, totalInodes, size, salvageTo;
1410 int dec_VGLinkH = 0;
1412 FdHandle_t *fdP = NULL;
1415 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1416 && isp->nSpecialInodes > 0);
1417 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1418 if (!ForceSalvage && QuickCheck(isp, nVols))
1421 if (ShowMounts && !haveRWvolume)
1423 if (canfork && !debug && Fork() != 0) {
1424 (void)Wait("Salvage volume group");
1427 for (i = 0, totalInodes = 0; i < nVols; i++)
1428 totalInodes += isp[i].nInodes;
1429 size = totalInodes * sizeof(struct ViceInodeInfo);
1430 inodes = (struct ViceInodeInfo *)malloc(size);
1431 allInodes = inodes - isp->index; /* this would the base of all the inodes
1432 * for the partition, if all the inodes
1433 * had been read into memory */
1435 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1437 assert(read(inodeFd, inodes, size) == size);
1439 /* Don't try to salvage a read write volume if there isn't one on this
1441 salvageTo = haveRWvolume ? 0 : 1;
1443 #ifdef AFS_NAMEI_ENV
1444 ino = FindLinkHandle(isp, nVols, allInodes);
1445 if (VALID_INO(ino)) {
1446 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1447 fdP = IH_OPEN(VGLinkH);
1449 if (!VALID_INO(ino) || fdP == NULL) {
1450 Log("%s link table for volume %u.\n",
1451 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1453 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1455 CreateLinkTable(isp, ino);
1459 FDH_REALLYCLOSE(fdP);
1461 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1464 /* Salvage in reverse order--read/write volume last; this way any
1465 * Inodes not referenced by the time we salvage the read/write volume
1466 * can be picked up by the read/write volume */
1467 /* ACTUALLY, that's not done right now--the inodes just vanish */
1468 for (i = nVols - 1; i >= salvageTo; i--) {
1470 struct InodeSummary *lisp = &isp[i];
1471 #ifdef AFS_NAMEI_ENV
1472 /* If only the RO is present on this partition, the link table
1473 * shows up as a RW volume special file. Need to make sure the
1474 * salvager doesn't try to salvage the non-existent RW.
1476 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1477 /* If this only special inode is the link table, continue */
1478 if (inodes->u.special.type == VI_LINKTABLE) {
1485 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1486 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1487 /* Check inodes twice. The second time do things seriously. This
1488 * way the whole RO volume can be deleted, below, if anything goes wrong */
1489 for (check = 1; check >= 0; check--) {
1491 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1493 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1494 if (rw && deleteMe) {
1495 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1496 * volume won't be called */
1502 if (rw && check == 1)
1504 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1505 MaybeZapVolume(lisp, "Vnode index", 0, check);
1511 /* Fix actual inode counts */
1513 Log("totalInodes %d\n",totalInodes);
1514 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1515 static int TraceBadLinkCounts = 0;
1516 #ifdef AFS_NAMEI_ENV
1517 if (VGLinkH->ih_ino == ip->inodeNumber) {
1518 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1519 VGLinkH_p1 = ip->u.param[0];
1520 continue; /* Deal with this last. */
1523 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1524 TraceBadLinkCounts--; /* Limit reports, per volume */
1525 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]);
1527 while (ip->linkCount > 0) {
1528 /* below used to assert, not break */
1530 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1531 Log("idec failed. inode %s errno %d\n",
1532 PrintInode(NULL, ip->inodeNumber), errno);
1538 while (ip->linkCount < 0) {
1539 /* these used to be asserts */
1541 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1542 Log("iinc failed. inode %s errno %d\n",
1543 PrintInode(NULL, ip->inodeNumber), errno);
1550 #ifdef AFS_NAMEI_ENV
1551 while (dec_VGLinkH > 0) {
1552 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1553 Log("idec failed on link table, errno = %d\n", errno);
1557 while (dec_VGLinkH < 0) {
1558 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1559 Log("iinc failed on link table, errno = %d\n", errno);
1566 /* Directory consistency checks on the rw volume */
1568 SalvageVolume(isp, VGLinkH);
1569 IH_RELEASE(VGLinkH);
1571 if (canfork && !debug) {
1578 QuickCheck(register struct InodeSummary *isp, int nVols)
1580 /* Check headers BEFORE forking */
1584 for (i = 0; i < nVols; i++) {
1585 struct VolumeSummary *vs = isp[i].volSummary;
1586 VolumeDiskData volHeader;
1588 /* Don't salvage just because phantom rw volume is there... */
1589 /* (If a read-only volume exists, read/write inodes must also exist) */
1590 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1594 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1595 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1596 == sizeof(volHeader)
1597 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1598 && volHeader.dontSalvage == DONT_SALVAGE
1599 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1600 if (volHeader.inUse == 1) {
1601 volHeader.inUse = 0;
1602 volHeader.inService = 1;
1604 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1605 != sizeof(volHeader)) {
1621 /* SalvageVolumeHeaderFile
1623 * Salvage the top level V*.vol header file. Make sure the special files
1624 * exist and that there are no duplicates.
1626 * Calls SalvageHeader for each possible type of volume special file.
1630 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1631 register struct ViceInodeInfo *inodes, int RW,
1632 int check, int *deleteMe)
1636 register struct ViceInodeInfo *ip;
1637 int allinodesobsolete = 1;
1638 struct VolumeDiskHeader diskHeader;
1642 memset(&tempHeader, 0, sizeof(tempHeader));
1643 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1644 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1645 tempHeader.id = isp->volumeId;
1646 tempHeader.parent = isp->RWvolumeId;
1647 /* Check for duplicates (inodes are sorted by type field) */
1648 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1649 ip = &inodes[isp->index + i];
1650 if (ip->u.special.type == (ip + 1)->u.special.type) {
1652 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1656 for (i = 0; i < isp->nSpecialInodes; i++) {
1657 ip = &inodes[isp->index + i];
1658 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1660 Log("Rubbish header inode\n");
1663 Log("Rubbish header inode; deleted\n");
1664 } else if (!stuff[ip->u.special.type - 1].obsolete) {
1665 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1666 if (!check && ip->u.special.type != VI_LINKTABLE)
1667 ip->linkCount--; /* Keep the inode around */
1668 allinodesobsolete = 0;
1672 if (allinodesobsolete) {
1679 VGLinkH_cnt++; /* one for every header. */
1681 if (!RW && !check && isp->volSummary) {
1682 ClearROInUseBit(isp->volSummary);
1686 for (i = 0; i < MAXINODETYPE; i++) {
1687 if (stuff[i].inodeType == VI_LINKTABLE) {
1688 /* Gross hack: SalvageHeader does a bcmp on the volume header.
1689 * And we may have recreated the link table earlier, so set the
1690 * RW header as well.
1692 if (VALID_INO(VGLinkH->ih_ino)) {
1693 *stuff[i].inode = VGLinkH->ih_ino;
1697 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1701 if (isp->volSummary == NULL) {
1703 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1705 Log("No header file for volume %u\n", isp->volumeId);
1709 Log("No header file for volume %u; %screating %s/%s\n",
1710 isp->volumeId, (Testing ? "it would have been " : ""),
1711 fileSysPathName, name);
1712 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
1713 assert(headerFd != -1);
1714 isp->volSummary = (struct VolumeSummary *)
1715 malloc(sizeof(struct VolumeSummary));
1716 isp->volSummary->fileName = ToString(name);
1719 /* hack: these two fields are obsolete... */
1720 isp->volSummary->header.volumeAcl = 0;
1721 isp->volSummary->header.volumeMountTable = 0;
1724 (&isp->volSummary->header, &tempHeader,
1725 sizeof(struct VolumeHeader))) {
1726 /* We often remove the name before calling us, so we make a fake one up */
1727 if (isp->volSummary->fileName) {
1728 strcpy(name, isp->volSummary->fileName);
1730 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1731 isp->volSummary->fileName = ToString(name);
1734 Log("Header file %s is damaged or no longer valid%s\n", name,
1735 (check ? "" : "; repairing"));
1739 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
1740 assert(headerFd != -1);
1744 memcpy(&isp->volSummary->header, &tempHeader,
1745 sizeof(struct VolumeHeader));
1748 Log("It would have written a new header file for volume %u\n",
1751 VolumeHeaderToDisk(&diskHeader, &tempHeader);
1752 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1753 != sizeof(struct VolumeDiskHeader)) {
1754 Log("Couldn't rewrite volume header file!\n");
1761 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1762 isp->volSummary->header.volumeInfo);
1767 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1771 VolumeDiskData volumeInfo;
1772 struct versionStamp fileHeader;
1781 #ifndef AFS_NAMEI_ENV
1782 if (sp->inodeType == VI_LINKTABLE)
1785 if (*(sp->inode) == 0) {
1787 Log("Missing inode in volume header (%s)\n", sp->description);
1791 Log("Missing inode in volume header (%s); %s\n", sp->description,
1792 (Testing ? "it would have recreated it" : "recreating"));
1795 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1796 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1797 if (!VALID_INO(*(sp->inode)))
1799 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1800 sp->description, errno);
1805 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1806 fdP = IH_OPEN(specH);
1807 if (OKToZap && (fdP == NULL) && BadError(errno)) {
1808 /* bail out early and destroy the volume */
1810 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1817 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1818 sp->description, errno);
1821 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1822 || header.fileHeader.magic != sp->stamp.magic)) {
1824 Log("Part of the header (%s) is corrupted\n", sp->description);
1825 FDH_REALLYCLOSE(fdP);
1829 Log("Part of the header (%s) is corrupted; recreating\n",
1833 if (sp->inodeType == VI_VOLINFO
1834 && header.volumeInfo.destroyMe == DESTROY_ME) {
1837 FDH_REALLYCLOSE(fdP);
1841 if (recreate && !Testing) {
1844 ("Internal error: recreating volume header (%s) in check mode\n",
1846 code = FDH_TRUNC(fdP, 0);
1848 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1849 sp->description, errno);
1851 /* The following code should be moved into vutil.c */
1852 if (sp->inodeType == VI_VOLINFO) {
1854 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1855 header.volumeInfo.stamp = sp->stamp;
1856 header.volumeInfo.id = isp->volumeId;
1857 header.volumeInfo.parentId = isp->RWvolumeId;
1858 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1859 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1860 isp->volumeId, isp->volumeId);
1861 header.volumeInfo.inService = 0;
1862 header.volumeInfo.blessed = 0;
1863 /* The + 1000 is a hack in case there are any files out in venus caches */
1864 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1865 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
1866 header.volumeInfo.needsCallback = 0;
1867 gettimeofday(&tp, 0);
1868 header.volumeInfo.creationDate = tp.tv_sec;
1869 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1871 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1872 sp->description, errno);
1875 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1876 sizeof(header.volumeInfo));
1877 if (code != sizeof(header.volumeInfo)) {
1880 ("Unable to write volume header file (%s) (errno = %d)\n",
1881 sp->description, errno);
1882 Abort("Unable to write entire volume header file (%s)\n",
1886 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1888 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1889 sp->description, errno);
1891 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1892 if (code != sizeof(sp->stamp)) {
1895 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1896 sp->description, errno);
1898 ("Unable to write entire version stamp in volume header file (%s)\n",
1903 FDH_REALLYCLOSE(fdP);
1905 if (sp->inodeType == VI_VOLINFO) {
1906 VolInfo = header.volumeInfo;
1909 if (VolInfo.updateDate) {
1910 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1912 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1913 (Testing ? "it would have been " : ""), update);
1915 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1917 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1918 VolInfo.id, update);
1928 SalvageVnodes(register struct InodeSummary *rwIsp,
1929 register struct InodeSummary *thisIsp,
1930 register struct ViceInodeInfo *inodes, int check)
1932 int ilarge, ismall, ioffset, RW, nInodes;
1933 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
1936 RW = (rwIsp == thisIsp);
1937 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1939 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1940 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1941 if (check && ismall == -1)
1944 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1945 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1946 return (ilarge == 0 && ismall == 0 ? 0 : -1);
1950 SalvageIndex(Inode ino, VnodeClass class, int RW,
1951 register struct ViceInodeInfo *ip, int nInodes,
1952 struct VolumeSummary *volSummary, int check)
1954 VolumeId volumeNumber;
1955 char buf[SIZEOF_LARGEDISKVNODE];
1956 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1958 StreamHandle_t *file;
1959 struct VnodeClassInfo *vcp;
1961 afs_fsize_t vnodeLength;
1962 int vnodeIndex, nVnodes;
1963 afs_ino_str_t stmp1, stmp2;
1967 volumeNumber = volSummary->header.id;
1968 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
1969 fdP = IH_OPEN(handle);
1970 assert(fdP != NULL);
1971 file = FDH_FDOPEN(fdP, "r+");
1972 assert(file != NULL);
1973 vcp = &VnodeClassInfo[class];
1974 size = OS_SIZE(fdP->fd_fd);
1976 nVnodes = (size / vcp->diskSize) - 1;
1978 assert((nVnodes + 1) * vcp->diskSize == size);
1979 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
1983 for (vnodeIndex = 0;
1984 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
1985 nVnodes--, vnodeIndex++) {
1986 if (vnode->type != vNull) {
1987 int vnodeChanged = 0;
1988 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
1989 /* Log programs that belong to root (potentially suid root);
1990 * don't bother for read-only or backup volumes */
1991 #ifdef notdef /* This is done elsewhere */
1992 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
1993 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);
1995 if (VNDISK_GET_INO(vnode) == 0) {
1997 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
1998 memset(vnode, 0, vcp->diskSize);
2002 if (vcp->magic != vnode->vnodeMagic) {
2003 /* bad magic #, probably partially created vnode */
2004 Log("Partially allocated vnode %d deleted.\n",
2006 memset(vnode, 0, vcp->diskSize);
2010 /* ****** Should do a bit more salvage here: e.g. make sure
2011 * vnode type matches what it should be given the index */
2012 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2013 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2014 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2015 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2022 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2023 /* The following doesn't work, because the version number
2024 * is not maintained correctly by the file server */
2025 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2026 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2028 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2034 /* For RW volume, look for vnode with matching inode number;
2035 * if no such match, take the first determined by our sort
2037 register struct ViceInodeInfo *lip = ip;
2038 register int lnInodes = nInodes;
2040 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2041 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2050 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2051 /* "Matching" inode */
2055 vu = vnode->uniquifier;
2056 iu = ip->u.vnode.vnodeUniquifier;
2057 vd = vnode->dataVersion;
2058 id = ip->u.vnode.inodeDataVersion;
2060 * Because of the possibility of the uniquifier overflows (> 4M)
2061 * we compare them modulo the low 22-bits; we shouldn't worry
2062 * about mismatching since they shouldn't to many old
2063 * uniquifiers of the same vnode...
2065 if (IUnique(vu) != IUnique(iu)) {
2067 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2070 vnode->uniquifier = iu;
2071 #ifdef AFS_3DISPARES
2072 vnode->dataVersion = (id >= vd ?
2075 1887437 ? vd : id) :
2078 1887437 ? id : vd));
2080 #if defined(AFS_SGI_EXMAG)
2081 vnode->dataVersion = (id >= vd ?
2084 15099494 ? vd : id) :
2087 15099494 ? id : vd));
2089 vnode->dataVersion = (id > vd ? id : vd);
2090 #endif /* AFS_SGI_EXMAG */
2091 #endif /* AFS_3DISPARES */
2094 /* don't bother checking for vd > id any more, since
2095 * partial file transfers always result in this state,
2096 * and you can't do much else anyway (you've already
2097 * found the best data you can) */
2098 #ifdef AFS_3DISPARES
2099 if (!vnodeIsDirectory(vnodeNumber)
2100 && ((vd < id && (id - vd) < 1887437)
2101 || ((vd > id && (vd - id) > 1887437)))) {
2103 #if defined(AFS_SGI_EXMAG)
2104 if (!vnodeIsDirectory(vnodeNumber)
2105 && ((vd < id && (id - vd) < 15099494)
2106 || ((vd > id && (vd - id) > 15099494)))) {
2108 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2109 #endif /* AFS_SGI_EXMAG */
2112 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2113 vnode->dataVersion = id;
2118 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2121 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);
2123 VNDISK_SET_INO(vnode, ip->inodeNumber);
2128 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);
2130 VNDISK_SET_INO(vnode, ip->inodeNumber);
2133 VNDISK_GET_LEN(vnodeLength, vnode);
2134 if (ip->byteCount != vnodeLength) {
2137 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2142 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2143 VNDISK_SET_LEN(vnode, ip->byteCount);
2147 ip->linkCount--; /* Keep the inode around */
2150 } else { /* no matching inode */
2151 if (VNDISK_GET_INO(vnode) != 0
2152 || vnode->type == vDirectory) {
2153 /* No matching inode--get rid of the vnode */
2155 if (VNDISK_GET_INO(vnode)) {
2157 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2161 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2166 if (VNDISK_GET_INO(vnode)) {
2168 time_t serverModifyTime = vnode->serverModifyTime;
2169 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));
2173 time_t serverModifyTime = vnode->serverModifyTime;
2174 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2177 memset(vnode, 0, vcp->diskSize);
2180 /* Should not reach here becuase we checked for
2181 * (inodeNumber == 0) above. And where we zero the vnode,
2182 * we also goto vnodeDone.
2186 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2190 } /* VNDISK_GET_INO(vnode) != 0 */
2192 assert(!(vnodeChanged && check));
2193 if (vnodeChanged && !Testing) {
2195 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2196 (char *)vnode, vcp->diskSize)
2198 VolumeChanged = 1; /* For break call back */
2209 struct VnodeEssence *
2210 CheckVnodeNumber(VnodeId vnodeNumber)
2213 struct VnodeInfo *vip;
2216 class = vnodeIdToClass(vnodeNumber);
2217 vip = &vnodeInfo[class];
2218 offset = vnodeIdToBitNumber(vnodeNumber);
2219 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2223 CopyOnWrite(register struct DirSummary *dir)
2225 /* Copy the directory unconditionally if we are going to change it:
2226 * not just if was cloned.
2228 struct VnodeDiskObject vnode;
2229 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2230 Inode oldinode, newinode;
2233 if (dir->copied || Testing)
2235 DFlush(); /* Well justified paranoia... */
2238 IH_IREAD(vnodeInfo[vLarge].handle,
2239 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2241 assert(code == sizeof(vnode));
2242 oldinode = VNDISK_GET_INO(&vnode);
2243 /* Increment the version number by a whole lot to avoid problems with
2244 * clients that were promised new version numbers--but the file server
2245 * crashed before the versions were written to disk.
2248 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2249 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2251 assert(VALID_INO(newinode));
2252 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2254 VNDISK_SET_INO(&vnode, newinode);
2256 IH_IWRITE(vnodeInfo[vLarge].handle,
2257 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2259 assert(code == sizeof(vnode));
2261 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2262 fileSysDevice, newinode);
2263 /* Don't delete the original inode right away, because the directory is
2264 * still being scanned.
2270 * This function should either successfully create a new dir, or give up
2271 * and leave things the way they were. In particular, if it fails to write
2272 * the new dir properly, it should return w/o changing the reference to the
2276 CopyAndSalvage(register struct DirSummary *dir)
2278 struct VnodeDiskObject vnode;
2279 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2280 Inode oldinode, newinode;
2282 register afs_int32 code;
2283 afs_int32 parentUnique = 1;
2284 struct VnodeEssence *vnodeEssence;
2288 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2290 IH_IREAD(vnodeInfo[vLarge].handle,
2291 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2293 assert(code == sizeof(vnode));
2294 oldinode = VNDISK_GET_INO(&vnode);
2295 /* Increment the version number by a whole lot to avoid problems with
2296 * clients that were promised new version numbers--but the file server
2297 * crashed before the versions were written to disk.
2300 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2301 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2303 assert(VALID_INO(newinode));
2304 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2306 /* Assign . and .. vnode numbers from dir and vnode.parent.
2307 * The uniquifier for . is in the vnode.
2308 * The uniquifier for .. might be set to a bogus value of 1 and
2309 * the salvager will later clean it up.
2311 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2312 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2315 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2317 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2322 /* didn't really build the new directory properly, let's just give up. */
2323 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2325 Log("Directory salvage returned code %d, continuing.\n", code);
2328 Log("Checking the results of the directory salvage...\n");
2329 if (!DirOK(&newdir)) {
2330 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2331 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2336 VNDISK_SET_INO(&vnode, newinode);
2337 VNDISK_SET_LEN(&vnode, Length(&newdir));
2339 IH_IWRITE(vnodeInfo[vLarge].handle,
2340 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2342 assert(code == sizeof(vnode));
2344 nt_sync(fileSysDevice);
2346 sync(); /* this is slow, but hopefully rarely called. We don't have
2347 * an open FD on the file itself to fsync.
2350 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2352 dir->dirHandle = newdir;
2356 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2359 struct VnodeEssence *vnodeEssence;
2360 afs_int32 dirOrphaned, todelete;
2362 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2364 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2365 if (vnodeEssence == NULL) {
2367 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2371 assert(Delete(&dir->dirHandle, name) == 0);
2376 #ifndef AFS_NAMEI_ENV
2377 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2378 * mount inode for the partition. If this inode were deleted, it would crash
2381 if (vnodeEssence->InodeNumber == 0) {
2382 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"));
2385 assert(Delete(&dir->dirHandle, name) == 0);
2392 if (!(vnodeNumber & 1) && !Showmode
2393 && !(vnodeEssence->count || vnodeEssence->unique
2394 || vnodeEssence->modeBits)) {
2395 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2396 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2397 vnodeNumber, unique,
2398 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2403 assert(Delete(&dir->dirHandle, name) == 0);
2409 /* Check if the Uniquifiers match. If not, change the directory entry
2410 * so its unique matches the vnode unique. Delete if the unique is zero
2411 * or if the directory is orphaned.
2413 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2414 if (!vnodeEssence->unique
2415 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2416 /* This is an orphaned directory. Don't delete the . or ..
2417 * entry. Otherwise, it will get created in the next
2418 * salvage and deleted again here. So Just skip it.
2423 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2426 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")));
2430 fid.Vnode = vnodeNumber;
2431 fid.Unique = vnodeEssence->unique;
2433 assert(Delete(&dir->dirHandle, name) == 0);
2435 assert(Create(&dir->dirHandle, name, &fid) == 0);
2438 return; /* no need to continue */
2441 if (strcmp(name, ".") == 0) {
2442 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2445 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2448 assert(Delete(&dir->dirHandle, ".") == 0);
2449 fid.Vnode = dir->vnodeNumber;
2450 fid.Unique = dir->unique;
2451 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2454 vnodeNumber = fid.Vnode; /* Get the new Essence */
2455 unique = fid.Unique;
2456 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2459 } else if (strcmp(name, "..") == 0) {
2462 struct VnodeEssence *dotdot;
2463 pa.Vnode = dir->parent;
2464 dotdot = CheckVnodeNumber(pa.Vnode);
2465 assert(dotdot != NULL); /* XXX Should not be assert */
2466 pa.Unique = dotdot->unique;
2468 pa.Vnode = dir->vnodeNumber;
2469 pa.Unique = dir->unique;
2471 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2473 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2476 assert(Delete(&dir->dirHandle, "..") == 0);
2477 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2480 vnodeNumber = pa.Vnode; /* Get the new Essence */
2482 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2484 dir->haveDotDot = 1;
2485 } else if (strncmp(name, ".__afs", 6) == 0) {
2487 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);
2491 assert(Delete(&dir->dirHandle, name) == 0);
2493 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2494 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2497 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2498 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);
2499 if (ShowMounts && (vnodeEssence->type == vSymlink)
2500 && !(vnodeEssence->modeBits & 0111)) {
2506 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2507 vnodeEssence->InodeNumber);
2509 assert(fdP != NULL);
2510 size = FDH_SIZE(fdP);
2512 memset(buf, 0, 1024);
2515 code = FDH_READ(fdP, buf, size);
2516 assert(code == size);
2517 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2518 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2519 dir->name ? dir->name : "??", name, buf);
2520 FDH_REALLYCLOSE(fdP);
2523 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2524 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);
2525 if (vnodeIdToClass(vnodeNumber) == vLarge
2526 && vnodeEssence->name == NULL) {
2528 if ((n = (char *)malloc(strlen(name) + 1)))
2530 vnodeEssence->name = n;
2533 /* The directory entry points to the vnode. Check to see if the
2534 * vnode points back to the directory. If not, then let the
2535 * directory claim it (else it might end up orphaned). Vnodes
2536 * already claimed by another directory are deleted from this
2537 * directory: hardlinks to the same vnode are not allowed
2538 * from different directories.
2540 if (vnodeEssence->parent != dir->vnodeNumber) {
2541 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2542 /* Vnode does not point back to this directory.
2543 * Orphaned dirs cannot claim a file (it may belong to
2544 * another non-orphaned dir).
2547 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);
2549 vnodeEssence->parent = dir->vnodeNumber;
2550 vnodeEssence->changed = 1;
2552 /* Vnode was claimed by another directory */
2555 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 " : ""));
2556 } else if (vnodeNumber == 1) {
2557 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 " : ""));
2559 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 " : ""));
2564 assert(Delete(&dir->dirHandle, name) == 0);
2569 /* This directory claims the vnode */
2570 vnodeEssence->claimed = 1;
2572 vnodeEssence->count--;
2576 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2578 register struct VnodeInfo *vip = &vnodeInfo[class];
2579 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2580 char buf[SIZEOF_LARGEDISKVNODE];
2581 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2583 StreamHandle_t *file;
2588 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2589 fdP = IH_OPEN(vip->handle);
2590 assert(fdP != NULL);
2591 file = FDH_FDOPEN(fdP, "r+");
2592 assert(file != NULL);
2593 size = OS_SIZE(fdP->fd_fd);
2595 vip->nVnodes = (size / vcp->diskSize) - 1;
2596 if (vip->nVnodes > 0) {
2597 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2598 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2599 assert((vip->vnodes = (struct VnodeEssence *)
2600 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2601 if (class == vLarge) {
2602 assert((vip->inodes = (Inode *)
2603 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2612 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2613 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2614 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2615 nVnodes--, vnodeIndex++) {
2616 if (vnode->type != vNull) {
2617 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2618 afs_fsize_t vnodeLength;
2619 vip->nAllocatedVnodes++;
2620 vep->count = vnode->linkCount;
2621 VNDISK_GET_LEN(vnodeLength, vnode);
2622 vep->blockCount = nBlocks(vnodeLength);
2623 vip->volumeBlockCount += vep->blockCount;
2624 vep->parent = vnode->parent;
2625 vep->unique = vnode->uniquifier;
2626 if (*maxu < vnode->uniquifier)
2627 *maxu = vnode->uniquifier;
2628 vep->modeBits = vnode->modeBits;
2629 vep->InodeNumber = VNDISK_GET_INO(vnode);
2630 vep->type = vnode->type;
2631 vep->author = vnode->author;
2632 vep->owner = vnode->owner;
2633 vep->group = vnode->group;
2634 if (vnode->type == vDirectory) {
2635 assert(class == vLarge);
2636 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2645 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2647 struct VnodeEssence *parentvp;
2653 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2654 && GetDirName(vp->parent, parentvp, path)) {
2656 strcat(path, vp->name);
2662 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2663 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2666 IsVnodeOrphaned(VnodeId vnode)
2668 struct VnodeEssence *vep;
2671 return (1); /* Vnode zero does not exist */
2673 return (0); /* The root dir vnode is always claimed */
2674 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2675 if (!vep || !vep->claimed)
2676 return (1); /* Vnode is not claimed - it is orphaned */
2678 return (IsVnodeOrphaned(vep->parent));
2682 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2683 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2686 static struct DirSummary dir;
2687 static struct DirHandle dirHandle;
2688 struct VnodeEssence *parent;
2689 static char path[MAXPATHLEN];
2692 if (dirVnodeInfo->vnodes[i].salvaged)
2693 return; /* already salvaged */
2696 dirVnodeInfo->vnodes[i].salvaged = 1;
2698 if (dirVnodeInfo->inodes[i] == 0)
2699 return; /* Not allocated to a directory */
2701 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2702 if (dirVnodeInfo->vnodes[i].parent) {
2703 Log("Bad parent, vnode 1; %s...\n",
2704 (Testing ? "skipping" : "salvaging"));
2705 dirVnodeInfo->vnodes[i].parent = 0;
2706 dirVnodeInfo->vnodes[i].changed = 1;
2709 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2710 if (parent && parent->salvaged == 0)
2711 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2712 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2713 rootdir, rootdirfound);
2716 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2717 dir.unique = dirVnodeInfo->vnodes[i].unique;
2720 dir.parent = dirVnodeInfo->vnodes[i].parent;
2721 dir.haveDot = dir.haveDotDot = 0;
2722 dir.ds_linkH = alinkH;
2723 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2724 dirVnodeInfo->inodes[i]);
2726 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2729 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2730 (Testing ? "skipping" : "salvaging"));
2733 CopyAndSalvage(&dir);
2737 dirHandle = dir.dirHandle;
2740 GetDirName(bitNumberToVnodeNumber(i, vLarge),
2741 &dirVnodeInfo->vnodes[i], path);
2744 /* If enumeration failed for random reasons, we will probably delete
2745 * too much stuff, so we guard against this instead.
2747 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2750 /* Delete the old directory if it was copied in order to salvage.
2751 * CopyOnWrite has written the new inode # to the disk, but we still
2752 * have the old one in our local structure here. Thus, we idec the
2756 if (dir.copied && !Testing) {
2757 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2759 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2762 /* Remember rootdir DirSummary _after_ it has been judged */
2763 if (dir.vnodeNumber == 1 && dir.unique == 1) {
2764 memcpy(rootdir, &dir, sizeof(struct DirSummary));
2772 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2774 /* This routine, for now, will only be called for read-write volumes */
2776 int BlocksInVolume = 0, FilesInVolume = 0;
2777 register VnodeClass class;
2778 struct DirSummary rootdir, oldrootdir;
2779 struct VnodeInfo *dirVnodeInfo;
2780 struct VnodeDiskObject vnode;
2781 VolumeDiskData volHeader;
2783 int orphaned, rootdirfound = 0;
2784 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
2785 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
2786 struct VnodeEssence *vep;
2791 VnodeId LFVnode, ThisVnode;
2792 Unique LFUnique, ThisUnique;
2795 vid = rwIsp->volSummary->header.id;
2796 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2797 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2798 assert(nBytes == sizeof(volHeader));
2799 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2800 assert(volHeader.destroyMe != DESTROY_ME);
2801 /* (should not have gotten this far with DESTROY_ME flag still set!) */
2803 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2805 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2808 dirVnodeInfo = &vnodeInfo[vLarge];
2809 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2810 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2818 /* Parse each vnode looking for orphaned vnodes and
2819 * connect them to the tree as orphaned (if requested).
2821 oldrootdir = rootdir;
2822 for (class = 0; class < nVNODECLASSES; class++) {
2823 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2824 vep = &(vnodeInfo[class].vnodes[v]);
2825 ThisVnode = bitNumberToVnodeNumber(v, class);
2826 ThisUnique = vep->unique;
2828 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2829 continue; /* Ignore unused, claimed, and root vnodes */
2831 /* This vnode is orphaned. If it is a directory vnode, then the '..'
2832 * entry in this vnode had incremented the parent link count (In
2833 * JudgeEntry()). We need to go to the parent and decrement that
2834 * link count. But if the parent's unique is zero, then the parent
2835 * link count was not incremented in JudgeEntry().
2837 if (class == vLarge) { /* directory vnode */
2838 pv = vnodeIdToBitNumber(vep->parent);
2839 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2840 vnodeInfo[vLarge].vnodes[pv].count++;
2844 continue; /* If no rootdir, can't attach orphaned files */
2846 /* Here we attach orphaned files and directories into the
2847 * root directory, LVVnode, making sure link counts stay correct.
2849 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2850 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
2851 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
2853 /* Update this orphaned vnode's info. Its parent info and
2854 * link count (do for orphaned directories and files).
2856 vep->parent = LFVnode; /* Parent is the root dir */
2857 vep->unique = LFUnique;
2860 vep->count--; /* Inc link count (root dir will pt to it) */
2862 /* If this orphaned vnode is a directory, change '..'.
2863 * The name of the orphaned dir/file is unknown, so we
2864 * build a unique name. No need to CopyOnWrite the directory
2865 * since it is not connected to tree in BK or RO volume and
2866 * won't be visible there.
2868 if (class == vLarge) {
2872 /* Remove and recreate the ".." entry in this orphaned directory */
2873 SetSalvageDirHandle(&dh, vid, fileSysDevice,
2874 vnodeInfo[class].inodes[v]);
2876 pa.Unique = LFUnique;
2877 assert(Delete(&dh, "..") == 0);
2878 assert(Create(&dh, "..", &pa) == 0);
2880 /* The original parent's link count was decremented above.
2881 * Here we increment the new parent's link count.
2883 pv = vnodeIdToBitNumber(LFVnode);
2884 vnodeInfo[vLarge].vnodes[pv].count--;
2888 /* Go to the root dir and add this entry. The link count of the
2889 * root dir was incremented when ".." was created. Try 10 times.
2891 for (j = 0; j < 10; j++) {
2892 pa.Vnode = ThisVnode;
2893 pa.Unique = ThisUnique;
2895 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2897 vLarge) ? "__ORPHANDIR__" :
2898 "__ORPHANFILE__"), ThisVnode,
2901 CopyOnWrite(&rootdir);
2902 code = Create(&rootdir.dirHandle, npath, &pa);
2906 ThisUnique += 50; /* Try creating a different file */
2909 Log("Attaching orphaned %s to volume's root dir as %s\n",
2910 ((class == vLarge) ? "directory" : "file"), npath);
2912 } /* for each vnode in the class */
2913 } /* for each class of vnode */
2915 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2917 if (!oldrootdir.copied && rootdir.copied) {
2919 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2922 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
2925 DFlush(); /* Flush the changes */
2926 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
2927 Log("Cannot attach orphaned files and directories: Root directory not found\n");
2928 orphans = ORPH_IGNORE;
2931 /* Write out all changed vnodes. Orphaned files and directories
2932 * will get removed here also (if requested).
2934 for (class = 0; class < nVNODECLASSES; class++) {
2935 int nVnodes = vnodeInfo[class].nVnodes;
2936 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2937 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
2938 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
2939 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
2940 for (i = 0; i < nVnodes; i++) {
2941 register struct VnodeEssence *vnp = &vnodes[i];
2942 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
2944 /* If the vnode is good but is unclaimed (not listed in
2945 * any directory entries), then it is orphaned.
2948 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
2949 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
2953 if (vnp->changed || vnp->count) {
2957 IH_IREAD(vnodeInfo[class].handle,
2958 vnodeIndexOffset(vcp, vnodeNumber),
2959 (char *)&vnode, sizeof(vnode));
2960 assert(nBytes == sizeof(vnode));
2962 vnode.parent = vnp->parent;
2963 oldCount = vnode.linkCount;
2964 vnode.linkCount = vnode.linkCount - vnp->count;
2967 orphaned = IsVnodeOrphaned(vnodeNumber);
2969 if (!vnp->todelete) {
2970 /* Orphans should have already been attached (if requested) */
2971 assert(orphans != ORPH_ATTACH);
2972 oblocks += vnp->blockCount;
2975 if (((orphans == ORPH_REMOVE) || vnp->todelete)
2977 BlocksInVolume -= vnp->blockCount;
2979 if (VNDISK_GET_INO(&vnode)) {
2981 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
2984 memset(&vnode, 0, sizeof(vnode));
2986 } else if (vnp->count) {
2988 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
2992 vnode.dataVersion++;
2995 IH_IWRITE(vnodeInfo[class].handle,
2996 vnodeIndexOffset(vcp, vnodeNumber),
2997 (char *)&vnode, sizeof(vnode));
2998 assert(nBytes == sizeof(vnode));
3004 if (!Showmode && ofiles) {
3005 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3007 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3011 for (class = 0; class < nVNODECLASSES; class++) {
3012 register struct VnodeInfo *vip = &vnodeInfo[class];
3013 for (i = 0; i < vip->nVnodes; i++)
3014 if (vip->vnodes[i].name)
3015 free(vip->vnodes[i].name);
3022 /* Set correct resource utilization statistics */
3023 volHeader.filecount = FilesInVolume;
3024 volHeader.diskused = BlocksInVolume;
3026 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3027 if (volHeader.uniquifier < (maxunique + 1)) {
3029 Log("Volume uniquifier is too low; fixed\n");
3030 /* Plus 2,000 in case there are workstations out there with
3031 * cached vnodes that have since been deleted
3033 volHeader.uniquifier = (maxunique + 1 + 2000);
3036 /* Turn off the inUse bit; the volume's been salvaged! */
3037 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3038 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3039 volHeader.inService = 1; /* allow service again */
3040 volHeader.needsCallback = (VolumeChanged != 0);
3041 volHeader.dontSalvage = DONT_SALVAGE;
3044 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3045 assert(nBytes == sizeof(volHeader));
3048 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3049 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3050 FilesInVolume, BlocksInVolume);
3052 IH_RELEASE(vnodeInfo[vSmall].handle);
3053 IH_RELEASE(vnodeInfo[vLarge].handle);
3059 ClearROInUseBit(struct VolumeSummary *summary)
3061 IHandle_t *h = summary->volumeInfoHandle;
3064 VolumeDiskData volHeader;
3066 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3067 assert(nBytes == sizeof(volHeader));
3068 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3069 volHeader.inUse = 0;
3070 volHeader.needsSalvaged = 0;
3071 volHeader.inService = 1;
3072 volHeader.dontSalvage = DONT_SALVAGE;
3074 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3075 assert(nBytes == sizeof(volHeader));
3080 * Possible delete the volume.
3082 * deleteMe - Always do so, only a partial volume.
3085 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3088 if (readOnly(isp) || deleteMe) {
3089 if (isp->volSummary && isp->volSummary->fileName) {
3092 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);
3094 Log("It will be deleted on this server (you may find it elsewhere)\n");
3097 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3099 Log("it will be deleted instead. It should be recloned.\n");
3102 unlink(isp->volSummary->fileName);
3104 } else if (!check) {
3105 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3107 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3113 AskOffline(VolumeId volumeId)
3117 for (i = 0; i < 3; i++) {
3118 code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3120 if (code == SYNC_OK) {
3122 } else if (code == SYNC_DENIED) {
3123 #ifdef DEMAND_ATTACH_ENABLE
3124 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3126 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3128 Abort("Salvage aborted\n");
3129 } else if (code == SYNC_BAD_COMMAND) {
3130 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3132 #ifdef DEMAND_ATTACH_ENABLE
3133 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3135 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3137 Abort("Salvage aborted\n");
3140 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3141 FSYNC_clientFinis();
3145 if (code != SYNC_OK) {
3146 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3147 Abort("Salvage aborted\n");
3152 AskOnline(VolumeId volumeId, char *partition)
3156 for (i = 0; i < 3; i++) {
3157 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3159 if (code == SYNC_OK) {
3161 } else if (code == SYNC_DENIED) {
3162 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3163 } else if (code == SYNC_BAD_COMMAND) {
3164 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3166 #ifdef DEMAND_ATTACH_ENABLE
3167 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3169 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3174 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3175 FSYNC_clientFinis();
3182 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3184 /* Volume parameter is passed in case iopen is upgraded in future to
3185 * require a volume Id to be passed
3188 IHandle_t *srcH, *destH;
3189 FdHandle_t *srcFdP, *destFdP;
3192 IH_INIT(srcH, device, rwvolume, inode1);
3193 srcFdP = IH_OPEN(srcH);
3194 assert(srcFdP != NULL);
3195 IH_INIT(destH, device, rwvolume, inode2);
3196 destFdP = IH_OPEN(destH);
3198 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3199 assert(FDH_WRITE(destFdP, buf, n) == n);
3201 FDH_REALLYCLOSE(srcFdP);
3202 FDH_REALLYCLOSE(destFdP);
3209 PrintInodeList(void)
3211 register struct ViceInodeInfo *ip;
3212 struct ViceInodeInfo *buf;
3213 struct afs_stat status;
3216 assert(afs_fstat(inodeFd, &status) == 0);
3217 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3218 assert(buf != NULL);
3219 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3220 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3221 for (ip = buf; nInodes--; ip++) {
3222 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3223 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3224 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3225 ip->u.param[2], ip->u.param[3]);
3231 PrintInodeSummary(void)
3234 struct InodeSummary *isp;
3236 for (i = 0; i < nVolumesInInodeFile; i++) {
3237 isp = &inodeSummary[i];
3238 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);
3243 PrintVolumeSummary(void)
3246 struct VolumeSummary *vsp;
3248 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3249 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3259 assert(0); /* Fork is never executed in the NT code path */
3274 if (main_thread != pthread_self())
3275 pthread_exit((void *)code);
3288 pid = wait(&status);
3290 if (WCOREDUMP(status))
3291 Log("\"%s\" core dumped!\n", prog);
3292 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3298 TimeStamp(time_t clock, int precision)
3301 static char timestamp[20];
3302 lt = localtime(&clock);
3304 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3306 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3311 CheckLogFile(char * log_path)
3313 char oldSlvgLog[AFSDIR_PATH_MAX];
3315 #ifndef AFS_NT40_ENV
3322 strcpy(oldSlvgLog, log_path);
3323 strcat(oldSlvgLog, ".old");
3325 renamefile(log_path, oldSlvgLog);
3326 logFile = afs_fopen(log_path, "a");
3328 if (!logFile) { /* still nothing, use stdout */
3332 #ifndef AFS_NAMEI_ENV
3333 AFS_DEBUG_IOPS_LOG(logFile);
3338 #ifndef AFS_NT40_ENV
3340 TimeStampLogFile(char * log_path)
3342 char stampSlvgLog[AFSDIR_PATH_MAX];
3347 lt = localtime(&now);
3348 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3349 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3350 log_path, lt->tm_year + 1900,
3351 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3354 /* try to link the logfile to a timestamped filename */
3355 /* if it fails, oh well, nothing we can do */
3356 link(log_path, stampSlvgLog);
3365 #ifndef AFS_NT40_ENV
3367 printf("Can't show log since using syslog.\n");
3376 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3379 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3382 while (fgets(line, sizeof(line), logFile))
3389 Log(const char *format, ...)
3395 va_start(args, format);
3396 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3398 #ifndef AFS_NT40_ENV
3400 syslog(LOG_INFO, "%s", tmp);
3404 gettimeofday(&now, 0);
3405 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3411 Abort(const char *format, ...)
3416 va_start(args, format);
3417 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3419 #ifndef AFS_NT40_ENV
3421 syslog(LOG_INFO, "%s", tmp);
3425 fprintf(logFile, "%s", tmp);
3440 p = (char *)malloc(strlen(s) + 1);
3447 /* Remove the FORCESALVAGE file */
3449 RemoveTheForce(char *path)
3451 if (!Testing && ForceSalvage) {
3452 if (chdir(path) == 0)
3453 unlink("FORCESALVAGE");
3457 #ifndef AFS_AIX32_ENV
3459 * UseTheForceLuke - see if we can use the force
3462 UseTheForceLuke(char *path)
3464 struct afs_stat force;
3466 assert(chdir(path) != -1);
3468 return (afs_stat("FORCESALVAGE", &force) == 0);
3472 * UseTheForceLuke - see if we can use the force
3475 * The VRMIX fsck will not muck with the filesystem it is supposedly
3476 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
3477 * muck directly with the root inode, which is within the normal
3479 * ListViceInodes() has a side effect of setting ForceSalvage if
3480 * it detects a need, based on root inode examination.
3483 UseTheForceLuke(char *path)
3486 return 0; /* sorry OB1 */
3491 /* NT support routines */
3493 static char execpathname[MAX_PATH];
3495 nt_SalvagePartition(char *partName, int jobn)
3500 if (!*execpathname) {
3501 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3502 if (!n || n == 1023)
3505 job.cj_magic = SALVAGER_MAGIC;
3506 job.cj_number = jobn;
3507 (void)strcpy(job.cj_part, partName);
3508 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3513 nt_SetupPartitionSalvage(void *datap, int len)
3515 childJob_t *jobp = (childJob_t *) datap;
3516 char logname[AFSDIR_PATH_MAX];
3518 if (len != sizeof(childJob_t))
3520 if (jobp->cj_magic != SALVAGER_MAGIC)
3525 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3527 logFile = afs_fopen(logname, "w");
3535 #endif /* AFS_NT40_ENV */