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;
2284 afs_int32 parentUnique = 1;
2285 struct VnodeEssence *vnodeEssence;
2289 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2291 IH_IREAD(vnodeInfo[vLarge].handle,
2292 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2294 assert(lcode == sizeof(vnode));
2295 oldinode = VNDISK_GET_INO(&vnode);
2296 /* Increment the version number by a whole lot to avoid problems with
2297 * clients that were promised new version numbers--but the file server
2298 * crashed before the versions were written to disk.
2301 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2302 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2304 assert(VALID_INO(newinode));
2305 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2307 /* Assign . and .. vnode numbers from dir and vnode.parent.
2308 * The uniquifier for . is in the vnode.
2309 * The uniquifier for .. might be set to a bogus value of 1 and
2310 * the salvager will later clean it up.
2312 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2313 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2316 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2318 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2323 /* didn't really build the new directory properly, let's just give up. */
2324 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2325 Log("Directory salvage returned code %d, continuing.\n", code);
2327 Log("also failed to decrement link count on new inode");
2331 Log("Checking the results of the directory salvage...\n");
2332 if (!DirOK(&newdir)) {
2333 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2334 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2339 VNDISK_SET_INO(&vnode, newinode);
2340 VNDISK_SET_LEN(&vnode, Length(&newdir));
2342 IH_IWRITE(vnodeInfo[vLarge].handle,
2343 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2345 assert(lcode == sizeof(vnode));
2347 nt_sync(fileSysDevice);
2349 sync(); /* this is slow, but hopefully rarely called. We don't have
2350 * an open FD on the file itself to fsync.
2353 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2355 dir->dirHandle = newdir;
2359 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2362 struct VnodeEssence *vnodeEssence;
2363 afs_int32 dirOrphaned, todelete;
2365 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2367 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2368 if (vnodeEssence == NULL) {
2370 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2374 assert(Delete(&dir->dirHandle, name) == 0);
2379 #ifndef AFS_NAMEI_ENV
2380 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2381 * mount inode for the partition. If this inode were deleted, it would crash
2384 if (vnodeEssence->InodeNumber == 0) {
2385 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"));
2388 assert(Delete(&dir->dirHandle, name) == 0);
2395 if (!(vnodeNumber & 1) && !Showmode
2396 && !(vnodeEssence->count || vnodeEssence->unique
2397 || vnodeEssence->modeBits)) {
2398 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2399 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2400 vnodeNumber, unique,
2401 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2406 assert(Delete(&dir->dirHandle, name) == 0);
2412 /* Check if the Uniquifiers match. If not, change the directory entry
2413 * so its unique matches the vnode unique. Delete if the unique is zero
2414 * or if the directory is orphaned.
2416 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2417 if (!vnodeEssence->unique
2418 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2419 /* This is an orphaned directory. Don't delete the . or ..
2420 * entry. Otherwise, it will get created in the next
2421 * salvage and deleted again here. So Just skip it.
2426 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2429 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")));
2433 fid.Vnode = vnodeNumber;
2434 fid.Unique = vnodeEssence->unique;
2436 assert(Delete(&dir->dirHandle, name) == 0);
2438 assert(Create(&dir->dirHandle, name, &fid) == 0);
2441 return; /* no need to continue */
2444 if (strcmp(name, ".") == 0) {
2445 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2448 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2451 assert(Delete(&dir->dirHandle, ".") == 0);
2452 fid.Vnode = dir->vnodeNumber;
2453 fid.Unique = dir->unique;
2454 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2457 vnodeNumber = fid.Vnode; /* Get the new Essence */
2458 unique = fid.Unique;
2459 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2462 } else if (strcmp(name, "..") == 0) {
2465 struct VnodeEssence *dotdot;
2466 pa.Vnode = dir->parent;
2467 dotdot = CheckVnodeNumber(pa.Vnode);
2468 assert(dotdot != NULL); /* XXX Should not be assert */
2469 pa.Unique = dotdot->unique;
2471 pa.Vnode = dir->vnodeNumber;
2472 pa.Unique = dir->unique;
2474 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2476 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2479 assert(Delete(&dir->dirHandle, "..") == 0);
2480 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2483 vnodeNumber = pa.Vnode; /* Get the new Essence */
2485 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2487 dir->haveDotDot = 1;
2488 } else if (strncmp(name, ".__afs", 6) == 0) {
2490 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);
2494 assert(Delete(&dir->dirHandle, name) == 0);
2496 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2497 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2500 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2501 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);
2502 if (ShowMounts && (vnodeEssence->type == vSymlink)
2503 && !(vnodeEssence->modeBits & 0111)) {
2509 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2510 vnodeEssence->InodeNumber);
2512 assert(fdP != NULL);
2513 size = FDH_SIZE(fdP);
2515 memset(buf, 0, 1024);
2518 code = FDH_READ(fdP, buf, size);
2519 assert(code == size);
2520 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2521 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2522 dir->name ? dir->name : "??", name, buf);
2523 FDH_REALLYCLOSE(fdP);
2526 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2527 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);
2528 if (vnodeIdToClass(vnodeNumber) == vLarge
2529 && vnodeEssence->name == NULL) {
2531 if ((n = (char *)malloc(strlen(name) + 1)))
2533 vnodeEssence->name = n;
2536 /* The directory entry points to the vnode. Check to see if the
2537 * vnode points back to the directory. If not, then let the
2538 * directory claim it (else it might end up orphaned). Vnodes
2539 * already claimed by another directory are deleted from this
2540 * directory: hardlinks to the same vnode are not allowed
2541 * from different directories.
2543 if (vnodeEssence->parent != dir->vnodeNumber) {
2544 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2545 /* Vnode does not point back to this directory.
2546 * Orphaned dirs cannot claim a file (it may belong to
2547 * another non-orphaned dir).
2550 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);
2552 vnodeEssence->parent = dir->vnodeNumber;
2553 vnodeEssence->changed = 1;
2555 /* Vnode was claimed by another directory */
2558 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 " : ""));
2559 } else if (vnodeNumber == 1) {
2560 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 " : ""));
2562 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 " : ""));
2567 assert(Delete(&dir->dirHandle, name) == 0);
2572 /* This directory claims the vnode */
2573 vnodeEssence->claimed = 1;
2575 vnodeEssence->count--;
2579 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2581 register struct VnodeInfo *vip = &vnodeInfo[class];
2582 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2583 char buf[SIZEOF_LARGEDISKVNODE];
2584 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2586 StreamHandle_t *file;
2591 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2592 fdP = IH_OPEN(vip->handle);
2593 assert(fdP != NULL);
2594 file = FDH_FDOPEN(fdP, "r+");
2595 assert(file != NULL);
2596 size = OS_SIZE(fdP->fd_fd);
2598 vip->nVnodes = (size / vcp->diskSize) - 1;
2599 if (vip->nVnodes > 0) {
2600 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2601 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2602 assert((vip->vnodes = (struct VnodeEssence *)
2603 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2604 if (class == vLarge) {
2605 assert((vip->inodes = (Inode *)
2606 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2615 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2616 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2617 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2618 nVnodes--, vnodeIndex++) {
2619 if (vnode->type != vNull) {
2620 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2621 afs_fsize_t vnodeLength;
2622 vip->nAllocatedVnodes++;
2623 vep->count = vnode->linkCount;
2624 VNDISK_GET_LEN(vnodeLength, vnode);
2625 vep->blockCount = nBlocks(vnodeLength);
2626 vip->volumeBlockCount += vep->blockCount;
2627 vep->parent = vnode->parent;
2628 vep->unique = vnode->uniquifier;
2629 if (*maxu < vnode->uniquifier)
2630 *maxu = vnode->uniquifier;
2631 vep->modeBits = vnode->modeBits;
2632 vep->InodeNumber = VNDISK_GET_INO(vnode);
2633 vep->type = vnode->type;
2634 vep->author = vnode->author;
2635 vep->owner = vnode->owner;
2636 vep->group = vnode->group;
2637 if (vnode->type == vDirectory) {
2638 assert(class == vLarge);
2639 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2648 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2650 struct VnodeEssence *parentvp;
2656 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2657 && GetDirName(vp->parent, parentvp, path)) {
2659 strcat(path, vp->name);
2665 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2666 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2669 IsVnodeOrphaned(VnodeId vnode)
2671 struct VnodeEssence *vep;
2674 return (1); /* Vnode zero does not exist */
2676 return (0); /* The root dir vnode is always claimed */
2677 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2678 if (!vep || !vep->claimed)
2679 return (1); /* Vnode is not claimed - it is orphaned */
2681 return (IsVnodeOrphaned(vep->parent));
2685 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2686 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2689 static struct DirSummary dir;
2690 static struct DirHandle dirHandle;
2691 struct VnodeEssence *parent;
2692 static char path[MAXPATHLEN];
2695 if (dirVnodeInfo->vnodes[i].salvaged)
2696 return; /* already salvaged */
2699 dirVnodeInfo->vnodes[i].salvaged = 1;
2701 if (dirVnodeInfo->inodes[i] == 0)
2702 return; /* Not allocated to a directory */
2704 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2705 if (dirVnodeInfo->vnodes[i].parent) {
2706 Log("Bad parent, vnode 1; %s...\n",
2707 (Testing ? "skipping" : "salvaging"));
2708 dirVnodeInfo->vnodes[i].parent = 0;
2709 dirVnodeInfo->vnodes[i].changed = 1;
2712 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2713 if (parent && parent->salvaged == 0)
2714 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2715 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2716 rootdir, rootdirfound);
2719 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2720 dir.unique = dirVnodeInfo->vnodes[i].unique;
2723 dir.parent = dirVnodeInfo->vnodes[i].parent;
2724 dir.haveDot = dir.haveDotDot = 0;
2725 dir.ds_linkH = alinkH;
2726 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2727 dirVnodeInfo->inodes[i]);
2729 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2732 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2733 (Testing ? "skipping" : "salvaging"));
2736 CopyAndSalvage(&dir);
2740 dirHandle = dir.dirHandle;
2743 GetDirName(bitNumberToVnodeNumber(i, vLarge),
2744 &dirVnodeInfo->vnodes[i], path);
2747 /* If enumeration failed for random reasons, we will probably delete
2748 * too much stuff, so we guard against this instead.
2750 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2753 /* Delete the old directory if it was copied in order to salvage.
2754 * CopyOnWrite has written the new inode # to the disk, but we still
2755 * have the old one in our local structure here. Thus, we idec the
2759 if (dir.copied && !Testing) {
2760 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2762 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2765 /* Remember rootdir DirSummary _after_ it has been judged */
2766 if (dir.vnodeNumber == 1 && dir.unique == 1) {
2767 memcpy(rootdir, &dir, sizeof(struct DirSummary));
2775 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2777 /* This routine, for now, will only be called for read-write volumes */
2779 int BlocksInVolume = 0, FilesInVolume = 0;
2780 register VnodeClass class;
2781 struct DirSummary rootdir, oldrootdir;
2782 struct VnodeInfo *dirVnodeInfo;
2783 struct VnodeDiskObject vnode;
2784 VolumeDiskData volHeader;
2786 int orphaned, rootdirfound = 0;
2787 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
2788 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
2789 struct VnodeEssence *vep;
2792 afs_sfsize_t nBytes;
2794 VnodeId LFVnode, ThisVnode;
2795 Unique LFUnique, ThisUnique;
2798 vid = rwIsp->volSummary->header.id;
2799 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2800 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2801 assert(nBytes == sizeof(volHeader));
2802 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2803 assert(volHeader.destroyMe != DESTROY_ME);
2804 /* (should not have gotten this far with DESTROY_ME flag still set!) */
2806 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2808 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2811 dirVnodeInfo = &vnodeInfo[vLarge];
2812 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2813 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2821 /* Parse each vnode looking for orphaned vnodes and
2822 * connect them to the tree as orphaned (if requested).
2824 oldrootdir = rootdir;
2825 for (class = 0; class < nVNODECLASSES; class++) {
2826 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2827 vep = &(vnodeInfo[class].vnodes[v]);
2828 ThisVnode = bitNumberToVnodeNumber(v, class);
2829 ThisUnique = vep->unique;
2831 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2832 continue; /* Ignore unused, claimed, and root vnodes */
2834 /* This vnode is orphaned. If it is a directory vnode, then the '..'
2835 * entry in this vnode had incremented the parent link count (In
2836 * JudgeEntry()). We need to go to the parent and decrement that
2837 * link count. But if the parent's unique is zero, then the parent
2838 * link count was not incremented in JudgeEntry().
2840 if (class == vLarge) { /* directory vnode */
2841 pv = vnodeIdToBitNumber(vep->parent);
2842 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2843 vnodeInfo[vLarge].vnodes[pv].count++;
2847 continue; /* If no rootdir, can't attach orphaned files */
2849 /* Here we attach orphaned files and directories into the
2850 * root directory, LVVnode, making sure link counts stay correct.
2852 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2853 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
2854 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
2856 /* Update this orphaned vnode's info. Its parent info and
2857 * link count (do for orphaned directories and files).
2859 vep->parent = LFVnode; /* Parent is the root dir */
2860 vep->unique = LFUnique;
2863 vep->count--; /* Inc link count (root dir will pt to it) */
2865 /* If this orphaned vnode is a directory, change '..'.
2866 * The name of the orphaned dir/file is unknown, so we
2867 * build a unique name. No need to CopyOnWrite the directory
2868 * since it is not connected to tree in BK or RO volume and
2869 * won't be visible there.
2871 if (class == vLarge) {
2875 /* Remove and recreate the ".." entry in this orphaned directory */
2876 SetSalvageDirHandle(&dh, vid, fileSysDevice,
2877 vnodeInfo[class].inodes[v]);
2879 pa.Unique = LFUnique;
2880 assert(Delete(&dh, "..") == 0);
2881 assert(Create(&dh, "..", &pa) == 0);
2883 /* The original parent's link count was decremented above.
2884 * Here we increment the new parent's link count.
2886 pv = vnodeIdToBitNumber(LFVnode);
2887 vnodeInfo[vLarge].vnodes[pv].count--;
2891 /* Go to the root dir and add this entry. The link count of the
2892 * root dir was incremented when ".." was created. Try 10 times.
2894 for (j = 0; j < 10; j++) {
2895 pa.Vnode = ThisVnode;
2896 pa.Unique = ThisUnique;
2898 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2900 vLarge) ? "__ORPHANDIR__" :
2901 "__ORPHANFILE__"), ThisVnode,
2904 CopyOnWrite(&rootdir);
2905 code = Create(&rootdir.dirHandle, npath, &pa);
2909 ThisUnique += 50; /* Try creating a different file */
2912 Log("Attaching orphaned %s to volume's root dir as %s\n",
2913 ((class == vLarge) ? "directory" : "file"), npath);
2915 } /* for each vnode in the class */
2916 } /* for each class of vnode */
2918 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2920 if (!oldrootdir.copied && rootdir.copied) {
2922 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2925 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
2928 DFlush(); /* Flush the changes */
2929 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
2930 Log("Cannot attach orphaned files and directories: Root directory not found\n");
2931 orphans = ORPH_IGNORE;
2934 /* Write out all changed vnodes. Orphaned files and directories
2935 * will get removed here also (if requested).
2937 for (class = 0; class < nVNODECLASSES; class++) {
2938 int nVnodes = vnodeInfo[class].nVnodes;
2939 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2940 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
2941 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
2942 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
2943 for (i = 0; i < nVnodes; i++) {
2944 register struct VnodeEssence *vnp = &vnodes[i];
2945 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
2947 /* If the vnode is good but is unclaimed (not listed in
2948 * any directory entries), then it is orphaned.
2951 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
2952 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
2956 if (vnp->changed || vnp->count) {
2960 IH_IREAD(vnodeInfo[class].handle,
2961 vnodeIndexOffset(vcp, vnodeNumber),
2962 (char *)&vnode, sizeof(vnode));
2963 assert(nBytes == sizeof(vnode));
2965 vnode.parent = vnp->parent;
2966 oldCount = vnode.linkCount;
2967 vnode.linkCount = vnode.linkCount - vnp->count;
2970 orphaned = IsVnodeOrphaned(vnodeNumber);
2972 if (!vnp->todelete) {
2973 /* Orphans should have already been attached (if requested) */
2974 assert(orphans != ORPH_ATTACH);
2975 oblocks += vnp->blockCount;
2978 if (((orphans == ORPH_REMOVE) || vnp->todelete)
2980 BlocksInVolume -= vnp->blockCount;
2982 if (VNDISK_GET_INO(&vnode)) {
2984 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
2987 memset(&vnode, 0, sizeof(vnode));
2989 } else if (vnp->count) {
2991 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
2995 vnode.dataVersion++;
2998 IH_IWRITE(vnodeInfo[class].handle,
2999 vnodeIndexOffset(vcp, vnodeNumber),
3000 (char *)&vnode, sizeof(vnode));
3001 assert(nBytes == sizeof(vnode));
3007 if (!Showmode && ofiles) {
3008 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3010 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3014 for (class = 0; class < nVNODECLASSES; class++) {
3015 register struct VnodeInfo *vip = &vnodeInfo[class];
3016 for (i = 0; i < vip->nVnodes; i++)
3017 if (vip->vnodes[i].name)
3018 free(vip->vnodes[i].name);
3025 /* Set correct resource utilization statistics */
3026 volHeader.filecount = FilesInVolume;
3027 volHeader.diskused = BlocksInVolume;
3029 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3030 if (volHeader.uniquifier < (maxunique + 1)) {
3032 Log("Volume uniquifier is too low; fixed\n");
3033 /* Plus 2,000 in case there are workstations out there with
3034 * cached vnodes that have since been deleted
3036 volHeader.uniquifier = (maxunique + 1 + 2000);
3039 /* Turn off the inUse bit; the volume's been salvaged! */
3040 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3041 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3042 volHeader.inService = 1; /* allow service again */
3043 volHeader.needsCallback = (VolumeChanged != 0);
3044 volHeader.dontSalvage = DONT_SALVAGE;
3047 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3048 assert(nBytes == sizeof(volHeader));
3051 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3052 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3053 FilesInVolume, BlocksInVolume);
3055 IH_RELEASE(vnodeInfo[vSmall].handle);
3056 IH_RELEASE(vnodeInfo[vLarge].handle);
3062 ClearROInUseBit(struct VolumeSummary *summary)
3064 IHandle_t *h = summary->volumeInfoHandle;
3065 afs_sfsize_t nBytes;
3067 VolumeDiskData volHeader;
3069 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3070 assert(nBytes == sizeof(volHeader));
3071 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3072 volHeader.inUse = 0;
3073 volHeader.needsSalvaged = 0;
3074 volHeader.inService = 1;
3075 volHeader.dontSalvage = DONT_SALVAGE;
3077 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3078 assert(nBytes == sizeof(volHeader));
3083 * Possible delete the volume.
3085 * deleteMe - Always do so, only a partial volume.
3088 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3091 if (readOnly(isp) || deleteMe) {
3092 if (isp->volSummary && isp->volSummary->fileName) {
3095 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);
3097 Log("It will be deleted on this server (you may find it elsewhere)\n");
3100 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3102 Log("it will be deleted instead. It should be recloned.\n");
3105 unlink(isp->volSummary->fileName);
3107 } else if (!check) {
3108 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3110 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3116 AskOffline(VolumeId volumeId)
3120 for (i = 0; i < 3; i++) {
3121 code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3123 if (code == SYNC_OK) {
3125 } else if (code == SYNC_DENIED) {
3126 #ifdef DEMAND_ATTACH_ENABLE
3127 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3129 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3131 Abort("Salvage aborted\n");
3132 } else if (code == SYNC_BAD_COMMAND) {
3133 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3135 #ifdef DEMAND_ATTACH_ENABLE
3136 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3138 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3140 Abort("Salvage aborted\n");
3143 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3144 FSYNC_clientFinis();
3148 if (code != SYNC_OK) {
3149 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3150 Abort("Salvage aborted\n");
3155 AskOnline(VolumeId volumeId, char *partition)
3159 for (i = 0; i < 3; i++) {
3160 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3162 if (code == SYNC_OK) {
3164 } else if (code == SYNC_DENIED) {
3165 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3166 } else if (code == SYNC_BAD_COMMAND) {
3167 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3169 #ifdef DEMAND_ATTACH_ENABLE
3170 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3172 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3177 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3178 FSYNC_clientFinis();
3185 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3187 /* Volume parameter is passed in case iopen is upgraded in future to
3188 * require a volume Id to be passed
3191 IHandle_t *srcH, *destH;
3192 FdHandle_t *srcFdP, *destFdP;
3195 IH_INIT(srcH, device, rwvolume, inode1);
3196 srcFdP = IH_OPEN(srcH);
3197 assert(srcFdP != NULL);
3198 IH_INIT(destH, device, rwvolume, inode2);
3199 destFdP = IH_OPEN(destH);
3201 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3202 assert(FDH_WRITE(destFdP, buf, n) == n);
3204 FDH_REALLYCLOSE(srcFdP);
3205 FDH_REALLYCLOSE(destFdP);
3212 PrintInodeList(void)
3214 register struct ViceInodeInfo *ip;
3215 struct ViceInodeInfo *buf;
3216 struct afs_stat status;
3219 assert(afs_fstat(inodeFd, &status) == 0);
3220 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3221 assert(buf != NULL);
3222 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3223 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3224 for (ip = buf; nInodes--; ip++) {
3225 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3226 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3227 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3228 ip->u.param[2], ip->u.param[3]);
3234 PrintInodeSummary(void)
3237 struct InodeSummary *isp;
3239 for (i = 0; i < nVolumesInInodeFile; i++) {
3240 isp = &inodeSummary[i];
3241 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);
3246 PrintVolumeSummary(void)
3249 struct VolumeSummary *vsp;
3251 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3252 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3262 assert(0); /* Fork is never executed in the NT code path */
3277 if (main_thread != pthread_self())
3278 pthread_exit((void *)code);
3291 pid = wait(&status);
3293 if (WCOREDUMP(status))
3294 Log("\"%s\" core dumped!\n", prog);
3295 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3301 TimeStamp(time_t clock, int precision)
3304 static char timestamp[20];
3305 lt = localtime(&clock);
3307 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3309 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3314 CheckLogFile(char * log_path)
3316 char oldSlvgLog[AFSDIR_PATH_MAX];
3318 #ifndef AFS_NT40_ENV
3325 strcpy(oldSlvgLog, log_path);
3326 strcat(oldSlvgLog, ".old");
3328 renamefile(log_path, oldSlvgLog);
3329 logFile = afs_fopen(log_path, "a");
3331 if (!logFile) { /* still nothing, use stdout */
3335 #ifndef AFS_NAMEI_ENV
3336 AFS_DEBUG_IOPS_LOG(logFile);
3341 #ifndef AFS_NT40_ENV
3343 TimeStampLogFile(char * log_path)
3345 char stampSlvgLog[AFSDIR_PATH_MAX];
3350 lt = localtime(&now);
3351 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3352 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3353 log_path, lt->tm_year + 1900,
3354 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3357 /* try to link the logfile to a timestamped filename */
3358 /* if it fails, oh well, nothing we can do */
3359 link(log_path, stampSlvgLog);
3368 #ifndef AFS_NT40_ENV
3370 printf("Can't show log since using syslog.\n");
3379 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3382 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3385 while (fgets(line, sizeof(line), logFile))
3392 Log(const char *format, ...)
3398 va_start(args, format);
3399 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3401 #ifndef AFS_NT40_ENV
3403 syslog(LOG_INFO, "%s", tmp);
3407 gettimeofday(&now, 0);
3408 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3414 Abort(const char *format, ...)
3419 va_start(args, format);
3420 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3422 #ifndef AFS_NT40_ENV
3424 syslog(LOG_INFO, "%s", tmp);
3428 fprintf(logFile, "%s", tmp);
3443 p = (char *)malloc(strlen(s) + 1);
3450 /* Remove the FORCESALVAGE file */
3452 RemoveTheForce(char *path)
3454 if (!Testing && ForceSalvage) {
3455 if (chdir(path) == 0)
3456 unlink("FORCESALVAGE");
3460 #ifndef AFS_AIX32_ENV
3462 * UseTheForceLuke - see if we can use the force
3465 UseTheForceLuke(char *path)
3467 struct afs_stat force;
3469 assert(chdir(path) != -1);
3471 return (afs_stat("FORCESALVAGE", &force) == 0);
3475 * UseTheForceLuke - see if we can use the force
3478 * The VRMIX fsck will not muck with the filesystem it is supposedly
3479 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
3480 * muck directly with the root inode, which is within the normal
3482 * ListViceInodes() has a side effect of setting ForceSalvage if
3483 * it detects a need, based on root inode examination.
3486 UseTheForceLuke(char *path)
3489 return 0; /* sorry OB1 */
3494 /* NT support routines */
3496 static char execpathname[MAX_PATH];
3498 nt_SalvagePartition(char *partName, int jobn)
3503 if (!*execpathname) {
3504 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3505 if (!n || n == 1023)
3508 job.cj_magic = SALVAGER_MAGIC;
3509 job.cj_number = jobn;
3510 (void)strcpy(job.cj_part, partName);
3511 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3516 nt_SetupPartitionSalvage(void *datap, int len)
3518 childJob_t *jobp = (childJob_t *) datap;
3519 char logname[AFSDIR_PATH_MAX];
3521 if (len != sizeof(childJob_t))
3523 if (jobp->cj_magic != SALVAGER_MAGIC)
3528 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3530 logFile = afs_fopen(logname, "w");
3538 #endif /* AFS_NT40_ENV */