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 extern int Testing; /* -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);
1456 struct ViceInodeInfo *ip;
1457 CreateLinkTable(isp, ino);
1458 fdP = IH_OPEN(VGLinkH);
1459 /* Sync fake 1 link counts to the link table, now that it exists */
1461 namei_SetNonZLC(fdP, ino);
1462 for (i = 0; i < nVols; i++) {
1463 ip = allInodes + isp[i].index;
1464 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++)
1465 namei_SetNonZLC(fdP, ip[j].inodeNumber);
1471 FDH_REALLYCLOSE(fdP);
1473 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1476 /* Salvage in reverse order--read/write volume last; this way any
1477 * Inodes not referenced by the time we salvage the read/write volume
1478 * can be picked up by the read/write volume */
1479 /* ACTUALLY, that's not done right now--the inodes just vanish */
1480 for (i = nVols - 1; i >= salvageTo; i--) {
1482 struct InodeSummary *lisp = &isp[i];
1483 #ifdef AFS_NAMEI_ENV
1484 /* If only the RO is present on this partition, the link table
1485 * shows up as a RW volume special file. Need to make sure the
1486 * salvager doesn't try to salvage the non-existent RW.
1488 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1489 /* If this only special inode is the link table, continue */
1490 if (inodes->u.special.type == VI_LINKTABLE) {
1497 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1498 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1499 /* Check inodes twice. The second time do things seriously. This
1500 * way the whole RO volume can be deleted, below, if anything goes wrong */
1501 for (check = 1; check >= 0; check--) {
1503 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1505 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1506 if (rw && deleteMe) {
1507 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1508 * volume won't be called */
1514 if (rw && check == 1)
1516 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1517 MaybeZapVolume(lisp, "Vnode index", 0, check);
1523 /* Fix actual inode counts */
1525 Log("totalInodes %d\n",totalInodes);
1526 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1527 static int TraceBadLinkCounts = 0;
1528 #ifdef AFS_NAMEI_ENV
1529 if (VGLinkH->ih_ino == ip->inodeNumber) {
1530 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1531 VGLinkH_p1 = ip->u.param[0];
1532 continue; /* Deal with this last. */
1535 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1536 TraceBadLinkCounts--; /* Limit reports, per volume */
1537 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]);
1539 while (ip->linkCount > 0) {
1540 /* below used to assert, not break */
1542 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1543 Log("idec failed. inode %s errno %d\n",
1544 PrintInode(NULL, ip->inodeNumber), errno);
1550 while (ip->linkCount < 0) {
1551 /* these used to be asserts */
1553 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1554 Log("iinc failed. inode %s errno %d\n",
1555 PrintInode(NULL, ip->inodeNumber), errno);
1562 #ifdef AFS_NAMEI_ENV
1563 while (dec_VGLinkH > 0) {
1564 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1565 Log("idec failed on link table, errno = %d\n", errno);
1569 while (dec_VGLinkH < 0) {
1570 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1571 Log("iinc failed on link table, errno = %d\n", errno);
1578 /* Directory consistency checks on the rw volume */
1580 SalvageVolume(isp, VGLinkH);
1581 IH_RELEASE(VGLinkH);
1583 if (canfork && !debug) {
1590 QuickCheck(register struct InodeSummary *isp, int nVols)
1592 /* Check headers BEFORE forking */
1596 for (i = 0; i < nVols; i++) {
1597 struct VolumeSummary *vs = isp[i].volSummary;
1598 VolumeDiskData volHeader;
1600 /* Don't salvage just because phantom rw volume is there... */
1601 /* (If a read-only volume exists, read/write inodes must also exist) */
1602 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1606 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1607 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1608 == sizeof(volHeader)
1609 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1610 && volHeader.dontSalvage == DONT_SALVAGE
1611 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1612 if (volHeader.inUse == 1) {
1613 volHeader.inUse = 0;
1614 volHeader.inService = 1;
1616 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1617 != sizeof(volHeader)) {
1633 /* SalvageVolumeHeaderFile
1635 * Salvage the top level V*.vol header file. Make sure the special files
1636 * exist and that there are no duplicates.
1638 * Calls SalvageHeader for each possible type of volume special file.
1642 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1643 register struct ViceInodeInfo *inodes, int RW,
1644 int check, int *deleteMe)
1648 register struct ViceInodeInfo *ip;
1649 int allinodesobsolete = 1;
1650 struct VolumeDiskHeader diskHeader;
1654 memset(&tempHeader, 0, sizeof(tempHeader));
1655 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1656 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1657 tempHeader.id = isp->volumeId;
1658 tempHeader.parent = isp->RWvolumeId;
1659 /* Check for duplicates (inodes are sorted by type field) */
1660 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1661 ip = &inodes[isp->index + i];
1662 if (ip->u.special.type == (ip + 1)->u.special.type) {
1664 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1668 for (i = 0; i < isp->nSpecialInodes; i++) {
1669 ip = &inodes[isp->index + i];
1670 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1672 Log("Rubbish header inode\n");
1675 Log("Rubbish header inode; deleted\n");
1676 } else if (!stuff[ip->u.special.type - 1].obsolete) {
1677 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1678 if (!check && ip->u.special.type != VI_LINKTABLE)
1679 ip->linkCount--; /* Keep the inode around */
1680 allinodesobsolete = 0;
1684 if (allinodesobsolete) {
1691 VGLinkH_cnt++; /* one for every header. */
1693 if (!RW && !check && isp->volSummary) {
1694 ClearROInUseBit(isp->volSummary);
1698 for (i = 0; i < MAXINODETYPE; i++) {
1699 if (stuff[i].inodeType == VI_LINKTABLE) {
1700 /* Gross hack: SalvageHeader does a bcmp on the volume header.
1701 * And we may have recreated the link table earlier, so set the
1702 * RW header as well.
1704 if (VALID_INO(VGLinkH->ih_ino)) {
1705 *stuff[i].inode = VGLinkH->ih_ino;
1709 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
1713 if (isp->volSummary == NULL) {
1715 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1717 Log("No header file for volume %u\n", isp->volumeId);
1721 Log("No header file for volume %u; %screating %s/%s\n",
1722 isp->volumeId, (Testing ? "it would have been " : ""),
1723 fileSysPathName, name);
1724 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
1725 assert(headerFd != -1);
1726 isp->volSummary = (struct VolumeSummary *)
1727 malloc(sizeof(struct VolumeSummary));
1728 isp->volSummary->fileName = ToString(name);
1731 /* hack: these two fields are obsolete... */
1732 isp->volSummary->header.volumeAcl = 0;
1733 isp->volSummary->header.volumeMountTable = 0;
1736 (&isp->volSummary->header, &tempHeader,
1737 sizeof(struct VolumeHeader))) {
1738 /* We often remove the name before calling us, so we make a fake one up */
1739 if (isp->volSummary->fileName) {
1740 strcpy(name, isp->volSummary->fileName);
1742 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
1743 isp->volSummary->fileName = ToString(name);
1746 Log("Header file %s is damaged or no longer valid%s\n", name,
1747 (check ? "" : "; repairing"));
1751 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
1752 assert(headerFd != -1);
1756 memcpy(&isp->volSummary->header, &tempHeader,
1757 sizeof(struct VolumeHeader));
1760 Log("It would have written a new header file for volume %u\n",
1763 VolumeHeaderToDisk(&diskHeader, &tempHeader);
1764 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
1765 != sizeof(struct VolumeDiskHeader)) {
1766 Log("Couldn't rewrite volume header file!\n");
1773 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
1774 isp->volSummary->header.volumeInfo);
1779 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
1783 VolumeDiskData volumeInfo;
1784 struct versionStamp fileHeader;
1793 #ifndef AFS_NAMEI_ENV
1794 if (sp->inodeType == VI_LINKTABLE)
1797 if (*(sp->inode) == 0) {
1799 Log("Missing inode in volume header (%s)\n", sp->description);
1803 Log("Missing inode in volume header (%s); %s\n", sp->description,
1804 (Testing ? "it would have recreated it" : "recreating"));
1807 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1808 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
1809 if (!VALID_INO(*(sp->inode)))
1811 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
1812 sp->description, errno);
1817 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
1818 fdP = IH_OPEN(specH);
1819 if (OKToZap && (fdP == NULL) && BadError(errno)) {
1820 /* bail out early and destroy the volume */
1822 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
1829 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
1830 sp->description, errno);
1833 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
1834 || header.fileHeader.magic != sp->stamp.magic)) {
1836 Log("Part of the header (%s) is corrupted\n", sp->description);
1837 FDH_REALLYCLOSE(fdP);
1841 Log("Part of the header (%s) is corrupted; recreating\n",
1845 if (sp->inodeType == VI_VOLINFO
1846 && header.volumeInfo.destroyMe == DESTROY_ME) {
1849 FDH_REALLYCLOSE(fdP);
1853 if (recreate && !Testing) {
1856 ("Internal error: recreating volume header (%s) in check mode\n",
1858 code = FDH_TRUNC(fdP, 0);
1860 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
1861 sp->description, errno);
1863 /* The following code should be moved into vutil.c */
1864 if (sp->inodeType == VI_VOLINFO) {
1866 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
1867 header.volumeInfo.stamp = sp->stamp;
1868 header.volumeInfo.id = isp->volumeId;
1869 header.volumeInfo.parentId = isp->RWvolumeId;
1870 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
1871 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
1872 isp->volumeId, isp->volumeId);
1873 header.volumeInfo.inService = 0;
1874 header.volumeInfo.blessed = 0;
1875 /* The + 1000 is a hack in case there are any files out in venus caches */
1876 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
1877 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
1878 header.volumeInfo.needsCallback = 0;
1879 gettimeofday(&tp, 0);
1880 header.volumeInfo.creationDate = tp.tv_sec;
1881 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1883 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1884 sp->description, errno);
1887 FDH_WRITE(fdP, (char *)&header.volumeInfo,
1888 sizeof(header.volumeInfo));
1889 if (code != sizeof(header.volumeInfo)) {
1892 ("Unable to write volume header file (%s) (errno = %d)\n",
1893 sp->description, errno);
1894 Abort("Unable to write entire volume header file (%s)\n",
1898 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1900 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
1901 sp->description, errno);
1903 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
1904 if (code != sizeof(sp->stamp)) {
1907 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
1908 sp->description, errno);
1910 ("Unable to write entire version stamp in volume header file (%s)\n",
1915 FDH_REALLYCLOSE(fdP);
1917 if (sp->inodeType == VI_VOLINFO) {
1918 VolInfo = header.volumeInfo;
1921 if (VolInfo.updateDate) {
1922 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
1924 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
1925 (Testing ? "it would have been " : ""), update);
1927 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
1929 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
1930 VolInfo.id, update);
1940 SalvageVnodes(register struct InodeSummary *rwIsp,
1941 register struct InodeSummary *thisIsp,
1942 register struct ViceInodeInfo *inodes, int check)
1944 int ilarge, ismall, ioffset, RW, nInodes;
1945 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
1948 RW = (rwIsp == thisIsp);
1949 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
1951 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
1952 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1953 if (check && ismall == -1)
1956 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
1957 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
1958 return (ilarge == 0 && ismall == 0 ? 0 : -1);
1962 SalvageIndex(Inode ino, VnodeClass class, int RW,
1963 register struct ViceInodeInfo *ip, int nInodes,
1964 struct VolumeSummary *volSummary, int check)
1966 VolumeId volumeNumber;
1967 char buf[SIZEOF_LARGEDISKVNODE];
1968 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1970 StreamHandle_t *file;
1971 struct VnodeClassInfo *vcp;
1973 afs_fsize_t vnodeLength;
1974 int vnodeIndex, nVnodes;
1975 afs_ino_str_t stmp1, stmp2;
1979 volumeNumber = volSummary->header.id;
1980 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
1981 fdP = IH_OPEN(handle);
1982 assert(fdP != NULL);
1983 file = FDH_FDOPEN(fdP, "r+");
1984 assert(file != NULL);
1985 vcp = &VnodeClassInfo[class];
1986 size = OS_SIZE(fdP->fd_fd);
1988 nVnodes = (size / vcp->diskSize) - 1;
1990 assert((nVnodes + 1) * vcp->diskSize == size);
1991 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
1995 for (vnodeIndex = 0;
1996 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
1997 nVnodes--, vnodeIndex++) {
1998 if (vnode->type != vNull) {
1999 int vnodeChanged = 0;
2000 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2001 /* Log programs that belong to root (potentially suid root);
2002 * don't bother for read-only or backup volumes */
2003 #ifdef notdef /* This is done elsewhere */
2004 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2005 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);
2007 if (VNDISK_GET_INO(vnode) == 0) {
2009 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2010 memset(vnode, 0, vcp->diskSize);
2014 if (vcp->magic != vnode->vnodeMagic) {
2015 /* bad magic #, probably partially created vnode */
2016 Log("Partially allocated vnode %d deleted.\n",
2018 memset(vnode, 0, vcp->diskSize);
2022 /* ****** Should do a bit more salvage here: e.g. make sure
2023 * vnode type matches what it should be given the index */
2024 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2025 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2026 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2027 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2034 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2035 /* The following doesn't work, because the version number
2036 * is not maintained correctly by the file server */
2037 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2038 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2040 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2046 /* For RW volume, look for vnode with matching inode number;
2047 * if no such match, take the first determined by our sort
2049 register struct ViceInodeInfo *lip = ip;
2050 register int lnInodes = nInodes;
2052 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2053 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2062 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2063 /* "Matching" inode */
2067 vu = vnode->uniquifier;
2068 iu = ip->u.vnode.vnodeUniquifier;
2069 vd = vnode->dataVersion;
2070 id = ip->u.vnode.inodeDataVersion;
2072 * Because of the possibility of the uniquifier overflows (> 4M)
2073 * we compare them modulo the low 22-bits; we shouldn't worry
2074 * about mismatching since they shouldn't to many old
2075 * uniquifiers of the same vnode...
2077 if (IUnique(vu) != IUnique(iu)) {
2079 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2082 vnode->uniquifier = iu;
2083 #ifdef AFS_3DISPARES
2084 vnode->dataVersion = (id >= vd ?
2087 1887437 ? vd : id) :
2090 1887437 ? id : vd));
2092 #if defined(AFS_SGI_EXMAG)
2093 vnode->dataVersion = (id >= vd ?
2096 15099494 ? vd : id) :
2099 15099494 ? id : vd));
2101 vnode->dataVersion = (id > vd ? id : vd);
2102 #endif /* AFS_SGI_EXMAG */
2103 #endif /* AFS_3DISPARES */
2106 /* don't bother checking for vd > id any more, since
2107 * partial file transfers always result in this state,
2108 * and you can't do much else anyway (you've already
2109 * found the best data you can) */
2110 #ifdef AFS_3DISPARES
2111 if (!vnodeIsDirectory(vnodeNumber)
2112 && ((vd < id && (id - vd) < 1887437)
2113 || ((vd > id && (vd - id) > 1887437)))) {
2115 #if defined(AFS_SGI_EXMAG)
2116 if (!vnodeIsDirectory(vnodeNumber)
2117 && ((vd < id && (id - vd) < 15099494)
2118 || ((vd > id && (vd - id) > 15099494)))) {
2120 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2121 #endif /* AFS_SGI_EXMAG */
2124 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2125 vnode->dataVersion = id;
2130 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2133 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);
2135 VNDISK_SET_INO(vnode, ip->inodeNumber);
2140 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);
2142 VNDISK_SET_INO(vnode, ip->inodeNumber);
2145 VNDISK_GET_LEN(vnodeLength, vnode);
2146 if (ip->byteCount != vnodeLength) {
2149 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2154 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2155 VNDISK_SET_LEN(vnode, ip->byteCount);
2159 ip->linkCount--; /* Keep the inode around */
2162 } else { /* no matching inode */
2163 if (VNDISK_GET_INO(vnode) != 0
2164 || vnode->type == vDirectory) {
2165 /* No matching inode--get rid of the vnode */
2167 if (VNDISK_GET_INO(vnode)) {
2169 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2173 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2178 if (VNDISK_GET_INO(vnode)) {
2180 time_t serverModifyTime = vnode->serverModifyTime;
2181 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));
2185 time_t serverModifyTime = vnode->serverModifyTime;
2186 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2189 memset(vnode, 0, vcp->diskSize);
2192 /* Should not reach here becuase we checked for
2193 * (inodeNumber == 0) above. And where we zero the vnode,
2194 * we also goto vnodeDone.
2198 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2202 } /* VNDISK_GET_INO(vnode) != 0 */
2204 assert(!(vnodeChanged && check));
2205 if (vnodeChanged && !Testing) {
2207 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2208 (char *)vnode, vcp->diskSize)
2210 VolumeChanged = 1; /* For break call back */
2221 struct VnodeEssence *
2222 CheckVnodeNumber(VnodeId vnodeNumber)
2225 struct VnodeInfo *vip;
2228 class = vnodeIdToClass(vnodeNumber);
2229 vip = &vnodeInfo[class];
2230 offset = vnodeIdToBitNumber(vnodeNumber);
2231 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2235 CopyOnWrite(register struct DirSummary *dir)
2237 /* Copy the directory unconditionally if we are going to change it:
2238 * not just if was cloned.
2240 struct VnodeDiskObject vnode;
2241 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2242 Inode oldinode, newinode;
2245 if (dir->copied || Testing)
2247 DFlush(); /* Well justified paranoia... */
2250 IH_IREAD(vnodeInfo[vLarge].handle,
2251 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2253 assert(code == sizeof(vnode));
2254 oldinode = VNDISK_GET_INO(&vnode);
2255 /* Increment the version number by a whole lot to avoid problems with
2256 * clients that were promised new version numbers--but the file server
2257 * crashed before the versions were written to disk.
2260 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2261 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2263 assert(VALID_INO(newinode));
2264 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2266 VNDISK_SET_INO(&vnode, newinode);
2268 IH_IWRITE(vnodeInfo[vLarge].handle,
2269 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2271 assert(code == sizeof(vnode));
2273 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2274 fileSysDevice, newinode);
2275 /* Don't delete the original inode right away, because the directory is
2276 * still being scanned.
2282 * This function should either successfully create a new dir, or give up
2283 * and leave things the way they were. In particular, if it fails to write
2284 * the new dir properly, it should return w/o changing the reference to the
2288 CopyAndSalvage(register struct DirSummary *dir)
2290 struct VnodeDiskObject vnode;
2291 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2292 Inode oldinode, newinode;
2296 afs_int32 parentUnique = 1;
2297 struct VnodeEssence *vnodeEssence;
2301 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2303 IH_IREAD(vnodeInfo[vLarge].handle,
2304 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2306 assert(lcode == sizeof(vnode));
2307 oldinode = VNDISK_GET_INO(&vnode);
2308 /* Increment the version number by a whole lot to avoid problems with
2309 * clients that were promised new version numbers--but the file server
2310 * crashed before the versions were written to disk.
2313 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2314 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2316 assert(VALID_INO(newinode));
2317 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2319 /* Assign . and .. vnode numbers from dir and vnode.parent.
2320 * The uniquifier for . is in the vnode.
2321 * The uniquifier for .. might be set to a bogus value of 1 and
2322 * the salvager will later clean it up.
2324 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2325 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2328 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2330 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2335 /* didn't really build the new directory properly, let's just give up. */
2336 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2337 Log("Directory salvage returned code %d, continuing.\n", code);
2339 Log("also failed to decrement link count on new inode");
2343 Log("Checking the results of the directory salvage...\n");
2344 if (!DirOK(&newdir)) {
2345 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2346 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2351 VNDISK_SET_INO(&vnode, newinode);
2352 VNDISK_SET_LEN(&vnode, Length(&newdir));
2354 IH_IWRITE(vnodeInfo[vLarge].handle,
2355 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2357 assert(lcode == sizeof(vnode));
2359 nt_sync(fileSysDevice);
2361 sync(); /* this is slow, but hopefully rarely called. We don't have
2362 * an open FD on the file itself to fsync.
2365 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2367 dir->dirHandle = newdir;
2371 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2374 struct VnodeEssence *vnodeEssence;
2375 afs_int32 dirOrphaned, todelete;
2377 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2379 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2380 if (vnodeEssence == NULL) {
2382 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2386 assert(Delete(&dir->dirHandle, name) == 0);
2391 #ifndef AFS_NAMEI_ENV
2392 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2393 * mount inode for the partition. If this inode were deleted, it would crash
2396 if (vnodeEssence->InodeNumber == 0) {
2397 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"));
2400 assert(Delete(&dir->dirHandle, name) == 0);
2407 if (!(vnodeNumber & 1) && !Showmode
2408 && !(vnodeEssence->count || vnodeEssence->unique
2409 || vnodeEssence->modeBits)) {
2410 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2411 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2412 vnodeNumber, unique,
2413 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2418 assert(Delete(&dir->dirHandle, name) == 0);
2424 /* Check if the Uniquifiers match. If not, change the directory entry
2425 * so its unique matches the vnode unique. Delete if the unique is zero
2426 * or if the directory is orphaned.
2428 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2429 if (!vnodeEssence->unique
2430 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2431 /* This is an orphaned directory. Don't delete the . or ..
2432 * entry. Otherwise, it will get created in the next
2433 * salvage and deleted again here. So Just skip it.
2438 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2441 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")));
2445 fid.Vnode = vnodeNumber;
2446 fid.Unique = vnodeEssence->unique;
2448 assert(Delete(&dir->dirHandle, name) == 0);
2450 assert(Create(&dir->dirHandle, name, &fid) == 0);
2453 return; /* no need to continue */
2456 if (strcmp(name, ".") == 0) {
2457 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2460 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2463 assert(Delete(&dir->dirHandle, ".") == 0);
2464 fid.Vnode = dir->vnodeNumber;
2465 fid.Unique = dir->unique;
2466 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2469 vnodeNumber = fid.Vnode; /* Get the new Essence */
2470 unique = fid.Unique;
2471 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2474 } else if (strcmp(name, "..") == 0) {
2477 struct VnodeEssence *dotdot;
2478 pa.Vnode = dir->parent;
2479 dotdot = CheckVnodeNumber(pa.Vnode);
2480 assert(dotdot != NULL); /* XXX Should not be assert */
2481 pa.Unique = dotdot->unique;
2483 pa.Vnode = dir->vnodeNumber;
2484 pa.Unique = dir->unique;
2486 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2488 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2491 assert(Delete(&dir->dirHandle, "..") == 0);
2492 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2495 vnodeNumber = pa.Vnode; /* Get the new Essence */
2497 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2499 dir->haveDotDot = 1;
2500 } else if (strncmp(name, ".__afs", 6) == 0) {
2502 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);
2506 assert(Delete(&dir->dirHandle, name) == 0);
2508 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2509 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2512 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2513 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);
2514 if (ShowMounts && (vnodeEssence->type == vSymlink)
2515 && !(vnodeEssence->modeBits & 0111)) {
2521 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2522 vnodeEssence->InodeNumber);
2524 assert(fdP != NULL);
2525 size = FDH_SIZE(fdP);
2527 memset(buf, 0, 1024);
2530 code = FDH_READ(fdP, buf, size);
2531 assert(code == size);
2532 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2533 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2534 dir->name ? dir->name : "??", name, buf);
2535 FDH_REALLYCLOSE(fdP);
2538 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2539 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);
2540 if (vnodeIdToClass(vnodeNumber) == vLarge
2541 && vnodeEssence->name == NULL) {
2543 if ((n = (char *)malloc(strlen(name) + 1)))
2545 vnodeEssence->name = n;
2548 /* The directory entry points to the vnode. Check to see if the
2549 * vnode points back to the directory. If not, then let the
2550 * directory claim it (else it might end up orphaned). Vnodes
2551 * already claimed by another directory are deleted from this
2552 * directory: hardlinks to the same vnode are not allowed
2553 * from different directories.
2555 if (vnodeEssence->parent != dir->vnodeNumber) {
2556 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2557 /* Vnode does not point back to this directory.
2558 * Orphaned dirs cannot claim a file (it may belong to
2559 * another non-orphaned dir).
2562 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);
2564 vnodeEssence->parent = dir->vnodeNumber;
2565 vnodeEssence->changed = 1;
2567 /* Vnode was claimed by another directory */
2570 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 " : ""));
2571 } else if (vnodeNumber == 1) {
2572 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 " : ""));
2574 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 " : ""));
2579 assert(Delete(&dir->dirHandle, name) == 0);
2584 /* This directory claims the vnode */
2585 vnodeEssence->claimed = 1;
2587 vnodeEssence->count--;
2591 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2593 register struct VnodeInfo *vip = &vnodeInfo[class];
2594 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2595 char buf[SIZEOF_LARGEDISKVNODE];
2596 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2598 StreamHandle_t *file;
2603 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2604 fdP = IH_OPEN(vip->handle);
2605 assert(fdP != NULL);
2606 file = FDH_FDOPEN(fdP, "r+");
2607 assert(file != NULL);
2608 size = OS_SIZE(fdP->fd_fd);
2610 vip->nVnodes = (size / vcp->diskSize) - 1;
2611 if (vip->nVnodes > 0) {
2612 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2613 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2614 assert((vip->vnodes = (struct VnodeEssence *)
2615 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2616 if (class == vLarge) {
2617 assert((vip->inodes = (Inode *)
2618 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2627 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2628 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2629 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2630 nVnodes--, vnodeIndex++) {
2631 if (vnode->type != vNull) {
2632 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2633 afs_fsize_t vnodeLength;
2634 vip->nAllocatedVnodes++;
2635 vep->count = vnode->linkCount;
2636 VNDISK_GET_LEN(vnodeLength, vnode);
2637 vep->blockCount = nBlocks(vnodeLength);
2638 vip->volumeBlockCount += vep->blockCount;
2639 vep->parent = vnode->parent;
2640 vep->unique = vnode->uniquifier;
2641 if (*maxu < vnode->uniquifier)
2642 *maxu = vnode->uniquifier;
2643 vep->modeBits = vnode->modeBits;
2644 vep->InodeNumber = VNDISK_GET_INO(vnode);
2645 vep->type = vnode->type;
2646 vep->author = vnode->author;
2647 vep->owner = vnode->owner;
2648 vep->group = vnode->group;
2649 if (vnode->type == vDirectory) {
2650 assert(class == vLarge);
2651 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2660 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
2662 struct VnodeEssence *parentvp;
2668 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
2669 && GetDirName(vp->parent, parentvp, path)) {
2671 strcat(path, vp->name);
2677 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2678 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2681 IsVnodeOrphaned(VnodeId vnode)
2683 struct VnodeEssence *vep;
2686 return (1); /* Vnode zero does not exist */
2688 return (0); /* The root dir vnode is always claimed */
2689 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2690 if (!vep || !vep->claimed)
2691 return (1); /* Vnode is not claimed - it is orphaned */
2693 return (IsVnodeOrphaned(vep->parent));
2697 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2698 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
2701 static struct DirSummary dir;
2702 static struct DirHandle dirHandle;
2703 struct VnodeEssence *parent;
2704 static char path[MAXPATHLEN];
2707 if (dirVnodeInfo->vnodes[i].salvaged)
2708 return; /* already salvaged */
2711 dirVnodeInfo->vnodes[i].salvaged = 1;
2713 if (dirVnodeInfo->inodes[i] == 0)
2714 return; /* Not allocated to a directory */
2716 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
2717 if (dirVnodeInfo->vnodes[i].parent) {
2718 Log("Bad parent, vnode 1; %s...\n",
2719 (Testing ? "skipping" : "salvaging"));
2720 dirVnodeInfo->vnodes[i].parent = 0;
2721 dirVnodeInfo->vnodes[i].changed = 1;
2724 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2725 if (parent && parent->salvaged == 0)
2726 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2727 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2728 rootdir, rootdirfound);
2731 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2732 dir.unique = dirVnodeInfo->vnodes[i].unique;
2735 dir.parent = dirVnodeInfo->vnodes[i].parent;
2736 dir.haveDot = dir.haveDotDot = 0;
2737 dir.ds_linkH = alinkH;
2738 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
2739 dirVnodeInfo->inodes[i]);
2741 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2744 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
2745 (Testing ? "skipping" : "salvaging"));
2748 CopyAndSalvage(&dir);
2752 dirHandle = dir.dirHandle;
2755 GetDirName(bitNumberToVnodeNumber(i, vLarge),
2756 &dirVnodeInfo->vnodes[i], path);
2759 /* If enumeration failed for random reasons, we will probably delete
2760 * too much stuff, so we guard against this instead.
2762 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
2765 /* Delete the old directory if it was copied in order to salvage.
2766 * CopyOnWrite has written the new inode # to the disk, but we still
2767 * have the old one in our local structure here. Thus, we idec the
2771 if (dir.copied && !Testing) {
2772 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
2774 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
2777 /* Remember rootdir DirSummary _after_ it has been judged */
2778 if (dir.vnodeNumber == 1 && dir.unique == 1) {
2779 memcpy(rootdir, &dir, sizeof(struct DirSummary));
2787 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
2789 /* This routine, for now, will only be called for read-write volumes */
2791 int BlocksInVolume = 0, FilesInVolume = 0;
2792 register VnodeClass class;
2793 struct DirSummary rootdir, oldrootdir;
2794 struct VnodeInfo *dirVnodeInfo;
2795 struct VnodeDiskObject vnode;
2796 VolumeDiskData volHeader;
2798 int orphaned, rootdirfound = 0;
2799 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
2800 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
2801 struct VnodeEssence *vep;
2804 afs_sfsize_t nBytes;
2806 VnodeId LFVnode, ThisVnode;
2807 Unique LFUnique, ThisUnique;
2810 vid = rwIsp->volSummary->header.id;
2811 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
2812 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
2813 assert(nBytes == sizeof(volHeader));
2814 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
2815 assert(volHeader.destroyMe != DESTROY_ME);
2816 /* (should not have gotten this far with DESTROY_ME flag still set!) */
2818 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
2820 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
2823 dirVnodeInfo = &vnodeInfo[vLarge];
2824 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
2825 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
2833 /* Parse each vnode looking for orphaned vnodes and
2834 * connect them to the tree as orphaned (if requested).
2836 oldrootdir = rootdir;
2837 for (class = 0; class < nVNODECLASSES; class++) {
2838 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
2839 vep = &(vnodeInfo[class].vnodes[v]);
2840 ThisVnode = bitNumberToVnodeNumber(v, class);
2841 ThisUnique = vep->unique;
2843 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
2844 continue; /* Ignore unused, claimed, and root vnodes */
2846 /* This vnode is orphaned. If it is a directory vnode, then the '..'
2847 * entry in this vnode had incremented the parent link count (In
2848 * JudgeEntry()). We need to go to the parent and decrement that
2849 * link count. But if the parent's unique is zero, then the parent
2850 * link count was not incremented in JudgeEntry().
2852 if (class == vLarge) { /* directory vnode */
2853 pv = vnodeIdToBitNumber(vep->parent);
2854 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
2855 vnodeInfo[vLarge].vnodes[pv].count++;
2859 continue; /* If no rootdir, can't attach orphaned files */
2861 /* Here we attach orphaned files and directories into the
2862 * root directory, LVVnode, making sure link counts stay correct.
2864 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
2865 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
2866 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
2868 /* Update this orphaned vnode's info. Its parent info and
2869 * link count (do for orphaned directories and files).
2871 vep->parent = LFVnode; /* Parent is the root dir */
2872 vep->unique = LFUnique;
2875 vep->count--; /* Inc link count (root dir will pt to it) */
2877 /* If this orphaned vnode is a directory, change '..'.
2878 * The name of the orphaned dir/file is unknown, so we
2879 * build a unique name. No need to CopyOnWrite the directory
2880 * since it is not connected to tree in BK or RO volume and
2881 * won't be visible there.
2883 if (class == vLarge) {
2887 /* Remove and recreate the ".." entry in this orphaned directory */
2888 SetSalvageDirHandle(&dh, vid, fileSysDevice,
2889 vnodeInfo[class].inodes[v]);
2891 pa.Unique = LFUnique;
2892 assert(Delete(&dh, "..") == 0);
2893 assert(Create(&dh, "..", &pa) == 0);
2895 /* The original parent's link count was decremented above.
2896 * Here we increment the new parent's link count.
2898 pv = vnodeIdToBitNumber(LFVnode);
2899 vnodeInfo[vLarge].vnodes[pv].count--;
2903 /* Go to the root dir and add this entry. The link count of the
2904 * root dir was incremented when ".." was created. Try 10 times.
2906 for (j = 0; j < 10; j++) {
2907 pa.Vnode = ThisVnode;
2908 pa.Unique = ThisUnique;
2910 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
2912 vLarge) ? "__ORPHANDIR__" :
2913 "__ORPHANFILE__"), ThisVnode,
2916 CopyOnWrite(&rootdir);
2917 code = Create(&rootdir.dirHandle, npath, &pa);
2921 ThisUnique += 50; /* Try creating a different file */
2924 Log("Attaching orphaned %s to volume's root dir as %s\n",
2925 ((class == vLarge) ? "directory" : "file"), npath);
2927 } /* for each vnode in the class */
2928 } /* for each class of vnode */
2930 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
2932 if (!oldrootdir.copied && rootdir.copied) {
2934 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
2937 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
2940 DFlush(); /* Flush the changes */
2941 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
2942 Log("Cannot attach orphaned files and directories: Root directory not found\n");
2943 orphans = ORPH_IGNORE;
2946 /* Write out all changed vnodes. Orphaned files and directories
2947 * will get removed here also (if requested).
2949 for (class = 0; class < nVNODECLASSES; class++) {
2950 int nVnodes = vnodeInfo[class].nVnodes;
2951 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2952 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
2953 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
2954 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
2955 for (i = 0; i < nVnodes; i++) {
2956 register struct VnodeEssence *vnp = &vnodes[i];
2957 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
2959 /* If the vnode is good but is unclaimed (not listed in
2960 * any directory entries), then it is orphaned.
2963 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
2964 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
2968 if (vnp->changed || vnp->count) {
2972 IH_IREAD(vnodeInfo[class].handle,
2973 vnodeIndexOffset(vcp, vnodeNumber),
2974 (char *)&vnode, sizeof(vnode));
2975 assert(nBytes == sizeof(vnode));
2977 vnode.parent = vnp->parent;
2978 oldCount = vnode.linkCount;
2979 vnode.linkCount = vnode.linkCount - vnp->count;
2982 orphaned = IsVnodeOrphaned(vnodeNumber);
2984 if (!vnp->todelete) {
2985 /* Orphans should have already been attached (if requested) */
2986 assert(orphans != ORPH_ATTACH);
2987 oblocks += vnp->blockCount;
2990 if (((orphans == ORPH_REMOVE) || vnp->todelete)
2992 BlocksInVolume -= vnp->blockCount;
2994 if (VNDISK_GET_INO(&vnode)) {
2996 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
2999 memset(&vnode, 0, sizeof(vnode));
3001 } else if (vnp->count) {
3003 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3007 vnode.dataVersion++;
3010 IH_IWRITE(vnodeInfo[class].handle,
3011 vnodeIndexOffset(vcp, vnodeNumber),
3012 (char *)&vnode, sizeof(vnode));
3013 assert(nBytes == sizeof(vnode));
3019 if (!Showmode && ofiles) {
3020 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3022 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3026 for (class = 0; class < nVNODECLASSES; class++) {
3027 register struct VnodeInfo *vip = &vnodeInfo[class];
3028 for (i = 0; i < vip->nVnodes; i++)
3029 if (vip->vnodes[i].name)
3030 free(vip->vnodes[i].name);
3037 /* Set correct resource utilization statistics */
3038 volHeader.filecount = FilesInVolume;
3039 volHeader.diskused = BlocksInVolume;
3041 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3042 if (volHeader.uniquifier < (maxunique + 1)) {
3044 Log("Volume uniquifier is too low; fixed\n");
3045 /* Plus 2,000 in case there are workstations out there with
3046 * cached vnodes that have since been deleted
3048 volHeader.uniquifier = (maxunique + 1 + 2000);
3051 /* Turn off the inUse bit; the volume's been salvaged! */
3052 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3053 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3054 volHeader.inService = 1; /* allow service again */
3055 volHeader.needsCallback = (VolumeChanged != 0);
3056 volHeader.dontSalvage = DONT_SALVAGE;
3059 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3060 assert(nBytes == sizeof(volHeader));
3063 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3064 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3065 FilesInVolume, BlocksInVolume);
3067 IH_RELEASE(vnodeInfo[vSmall].handle);
3068 IH_RELEASE(vnodeInfo[vLarge].handle);
3074 ClearROInUseBit(struct VolumeSummary *summary)
3076 IHandle_t *h = summary->volumeInfoHandle;
3077 afs_sfsize_t nBytes;
3079 VolumeDiskData volHeader;
3081 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3082 assert(nBytes == sizeof(volHeader));
3083 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3084 volHeader.inUse = 0;
3085 volHeader.needsSalvaged = 0;
3086 volHeader.inService = 1;
3087 volHeader.dontSalvage = DONT_SALVAGE;
3089 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3090 assert(nBytes == sizeof(volHeader));
3095 * Possible delete the volume.
3097 * deleteMe - Always do so, only a partial volume.
3100 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3103 if (readOnly(isp) || deleteMe) {
3104 if (isp->volSummary && isp->volSummary->fileName) {
3107 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);
3109 Log("It will be deleted on this server (you may find it elsewhere)\n");
3112 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3114 Log("it will be deleted instead. It should be recloned.\n");
3117 unlink(isp->volSummary->fileName);
3119 } else if (!check) {
3120 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3122 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3128 AskOffline(VolumeId volumeId)
3132 for (i = 0; i < 3; i++) {
3133 code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
3135 if (code == SYNC_OK) {
3137 } else if (code == SYNC_DENIED) {
3138 #ifdef DEMAND_ATTACH_ENABLE
3139 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3141 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3143 Abort("Salvage aborted\n");
3144 } else if (code == SYNC_BAD_COMMAND) {
3145 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3147 #ifdef DEMAND_ATTACH_ENABLE
3148 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3150 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3152 Abort("Salvage aborted\n");
3155 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3156 FSYNC_clientFinis();
3160 if (code != SYNC_OK) {
3161 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3162 Abort("Salvage aborted\n");
3167 AskOnline(VolumeId volumeId, char *partition)
3171 for (i = 0; i < 3; i++) {
3172 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3174 if (code == SYNC_OK) {
3176 } else if (code == SYNC_DENIED) {
3177 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3178 } else if (code == SYNC_BAD_COMMAND) {
3179 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3181 #ifdef DEMAND_ATTACH_ENABLE
3182 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3184 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3189 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3190 FSYNC_clientFinis();
3197 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3199 /* Volume parameter is passed in case iopen is upgraded in future to
3200 * require a volume Id to be passed
3203 IHandle_t *srcH, *destH;
3204 FdHandle_t *srcFdP, *destFdP;
3207 IH_INIT(srcH, device, rwvolume, inode1);
3208 srcFdP = IH_OPEN(srcH);
3209 assert(srcFdP != NULL);
3210 IH_INIT(destH, device, rwvolume, inode2);
3211 destFdP = IH_OPEN(destH);
3213 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3214 assert(FDH_WRITE(destFdP, buf, n) == n);
3216 FDH_REALLYCLOSE(srcFdP);
3217 FDH_REALLYCLOSE(destFdP);
3224 PrintInodeList(void)
3226 register struct ViceInodeInfo *ip;
3227 struct ViceInodeInfo *buf;
3228 struct afs_stat status;
3231 assert(afs_fstat(inodeFd, &status) == 0);
3232 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3233 assert(buf != NULL);
3234 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3235 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3236 for (ip = buf; nInodes--; ip++) {
3237 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3238 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3239 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3240 ip->u.param[2], ip->u.param[3]);
3246 PrintInodeSummary(void)
3249 struct InodeSummary *isp;
3251 for (i = 0; i < nVolumesInInodeFile; i++) {
3252 isp = &inodeSummary[i];
3253 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);
3258 PrintVolumeSummary(void)
3261 struct VolumeSummary *vsp;
3263 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3264 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3274 assert(0); /* Fork is never executed in the NT code path */
3289 if (main_thread != pthread_self())
3290 pthread_exit((void *)code);
3303 pid = wait(&status);
3305 if (WCOREDUMP(status))
3306 Log("\"%s\" core dumped!\n", prog);
3307 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3313 TimeStamp(time_t clock, int precision)
3316 static char timestamp[20];
3317 lt = localtime(&clock);
3319 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3321 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3326 CheckLogFile(char * log_path)
3328 char oldSlvgLog[AFSDIR_PATH_MAX];
3330 #ifndef AFS_NT40_ENV
3337 strcpy(oldSlvgLog, log_path);
3338 strcat(oldSlvgLog, ".old");
3340 renamefile(log_path, oldSlvgLog);
3341 logFile = afs_fopen(log_path, "a");
3343 if (!logFile) { /* still nothing, use stdout */
3347 #ifndef AFS_NAMEI_ENV
3348 AFS_DEBUG_IOPS_LOG(logFile);
3353 #ifndef AFS_NT40_ENV
3355 TimeStampLogFile(char * log_path)
3357 char stampSlvgLog[AFSDIR_PATH_MAX];
3362 lt = localtime(&now);
3363 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3364 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3365 log_path, lt->tm_year + 1900,
3366 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3369 /* try to link the logfile to a timestamped filename */
3370 /* if it fails, oh well, nothing we can do */
3371 link(log_path, stampSlvgLog);
3380 #ifndef AFS_NT40_ENV
3382 printf("Can't show log since using syslog.\n");
3391 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3394 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3397 while (fgets(line, sizeof(line), logFile))
3404 Log(const char *format, ...)
3410 va_start(args, format);
3411 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3413 #ifndef AFS_NT40_ENV
3415 syslog(LOG_INFO, "%s", tmp);
3419 gettimeofday(&now, 0);
3420 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3426 Abort(const char *format, ...)
3431 va_start(args, format);
3432 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3434 #ifndef AFS_NT40_ENV
3436 syslog(LOG_INFO, "%s", tmp);
3440 fprintf(logFile, "%s", tmp);
3455 p = (char *)malloc(strlen(s) + 1);
3462 /* Remove the FORCESALVAGE file */
3464 RemoveTheForce(char *path)
3466 if (!Testing && ForceSalvage) {
3467 if (chdir(path) == 0)
3468 unlink("FORCESALVAGE");
3472 #ifndef AFS_AIX32_ENV
3474 * UseTheForceLuke - see if we can use the force
3477 UseTheForceLuke(char *path)
3479 struct afs_stat force;
3481 assert(chdir(path) != -1);
3483 return (afs_stat("FORCESALVAGE", &force) == 0);
3487 * UseTheForceLuke - see if we can use the force
3490 * The VRMIX fsck will not muck with the filesystem it is supposedly
3491 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
3492 * muck directly with the root inode, which is within the normal
3494 * ListViceInodes() has a side effect of setting ForceSalvage if
3495 * it detects a need, based on root inode examination.
3498 UseTheForceLuke(char *path)
3501 return 0; /* sorry OB1 */
3506 /* NT support routines */
3508 static char execpathname[MAX_PATH];
3510 nt_SalvagePartition(char *partName, int jobn)
3515 if (!*execpathname) {
3516 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3517 if (!n || n == 1023)
3520 job.cj_magic = SALVAGER_MAGIC;
3521 job.cj_number = jobn;
3522 (void)strcpy(job.cj_part, partName);
3523 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3528 nt_SetupPartitionSalvage(void *datap, int len)
3530 childJob_t *jobp = (childJob_t *) datap;
3531 char logname[AFSDIR_PATH_MAX];
3533 if (len != sizeof(childJob_t))
3535 if (jobp->cj_magic != SALVAGER_MAGIC)
3540 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3542 logFile = afs_fopen(logname, "w");
3550 #endif /* AFS_NT40_ENV */