2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #include <afsconfig.h>
87 #include <afs/param.h>
91 #include <sys/param.h>
95 #endif /* ITIMER_REAL */
101 #include <sys/stat.h>
106 #include <WINNT/afsevent.h>
109 #define WCOREDUMP(x) ((x) & 0200)
112 #include <afs/afsint.h>
113 #include <afs/assert.h>
114 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
115 #if defined(AFS_VFSINCL_ENV)
116 #include <sys/vnode.h>
118 #include <sys/fs/ufs_inode.h>
120 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
121 #include <ufs/ufs/dinode.h>
122 #include <ufs/ffs/fs.h>
124 #include <ufs/inode.h>
127 #else /* AFS_VFSINCL_ENV */
129 #include <ufs/inode.h>
130 #else /* AFS_OSF_ENV */
131 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
132 #include <sys/inode.h>
135 #endif /* AFS_VFSINCL_ENV */
136 #endif /* AFS_SGI_ENV */
139 #include <sys/lockf.h>
143 #include <checklist.h>
145 #if defined(AFS_SGI_ENV)
150 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
153 #include <sys/mnttab.h>
154 #include <sys/mntent.h>
159 #endif /* AFS_SGI_ENV */
160 #endif /* AFS_HPUX_ENV */
165 #include <afs/osi_inode.h>
169 #include <afs/afsutil.h>
170 #include <afs/fileutil.h>
171 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
179 #include <afs/afssyscalls.h>
183 #include "partition.h"
184 #include "daemon_com.h"
186 #include "salvsync.h"
187 #include "viceinode.h"
189 #include "volinodes.h" /* header magic number, etc. stuff */
190 #include "vol-salvage.h"
191 #include "vol_internal.h"
193 #ifdef FSSYNC_BUILD_CLIENT
194 #include "vg_cache.h"
201 /*@+fcnmacros +macrofcndecl@*/
204 extern off64_t afs_lseek(int FD, off64_t O, int F);
205 #endif /*S_SPLINT_S */
206 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
207 #define afs_stat stat64
208 #define afs_fstat fstat64
209 #define afs_open open64
210 #define afs_fopen fopen64
211 #else /* !O_LARGEFILE */
213 extern off_t afs_lseek(int FD, off_t O, int F);
214 #endif /*S_SPLINT_S */
215 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
216 #define afs_stat stat
217 #define afs_fstat fstat
218 #define afs_open open
219 #define afs_fopen fopen
220 #endif /* !O_LARGEFILE */
221 /*@=fcnmacros =macrofcndecl@*/
224 extern void *calloc();
226 static char *TimeStamp(time_t clock, int precision);
229 int debug; /* -d flag */
230 extern int Testing; /* -n flag */
231 int ListInodeOption; /* -i flag */
232 int ShowRootFiles; /* -r flag */
233 int RebuildDirs; /* -sal flag */
234 int Parallel = 4; /* -para X flag */
235 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
236 int forceR = 0; /* -b flag */
237 int ShowLog = 0; /* -showlog flag */
238 int ShowSuid = 0; /* -showsuid flag */
239 int ShowMounts = 0; /* -showmounts flag */
240 int orphans = ORPH_IGNORE; /* -orphans option */
245 int useSyslog = 0; /* -syslog flag */
246 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
255 #define MAXPARALLEL 32
257 int OKToZap; /* -o flag */
258 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
259 * in the volume header */
261 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
263 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
265 Device fileSysDevice; /* The device number of the current
266 * partition being salvaged */
270 char *fileSysPath; /* The path of the mounted partition currently
271 * being salvaged, i.e. the directory
272 * containing the volume headers */
274 char *fileSysPathName; /* NT needs this to make name pretty in log. */
275 IHandle_t *VGLinkH; /* Link handle for current volume group. */
276 int VGLinkH_cnt; /* # of references to lnk handle. */
277 struct DiskPartition64 *fileSysPartition; /* Partition being salvaged */
279 char *fileSysDeviceName; /* The block device where the file system
280 * being salvaged was mounted */
281 char *filesysfulldev;
283 int VolumeChanged; /* Set by any routine which would change the volume in
284 * a way which would require callback is to be broken if the
285 * volume was put back on line by an active file server */
287 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
289 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
290 int inodeFd; /* File descriptor for inode file */
293 struct VnodeInfo vnodeInfo[nVNODECLASSES];
296 struct VolumeSummary *volumeSummaryp = NULL; /* Holds all the volumes in a part */
297 int nVolumes; /* Number of volumes (read-write and read-only)
298 * in volume summary */
304 /* Forward declarations */
305 /*@printflike@*/ void Log(const char *format, ...);
306 /*@printflike@*/ void Abort(const char *format, ...);
307 static int IsVnodeOrphaned(VnodeId vnode);
308 static int AskVolumeSummary(VolumeId singleVolumeNumber);
310 /* Uniquifier stored in the Inode */
315 return (u & 0x3fffff);
317 #if defined(AFS_SGI_EXMAG)
318 return (u & SGI_UNIQMASK);
321 #endif /* AFS_SGI_EXMAG */
326 BadError(register int aerror)
328 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
330 return 0; /* otherwise may be transient, e.g. EMFILE */
335 char *save_args[MAX_ARGS];
337 extern pthread_t main_thread;
338 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
341 /* Get the salvage lock if not already held. Hold until process exits. */
343 ObtainSalvageLock(void)
349 (FD_t)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
350 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
351 if (salvageLock == INVALID_FD) {
353 "salvager: There appears to be another salvager running! Aborted.\n");
358 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
359 if (salvageLock < 0) {
361 "salvager: can't open salvage lock file %s, aborting\n",
362 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
365 #ifdef AFS_DARWIN_ENV
366 if (flock(salvageLock, LOCK_EX) == -1) {
368 if (lockf(salvageLock, F_LOCK, 0) == -1) {
371 "salvager: There appears to be another salvager running! Aborted.\n");
378 #ifdef AFS_SGI_XFS_IOPS_ENV
379 /* Check if the given partition is mounted. For XFS, the root inode is not a
380 * constant. So we check the hard way.
383 IsPartitionMounted(char *part)
386 struct mntent *mntent;
388 assert(mntfp = setmntent(MOUNTED, "r"));
389 while (mntent = getmntent(mntfp)) {
390 if (!strcmp(part, mntent->mnt_dir))
395 return mntent ? 1 : 1;
398 /* Check if the given inode is the root of the filesystem. */
399 #ifndef AFS_SGI_XFS_IOPS_ENV
401 IsRootInode(struct afs_stat *status)
404 * The root inode is not a fixed value in XFS partitions. So we need to
405 * see if the partition is in the list of mounted partitions. This only
406 * affects the SalvageFileSys path, so we check there.
408 return (status->st_ino == ROOTINODE);
413 #ifndef AFS_NAMEI_ENV
414 /* We don't want to salvage big files filesystems, since we can't put volumes on
418 CheckIfBigFilesFS(char *mountPoint, char *devName)
420 struct superblock fs;
423 if (strncmp(devName, "/dev/", 5)) {
424 (void)sprintf(name, "/dev/%s", devName);
426 (void)strcpy(name, devName);
429 if (ReadSuper(&fs, name) < 0) {
430 Log("Unable to read superblock. Not salvaging partition %s.\n",
434 if (IsBigFilesFileSystem(&fs)) {
435 Log("Partition %s is a big files filesystem, not salvaging.\n",
445 #define HDSTR "\\Device\\Harddisk"
446 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
448 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
453 static int dowarn = 1;
455 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
457 if (strncmp(res, HDSTR, HDLEN)) {
460 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
461 res, HDSTR, p1->devName);
465 d1 = atoi(&res[HDLEN]);
467 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
469 if (strncmp(res, HDSTR, HDLEN)) {
472 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
473 res, HDSTR, p2->devName);
477 d2 = atoi(&res[HDLEN]);
482 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
485 /* This assumes that two partitions with the same device number divided by
486 * PartsPerDisk are on the same disk.
489 SalvageFileSysParallel(struct DiskPartition64 *partP)
492 struct DiskPartition64 *partP;
493 int pid; /* Pid for this job */
494 int jobnumb; /* Log file job number */
495 struct job *nextjob; /* Next partition on disk to salvage */
497 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
498 struct job *thisjob = 0;
499 static int numjobs = 0;
500 static int jobcount = 0;
506 char logFileName[256];
510 /* We have a partition to salvage. Copy it into thisjob */
511 thisjob = (struct job *)malloc(sizeof(struct job));
513 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
516 memset(thisjob, 0, sizeof(struct job));
517 thisjob->partP = partP;
518 thisjob->jobnumb = jobcount;
520 } else if (jobcount == 0) {
521 /* We are asking to wait for all jobs (partp == 0), yet we never
524 Log("No file system partitions named %s* found; not salvaged\n",
525 VICE_PARTITION_PREFIX);
529 if (debug || Parallel == 1) {
531 SalvageFileSys(thisjob->partP, 0);
538 /* Check to see if thisjob is for a disk that we are already
539 * salvaging. If it is, link it in as the next job to do. The
540 * jobs array has 1 entry per disk being salvages. numjobs is
541 * the total number of disks currently being salvaged. In
542 * order to keep thejobs array compact, when a disk is
543 * completed, the hightest element in the jobs array is moved
544 * down to now open slot.
546 for (j = 0; j < numjobs; j++) {
547 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
548 /* On same disk, add it to this list and return */
549 thisjob->nextjob = jobs[j]->nextjob;
550 jobs[j]->nextjob = thisjob;
557 /* Loop until we start thisjob or until all existing jobs are finished */
558 while (thisjob || (!partP && (numjobs > 0))) {
559 startjob = -1; /* No new job to start */
561 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
562 /* Either the max jobs are running or we have to wait for all
563 * the jobs to finish. In either case, we wait for at least one
564 * job to finish. When it's done, clean up after it.
566 pid = wait(&wstatus);
568 for (j = 0; j < numjobs; j++) { /* Find which job it is */
569 if (pid == jobs[j]->pid)
573 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
574 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
577 numjobs--; /* job no longer running */
578 oldjob = jobs[j]; /* remember */
579 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
580 free(oldjob); /* free the old job */
582 /* If there is another partition on the disk to salvage, then
583 * say we will start it (startjob). If not, then put thisjob there
584 * and say we will start it.
586 if (jobs[j]) { /* Another partitions to salvage */
587 startjob = j; /* Will start it */
588 } else { /* There is not another partition to salvage */
590 jobs[j] = thisjob; /* Add thisjob */
592 startjob = j; /* Will start it */
594 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
595 startjob = -1; /* Don't start it - already running */
599 /* We don't have to wait for a job to complete */
601 jobs[numjobs] = thisjob; /* Add this job */
603 startjob = numjobs; /* Will start it */
607 /* Start up a new salvage job on a partition in job slot "startjob" */
608 if (startjob != -1) {
610 Log("Starting salvage of file system partition %s\n",
611 jobs[startjob]->partP->name);
613 /* For NT, we not only fork, but re-exec the salvager. Pass in the
614 * commands and pass the child job number via the data path.
617 nt_SalvagePartition(jobs[startjob]->partP->name,
618 jobs[startjob]->jobnumb);
619 jobs[startjob]->pid = pid;
624 jobs[startjob]->pid = pid;
630 for (fd = 0; fd < 16; fd++)
637 openlog("salvager", LOG_PID, useSyslogFacility);
641 (void)afs_snprintf(logFileName, sizeof logFileName,
643 AFSDIR_SERVER_SLVGLOG_FILEPATH,
644 jobs[startjob]->jobnumb);
645 logFile = afs_fopen(logFileName, "w");
650 SalvageFileSys1(jobs[startjob]->partP, 0);
655 } /* while ( thisjob || (!partP && numjobs > 0) ) */
657 /* If waited for all jobs to complete, now collect log files and return */
659 if (!useSyslog) /* if syslogging - no need to collect */
662 for (i = 0; i < jobcount; i++) {
663 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
664 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
665 if ((passLog = afs_fopen(logFileName, "r"))) {
666 while (fgets(buf, sizeof(buf), passLog)) {
671 (void)unlink(logFileName);
680 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
682 if (!canfork || debug || Fork() == 0) {
683 SalvageFileSys1(partP, singleVolumeNumber);
684 if (canfork && !debug) {
689 Wait("SalvageFileSys");
693 get_DevName(char *pbuffer, char *wpath)
695 char pbuf[128], *ptr;
696 strcpy(pbuf, pbuffer);
697 ptr = (char *)strrchr(pbuf, '/');
703 ptr = (char *)strrchr(pbuffer, '/');
705 strcpy(pbuffer, ptr + 1);
712 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
715 char inodeListPath[256];
716 static char tmpDevName[100];
717 static char wpath[100];
718 struct VolumeSummary *vsp, *esp;
721 fileSysPartition = partP;
722 fileSysDevice = fileSysPartition->device;
723 fileSysPathName = VPartitionPath(fileSysPartition);
726 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
727 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
728 name = partP->devName;
730 fileSysPath = fileSysPathName;
731 strcpy(tmpDevName, partP->devName);
732 name = get_DevName(tmpDevName, wpath);
733 fileSysDeviceName = name;
734 filesysfulldev = wpath;
737 VLockPartition(partP->name);
738 if (singleVolumeNumber || ForceSalvage)
741 ForceSalvage = UseTheForceLuke(fileSysPath);
743 if (singleVolumeNumber) {
744 /* salvageserver already setup fssync conn for us */
745 if ((programType != salvageServer) && !VConnectFS()) {
746 Abort("Couldn't connect to file server\n");
748 AskOffline(singleVolumeNumber, partP->name);
751 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
752 partP->name, name, (Testing ? "(READONLY mode)" : ""));
754 Log("***Forced salvage of all volumes on this partition***\n");
759 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
766 assert((dirp = opendir(fileSysPath)) != NULL);
767 while ((dp = readdir(dirp))) {
768 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
769 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
771 Log("Removing old salvager temp files %s\n", dp->d_name);
772 strcpy(npath, fileSysPath);
774 strcat(npath, dp->d_name);
780 tdir = (tmpdir ? tmpdir : fileSysPath);
782 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
783 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
785 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
788 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
789 unlink(inodeListPath);
793 /* Using nt_unlink here since we're really using the delete on close
794 * semantics of unlink. In most places in the salvager, we really do
795 * mean to unlink the file at that point. Those places have been
796 * modified to actually do that so that the NT crt can be used there.
799 _open_osfhandle((intptr_t)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
800 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
802 inodeFd = afs_open(inodeListPath, O_RDONLY);
803 unlink(inodeListPath);
806 Abort("Temporary file %s is missing...\n", inodeListPath);
807 if (ListInodeOption) {
811 /* enumerate volumes in the partition.
812 * figure out sets of read-only + rw volumes.
813 * salvage each set, read-only volumes first, then read-write.
814 * Fix up inodes on last volume in set (whether it is read-write
817 GetVolumeSummary(singleVolumeNumber);
819 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
820 i < nVolumesInInodeFile; i = j) {
821 VolumeId rwvid = inodeSummary[i].RWvolumeId;
823 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
825 VolumeId vid = inodeSummary[j].volumeId;
826 struct VolumeSummary *tsp;
827 /* Scan volume list (from partition root directory) looking for the
828 * current rw volume number in the volume list from the inode scan.
829 * If there is one here that is not in the inode volume list,
831 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
833 DeleteExtraVolumeHeaderFile(vsp);
835 /* Now match up the volume summary info from the root directory with the
836 * entry in the volume list obtained from scanning inodes */
837 inodeSummary[j].volSummary = NULL;
838 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
839 if (tsp->header.id == vid) {
840 inodeSummary[j].volSummary = tsp;
846 /* Salvage the group of volumes (several read-only + 1 read/write)
847 * starting with the current read-only volume we're looking at.
849 SalvageVolumeGroup(&inodeSummary[i], j - i);
852 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
853 for (; vsp < esp; vsp++) {
855 DeleteExtraVolumeHeaderFile(vsp);
858 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
859 RemoveTheForce(fileSysPath);
861 if (!Testing && singleVolumeNumber) {
862 AskOnline(singleVolumeNumber, fileSysPartition->name);
864 /* Step through the volumeSummary list and set all volumes on-line.
865 * The volumes were taken off-line in GetVolumeSummary.
867 for (j = 0; j < nVolumes; j++) {
868 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
872 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
873 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
876 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
880 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
883 sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
886 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
889 code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
891 Log("Error %ld destroying volume disk header for volume %lu\n",
892 afs_printable_int32_ld(code),
893 afs_printable_uint32_lu(vsp->header.id));
896 /* make sure we actually delete the fileName file; ENOENT
897 * is fine, since VDestroyVolumeDiskHeader probably already
899 if (unlink(path) && errno != ENOENT) {
900 Log("Unable to unlink %s (errno = %d)\n", path, errno);
907 CompareInodes(const void *_p1, const void *_p2)
909 register const struct ViceInodeInfo *p1 = _p1;
910 register const struct ViceInodeInfo *p2 = _p2;
911 if (p1->u.vnode.vnodeNumber == INODESPECIAL
912 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
913 VolumeId p1rwid, p2rwid;
915 (p1->u.vnode.vnodeNumber ==
916 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
918 (p2->u.vnode.vnodeNumber ==
919 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
924 if (p1->u.vnode.vnodeNumber == INODESPECIAL
925 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
926 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
927 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
928 if (p1->u.vnode.volumeId == p1rwid)
930 if (p2->u.vnode.volumeId == p2rwid)
932 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
934 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
935 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
936 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
938 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
940 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
942 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
944 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
946 /* The following tests are reversed, so that the most desirable
947 * of several similar inodes comes first */
948 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
950 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
951 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
955 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
956 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
961 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
963 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
964 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
968 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
969 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
974 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
976 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
977 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
981 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
982 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
987 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
989 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
990 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
994 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
995 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1004 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1005 register struct InodeSummary *summary)
1007 VolumeId volume = ip->u.vnode.volumeId;
1008 VolumeId rwvolume = volume;
1009 register int n, nSpecial;
1010 register Unique maxunique;
1013 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1015 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1017 rwvolume = ip->u.special.parentId;
1018 /* This isn't quite right, as there could (in error) be different
1019 * parent inodes in different special vnodes */
1021 if (maxunique < ip->u.vnode.vnodeUniquifier)
1022 maxunique = ip->u.vnode.vnodeUniquifier;
1026 summary->volumeId = volume;
1027 summary->RWvolumeId = rwvolume;
1028 summary->nInodes = n;
1029 summary->nSpecialInodes = nSpecial;
1030 summary->maxUniquifier = maxunique;
1034 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1036 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1037 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1038 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1043 * Collect list of inodes in file named by path. If a truly fatal error,
1044 * unlink the file and abort. For lessor errors, return -1. The file will
1045 * be unlinked by the caller.
1048 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1050 struct afs_stat status;
1052 struct ViceInodeInfo *ip;
1053 struct InodeSummary summary;
1054 char summaryFileName[50];
1057 char *dev = fileSysPath;
1058 char *wpath = fileSysPath;
1060 char *dev = fileSysDeviceName;
1061 char *wpath = filesysfulldev;
1063 char *part = fileSysPath;
1066 /* This file used to come from vfsck; cobble it up ourselves now... */
1068 ListViceInodes(dev, fileSysPath, path,
1069 singleVolumeNumber ? OnlyOneVolume : 0,
1070 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1072 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);
1076 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1078 if (forceSal && !ForceSalvage) {
1079 Log("***Forced salvage of all volumes on this partition***\n");
1082 inodeFd = afs_open(path, O_RDWR);
1083 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1085 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1087 tdir = (tmpdir ? tmpdir : part);
1089 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1090 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1092 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1093 "%s/salvage.temp.%d", tdir, getpid());
1095 summaryFile = afs_fopen(summaryFileName, "a+");
1096 if (summaryFile == NULL) {
1099 Abort("Unable to create inode summary file\n");
1101 if (!canfork || debug || Fork() == 0) {
1103 unsigned long st_size=(unsigned long) status.st_size;
1104 nInodes = st_size / sizeof(struct ViceInodeInfo);
1106 fclose(summaryFile);
1108 unlink(summaryFileName);
1109 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1110 RemoveTheForce(fileSysPath);
1112 struct VolumeSummary *vsp;
1115 GetVolumeSummary(singleVolumeNumber);
1117 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1119 DeleteExtraVolumeHeaderFile(vsp);
1122 Log("%s vice inodes on %s; not salvaged\n",
1123 singleVolumeNumber ? "No applicable" : "No", dev);
1126 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1128 fclose(summaryFile);
1131 unlink(summaryFileName);
1133 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1136 if (read(inodeFd, ip, st_size) != st_size) {
1137 fclose(summaryFile);
1140 unlink(summaryFileName);
1141 Abort("Unable to read inode table; %s not salvaged\n", dev);
1143 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1144 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1145 || write(inodeFd, ip, st_size) != st_size) {
1146 fclose(summaryFile);
1149 unlink(summaryFileName);
1150 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1154 CountVolumeInodes(ip, nInodes, &summary);
1155 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1156 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1157 fclose(summaryFile);
1161 summary.index += (summary.nInodes);
1162 nInodes -= summary.nInodes;
1163 ip += summary.nInodes;
1165 /* Following fflush is not fclose, because if it was debug mode would not work */
1166 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1167 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1168 fclose(summaryFile);
1172 if (canfork && !debug) {
1177 if (Wait("Inode summary") == -1) {
1178 fclose(summaryFile);
1181 unlink(summaryFileName);
1182 Exit(1); /* salvage of this partition aborted */
1185 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1186 if (status.st_size != 0) {
1188 unsigned long st_status=(unsigned long)status.st_size;
1189 inodeSummary = (struct InodeSummary *)malloc(st_status);
1190 assert(inodeSummary != NULL);
1191 /* For GNU we need to do lseek to get the file pointer moved. */
1192 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1193 ret = read(fileno(summaryFile), inodeSummary, st_status);
1194 assert(ret == st_status);
1196 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1197 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1198 fclose(summaryFile);
1200 unlink(summaryFileName);
1204 /* Comparison routine for volume sort.
1205 This is setup so that a read-write volume comes immediately before
1206 any read-only clones of that volume */
1208 CompareVolumes(const void *_p1, const void *_p2)
1210 register const struct VolumeSummary *p1 = _p1;
1211 register const struct VolumeSummary *p2 = _p2;
1212 if (p1->header.parent != p2->header.parent)
1213 return p1->header.parent < p2->header.parent ? -1 : 1;
1214 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1216 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1218 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1222 * Gleans volumeSummary information by asking the fileserver
1224 * @param[in] singleVolumeNumber the volume we're salvaging. 0 if we're
1225 * salvaging a whole partition
1227 * @return whether we obtained the volume summary information or not
1228 * @retval 0 success; we obtained the volume summary information
1229 * @retval nonzero we did not get the volume summary information; either the
1230 * fileserver responded with an error, or we are not supposed to
1231 * ask the fileserver for the information (e.g. we are salvaging
1232 * the entire partition or we are not the salvageserver)
1234 * @note for non-DAFS, always returns 1
1237 AskVolumeSummary(VolumeId singleVolumeNumber)
1240 #ifdef FSSYNC_BUILD_CLIENT
1241 if (programType == salvageServer) {
1242 if (singleVolumeNumber) {
1243 FSSYNC_VGQry_response_t q_res;
1245 struct VolumeSummary *vsp;
1247 struct VolumeDiskHeader diskHdr;
1249 memset(&res, 0, sizeof(res));
1251 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1254 * We must wait for the partition to finish scanning before
1255 * can continue, since we will not know if we got the entire
1256 * VG membership unless the partition is fully scanned.
1257 * We could, in theory, just scan the partition ourselves if
1258 * the VG cache is not ready, but we would be doing the exact
1259 * same scan the fileserver is doing; it will almost always
1260 * be faster to wait for the fileserver. The only exceptions
1261 * are if the partition does not take very long to scan, and
1262 * in that case it's fast either way, so who cares?
1264 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1265 Log("waiting for fileserver to finish scanning partition %s...\n",
1266 fileSysPartition->name);
1268 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1269 /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1270 * just so small partitions don't need to wait over 10
1271 * seconds every time, and large partitions are generally
1272 * polled only once every ten seconds. */
1273 sleep((i > 10) ? (i = 10) : i);
1275 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1279 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1280 /* This can happen if there's no header for the volume
1281 * we're salvaging, or no headers exist for the VG (if
1282 * we're salvaging an RW). Act as if we got a response
1283 * with no VG members. The headers may be created during
1284 * salvaging, if there are inodes in this VG. */
1286 memset(&q_res, 0, sizeof(q_res));
1287 q_res.rw = singleVolumeNumber;
1291 Log("fileserver refused VGCQuery request for volume %lu on "
1292 "partition %s, code %ld reason %ld\n",
1293 afs_printable_uint32_lu(singleVolumeNumber),
1294 fileSysPartition->name,
1295 afs_printable_int32_ld(code),
1296 afs_printable_int32_ld(res.hdr.reason));
1300 if (q_res.rw != singleVolumeNumber) {
1301 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1302 afs_printable_uint32_lu(singleVolumeNumber),
1303 afs_printable_uint32_lu(q_res.rw));
1304 #ifdef SALVSYNC_BUILD_CLIENT
1305 if (SALVSYNC_LinkVolume(q_res.rw,
1307 fileSysPartition->name,
1309 Log("schedule request failed\n");
1311 #endif /* SALVSYNC_BUILD_CLIENT */
1312 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1315 volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
1316 assert(volumeSummaryp != NULL);
1319 vsp = volumeSummaryp;
1321 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1322 char name[VMAXPATHLEN];
1324 if (!q_res.children[i]) {
1328 if (q_res.children[i] != singleVolumeNumber) {
1329 AskOffline(q_res.children[i], fileSysPartition->name);
1331 code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
1333 Log("Cannot read header for %lu; trying to salvage group anyway\n",
1334 afs_printable_uint32_lu(q_res.children[i]));
1339 DiskToVolumeHeader(&vsp->header, &diskHdr);
1340 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1341 vsp->fileName = ToString(name);
1346 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1351 Log("Cannot get volume summary from fileserver; falling back to scanning "
1352 "entire partition\n");
1355 #endif /* FSSYNC_BUILD_CLIENT */
1360 GetVolumeSummary(VolumeId singleVolumeNumber)
1363 afs_int32 nvols = 0;
1364 struct VolumeSummary *vsp, vs;
1365 struct VolumeDiskHeader diskHeader;
1368 if (AskVolumeSummary(singleVolumeNumber) == 0) {
1369 /* we successfully got the vol information from the fileserver; no
1370 * need to scan the partition */
1374 /* Get headers from volume directory */
1375 dirp = opendir(fileSysPath);
1377 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1378 if (!singleVolumeNumber) {
1379 while ((dp = readdir(dirp))) {
1380 char *p = dp->d_name;
1381 p = strrchr(dp->d_name, '.');
1382 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1385 sprintf(name, "%s/%s", fileSysPath, dp->d_name);
1386 if ((fd = afs_open(name, O_RDONLY)) != -1
1387 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1388 == sizeof(diskHeader)
1389 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1390 DiskToVolumeHeader(&vs.header, &diskHeader);
1398 dirp = opendir("."); /* No rewinddir for NT */
1405 nvols = VOL_VG_MAX_VOLS;
1408 volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
1409 assert(volumeSummaryp != NULL);
1412 vsp = volumeSummaryp;
1413 while ((dp = readdir(dirp))) {
1414 char *p = dp->d_name;
1415 p = strrchr(dp->d_name, '.');
1416 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1420 sprintf(name, "%s/%s", fileSysPath, dp->d_name);
1421 if ((fd = afs_open(name, O_RDONLY)) == -1
1422 || read(fd, &diskHeader, sizeof(diskHeader))
1423 != sizeof(diskHeader)
1424 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1429 if (!singleVolumeNumber) {
1431 Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1434 Log("Unable to unlink %s (errno = %d)\n", name, errno);
1439 char nameShouldBe[64];
1440 DiskToVolumeHeader(&vsp->header, &diskHeader);
1441 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1442 && vsp->header.parent != singleVolumeNumber) {
1443 if (programType == salvageServer) {
1444 #ifdef SALVSYNC_BUILD_CLIENT
1445 Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1446 vsp->header.id, vsp->header.parent);
1447 if (SALVSYNC_LinkVolume(vsp->header.parent,
1449 fileSysPartition->name,
1451 Log("schedule request failed\n");
1454 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1456 Log("%u is a read-only volume; not salvaged\n",
1457 singleVolumeNumber);
1461 if (!singleVolumeNumber
1462 || (vsp->header.id == singleVolumeNumber
1463 || vsp->header.parent == singleVolumeNumber)) {
1464 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1465 VFORMAT, afs_printable_uint32_lu(vsp->header.id));
1466 if (singleVolumeNumber
1467 && vsp->header.id != singleVolumeNumber)
1468 AskOffline(vsp->header.id, fileSysPartition->name);
1469 if (strcmp(nameShouldBe, dp->d_name)) {
1471 Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", name, (Testing ? "it would have been " : ""));
1474 Log("Unable to unlink %s (errno = %d)\n", name, errno);
1478 vsp->fileName = ToString(dp->d_name);
1488 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1492 /* Find the link table. This should be associated with the RW volume or, if
1493 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1496 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1497 struct ViceInodeInfo *allInodes)
1500 struct ViceInodeInfo *ip;
1502 for (i = 0; i < nVols; i++) {
1503 ip = allInodes + isp[i].index;
1504 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1505 if (ip[j].u.special.type == VI_LINKTABLE)
1506 return ip[j].inodeNumber;
1513 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1515 struct versionStamp version;
1518 if (!VALID_INO(ino))
1520 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1521 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1522 if (!VALID_INO(ino))
1524 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1525 isp->RWvolumeId, errno);
1526 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1527 fdP = IH_OPEN(VGLinkH);
1529 Abort("Can't open link table for volume %u (error = %d)\n",
1530 isp->RWvolumeId, errno);
1532 if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1533 Abort("Can't truncate link table for volume %u (error = %d)\n",
1534 isp->RWvolumeId, errno);
1536 version.magic = LINKTABLEMAGIC;
1537 version.version = LINKTABLEVERSION;
1539 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1541 Abort("Can't truncate link table for volume %u (error = %d)\n",
1542 isp->RWvolumeId, errno);
1544 FDH_REALLYCLOSE(fdP);
1546 /* If the volume summary exits (i.e., the V*.vol header file exists),
1547 * then set this inode there as well.
1549 if (isp->volSummary)
1550 isp->volSummary->header.linkTable = ino;
1559 SVGParms_t *parms = (SVGParms_t *) arg;
1560 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1565 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1568 pthread_attr_t tattr;
1572 /* Initialize per volume global variables, even if later code does so */
1576 memset(&VolInfo, 0, sizeof(VolInfo));
1578 parms.svgp_inodeSummaryp = isp;
1579 parms.svgp_count = nVols;
1580 code = pthread_attr_init(&tattr);
1582 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1586 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1588 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1591 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1593 Log("Failed to create thread to salvage volume group %u\n",
1597 (void)pthread_join(tid, NULL);
1599 #endif /* AFS_NT40_ENV */
1602 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1604 struct ViceInodeInfo *inodes, *allInodes, *ip;
1605 int i, totalInodes, size, salvageTo;
1609 int dec_VGLinkH = 0;
1611 FdHandle_t *fdP = NULL;
1614 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1615 && isp->nSpecialInodes > 0);
1616 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1617 if (!ForceSalvage && QuickCheck(isp, nVols))
1620 if (ShowMounts && !haveRWvolume)
1622 if (canfork && !debug && Fork() != 0) {
1623 (void)Wait("Salvage volume group");
1626 for (i = 0, totalInodes = 0; i < nVols; i++)
1627 totalInodes += isp[i].nInodes;
1628 size = totalInodes * sizeof(struct ViceInodeInfo);
1629 inodes = (struct ViceInodeInfo *)malloc(size);
1630 allInodes = inodes - isp->index; /* this would the base of all the inodes
1631 * for the partition, if all the inodes
1632 * had been read into memory */
1634 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1636 assert(read(inodeFd, inodes, size) == size);
1638 /* Don't try to salvage a read write volume if there isn't one on this
1640 salvageTo = haveRWvolume ? 0 : 1;
1642 #ifdef AFS_NAMEI_ENV
1643 ino = FindLinkHandle(isp, nVols, allInodes);
1644 if (VALID_INO(ino)) {
1645 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1646 fdP = IH_OPEN(VGLinkH);
1648 if (!VALID_INO(ino) || fdP == NULL) {
1649 Log("%s link table for volume %u.\n",
1650 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1652 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1655 struct ViceInodeInfo *ip;
1656 CreateLinkTable(isp, ino);
1657 fdP = IH_OPEN(VGLinkH);
1658 /* Sync fake 1 link counts to the link table, now that it exists */
1660 for (i = 0; i < nVols; i++) {
1661 ip = allInodes + isp[i].index;
1662 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1664 nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1666 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1674 FDH_REALLYCLOSE(fdP);
1676 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1679 /* Salvage in reverse order--read/write volume last; this way any
1680 * Inodes not referenced by the time we salvage the read/write volume
1681 * can be picked up by the read/write volume */
1682 /* ACTUALLY, that's not done right now--the inodes just vanish */
1683 for (i = nVols - 1; i >= salvageTo; i--) {
1685 struct InodeSummary *lisp = &isp[i];
1686 #ifdef AFS_NAMEI_ENV
1687 /* If only the RO is present on this partition, the link table
1688 * shows up as a RW volume special file. Need to make sure the
1689 * salvager doesn't try to salvage the non-existent RW.
1691 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1692 /* If this only special inode is the link table, continue */
1693 if (inodes->u.special.type == VI_LINKTABLE) {
1700 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1701 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1702 /* Check inodes twice. The second time do things seriously. This
1703 * way the whole RO volume can be deleted, below, if anything goes wrong */
1704 for (check = 1; check >= 0; check--) {
1706 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1708 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1709 if (rw && deleteMe) {
1710 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1711 * volume won't be called */
1717 if (rw && check == 1)
1719 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1720 MaybeZapVolume(lisp, "Vnode index", 0, check);
1726 /* Fix actual inode counts */
1728 Log("totalInodes %d\n",totalInodes);
1729 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1730 static int TraceBadLinkCounts = 0;
1731 #ifdef AFS_NAMEI_ENV
1732 if (VGLinkH->ih_ino == ip->inodeNumber) {
1733 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1734 VGLinkH_p1 = ip->u.param[0];
1735 continue; /* Deal with this last. */
1738 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1739 TraceBadLinkCounts--; /* Limit reports, per volume */
1740 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]);
1742 while (ip->linkCount > 0) {
1743 /* below used to assert, not break */
1745 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1746 Log("idec failed. inode %s errno %d\n",
1747 PrintInode(NULL, ip->inodeNumber), errno);
1753 while (ip->linkCount < 0) {
1754 /* these used to be asserts */
1756 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1757 Log("iinc failed. inode %s errno %d\n",
1758 PrintInode(NULL, ip->inodeNumber), errno);
1765 #ifdef AFS_NAMEI_ENV
1766 while (dec_VGLinkH > 0) {
1767 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1768 Log("idec failed on link table, errno = %d\n", errno);
1772 while (dec_VGLinkH < 0) {
1773 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1774 Log("iinc failed on link table, errno = %d\n", errno);
1781 /* Directory consistency checks on the rw volume */
1783 SalvageVolume(isp, VGLinkH);
1784 IH_RELEASE(VGLinkH);
1786 if (canfork && !debug) {
1793 QuickCheck(register struct InodeSummary *isp, int nVols)
1795 /* Check headers BEFORE forking */
1799 for (i = 0; i < nVols; i++) {
1800 struct VolumeSummary *vs = isp[i].volSummary;
1801 VolumeDiskData volHeader;
1803 /* Don't salvage just because phantom rw volume is there... */
1804 /* (If a read-only volume exists, read/write inodes must also exist) */
1805 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
1809 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1810 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
1811 == sizeof(volHeader)
1812 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1813 && volHeader.dontSalvage == DONT_SALVAGE
1814 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
1815 if (volHeader.inUse != 0) {
1816 volHeader.inUse = 0;
1817 volHeader.inService = 1;
1819 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
1820 != sizeof(volHeader)) {
1836 /* SalvageVolumeHeaderFile
1838 * Salvage the top level V*.vol header file. Make sure the special files
1839 * exist and that there are no duplicates.
1841 * Calls SalvageHeader for each possible type of volume special file.
1845 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1846 register struct ViceInodeInfo *inodes, int RW,
1847 int check, int *deleteMe)
1850 register struct ViceInodeInfo *ip;
1851 int allinodesobsolete = 1;
1852 struct VolumeDiskHeader diskHeader;
1853 afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
1856 /* keeps track of special inodes that are probably 'good'; they are
1857 * referenced in the vol header, and are included in the given inodes
1862 } goodspecial[MAXINODETYPE];
1867 memset(goodspecial, 0, sizeof(goodspecial));
1869 skip = malloc(isp->nSpecialInodes * sizeof(*skip));
1871 memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
1873 Log("cannot allocate memory for inode skip array when salvaging "
1874 "volume %lu; not performing duplicate special inode recovery\n",
1875 afs_printable_uint32_lu(isp->volumeId));
1876 /* still try to perform the salvage; the skip array only does anything
1877 * if we detect duplicate special inodes */
1881 * First, look at the special inodes and see if any are referenced by
1882 * the existing volume header. If we find duplicate special inodes, we
1883 * can use this information to use the referenced inode (it's more
1884 * likely to be the 'good' one), and throw away the duplicates.
1886 if (isp->volSummary && skip) {
1887 /* use tempHeader, so we can use the stuff[] array to easily index
1888 * into the isp->volSummary special inodes */
1889 memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
1891 for (i = 0; i < isp->nSpecialInodes; i++) {
1892 ip = &inodes[isp->index + i];
1893 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1894 /* will get taken care of in a later loop */
1897 if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
1898 goodspecial[ip->u.special.type-1].valid = 1;
1899 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
1904 memset(&tempHeader, 0, sizeof(tempHeader));
1905 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1906 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1907 tempHeader.id = isp->volumeId;
1908 tempHeader.parent = isp->RWvolumeId;
1910 /* Check for duplicates (inodes are sorted by type field) */
1911 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
1912 ip = &inodes[isp->index + i];
1913 if (ip->u.special.type == (ip + 1)->u.special.type) {
1914 afs_ino_str_t stmp1, stmp2;
1916 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1917 /* Will be caught in the loop below */
1921 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
1922 ip->u.special.type, isp->volumeId,
1923 PrintInode(stmp1, ip->inodeNumber),
1924 PrintInode(stmp2, (ip+1)->inodeNumber));
1926 if (skip && goodspecial[ip->u.special.type-1].valid) {
1927 Inode gi = goodspecial[ip->u.special.type-1].inode;
1930 Log("using special inode referenced by vol header (%s)\n",
1931 PrintInode(stmp1, gi));
1934 /* the volume header references some special inode of
1935 * this type in the inodes array; are we it? */
1936 if (ip->inodeNumber != gi) {
1938 } else if ((ip+1)->inodeNumber != gi) {
1939 /* in case this is the last iteration; we need to
1940 * make sure we check ip+1, too */
1945 Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
1953 for (i = 0; i < isp->nSpecialInodes; i++) {
1954 ip = &inodes[isp->index + i];
1955 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1957 Log("Rubbish header inode %s of type %d\n",
1958 PrintInode(NULL, ip->inodeNumber),
1959 ip->u.special.type);
1965 Log("Rubbish header inode %s of type %d; deleted\n",
1966 PrintInode(NULL, ip->inodeNumber),
1967 ip->u.special.type);
1968 } else if (!stuff[ip->u.special.type - 1].obsolete) {
1969 if (skip && skip[i]) {
1970 if (orphans == ORPH_REMOVE) {
1971 Log("Removing orphan special inode %s of type %d\n",
1972 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
1975 Log("Ignoring orphan special inode %s of type %d\n",
1976 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
1977 /* fall through to the ip->linkCount--; line below */
1980 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
1981 allinodesobsolete = 0;
1983 if (!check && ip->u.special.type != VI_LINKTABLE)
1984 ip->linkCount--; /* Keep the inode around */
1992 if (allinodesobsolete) {
1999 VGLinkH_cnt++; /* one for every header. */
2001 if (!RW && !check && isp->volSummary) {
2002 ClearROInUseBit(isp->volSummary);
2006 for (i = 0; i < MAXINODETYPE; i++) {
2007 if (stuff[i].inodeType == VI_LINKTABLE) {
2008 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2009 * And we may have recreated the link table earlier, so set the
2010 * RW header as well.
2012 if (VALID_INO(VGLinkH->ih_ino)) {
2013 *stuff[i].inode = VGLinkH->ih_ino;
2017 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2021 if (isp->volSummary == NULL) {
2023 char headerName[64];
2024 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2025 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2027 Log("No header file for volume %u\n", isp->volumeId);
2031 Log("No header file for volume %u; %screating %s\n",
2032 isp->volumeId, (Testing ? "it would have been " : ""),
2034 isp->volSummary = (struct VolumeSummary *)
2035 malloc(sizeof(struct VolumeSummary));
2036 isp->volSummary->fileName = ToString(headerName);
2038 writefunc = VCreateVolumeDiskHeader;
2041 char headerName[64];
2042 /* hack: these two fields are obsolete... */
2043 isp->volSummary->header.volumeAcl = 0;
2044 isp->volSummary->header.volumeMountTable = 0;
2047 (&isp->volSummary->header, &tempHeader,
2048 sizeof(struct VolumeHeader))) {
2049 /* We often remove the name before calling us, so we make a fake one up */
2050 if (isp->volSummary->fileName) {
2051 strcpy(headerName, isp->volSummary->fileName);
2053 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2054 isp->volSummary->fileName = ToString(headerName);
2056 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2058 Log("Header file %s is damaged or no longer valid%s\n", path,
2059 (check ? "" : "; repairing"));
2063 writefunc = VWriteVolumeDiskHeader;
2067 memcpy(&isp->volSummary->header, &tempHeader,
2068 sizeof(struct VolumeHeader));
2071 Log("It would have written a new header file for volume %u\n",
2075 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2076 code = (*writefunc)(&diskHeader, fileSysPartition);
2078 Log("Error %ld writing volume header file for volume %lu\n",
2079 afs_printable_int32_ld(code),
2080 afs_printable_uint32_lu(diskHeader.id));
2085 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2086 isp->volSummary->header.volumeInfo);
2091 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2095 VolumeDiskData volumeInfo;
2096 struct versionStamp fileHeader;
2105 #ifndef AFS_NAMEI_ENV
2106 if (sp->inodeType == VI_LINKTABLE)
2109 if (*(sp->inode) == 0) {
2111 Log("Missing inode in volume header (%s)\n", sp->description);
2115 Log("Missing inode in volume header (%s); %s\n", sp->description,
2116 (Testing ? "it would have recreated it" : "recreating"));
2119 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2120 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2121 if (!VALID_INO(*(sp->inode)))
2123 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2124 sp->description, errno);
2129 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2130 fdP = IH_OPEN(specH);
2131 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2132 /* bail out early and destroy the volume */
2134 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2141 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2142 sp->description, errno);
2145 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2146 || header.fileHeader.magic != sp->stamp.magic)) {
2148 Log("Part of the header (%s) is corrupted\n", sp->description);
2149 FDH_REALLYCLOSE(fdP);
2153 Log("Part of the header (%s) is corrupted; recreating\n",
2157 if (sp->inodeType == VI_VOLINFO
2158 && header.volumeInfo.destroyMe == DESTROY_ME) {
2161 FDH_REALLYCLOSE(fdP);
2165 if (recreate && !Testing) {
2168 ("Internal error: recreating volume header (%s) in check mode\n",
2170 code = FDH_TRUNC(fdP, 0);
2172 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2173 sp->description, errno);
2175 /* The following code should be moved into vutil.c */
2176 if (sp->inodeType == VI_VOLINFO) {
2178 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2179 header.volumeInfo.stamp = sp->stamp;
2180 header.volumeInfo.id = isp->volumeId;
2181 header.volumeInfo.parentId = isp->RWvolumeId;
2182 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2183 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2184 isp->volumeId, isp->volumeId);
2185 header.volumeInfo.inService = 0;
2186 header.volumeInfo.blessed = 0;
2187 /* The + 1000 is a hack in case there are any files out in venus caches */
2188 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2189 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2190 header.volumeInfo.needsCallback = 0;
2191 gettimeofday(&tp, 0);
2192 header.volumeInfo.creationDate = tp.tv_sec;
2193 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2195 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2196 sp->description, errno);
2199 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2200 sizeof(header.volumeInfo));
2201 if (code != sizeof(header.volumeInfo)) {
2204 ("Unable to write volume header file (%s) (errno = %d)\n",
2205 sp->description, errno);
2206 Abort("Unable to write entire volume header file (%s)\n",
2210 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2212 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2213 sp->description, errno);
2215 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2216 if (code != sizeof(sp->stamp)) {
2219 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2220 sp->description, errno);
2222 ("Unable to write entire version stamp in volume header file (%s)\n",
2227 FDH_REALLYCLOSE(fdP);
2229 if (sp->inodeType == VI_VOLINFO) {
2230 VolInfo = header.volumeInfo;
2233 if (VolInfo.updateDate) {
2234 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2236 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2237 (Testing ? "it would have been " : ""), update);
2239 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2241 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2242 VolInfo.id, update);
2252 SalvageVnodes(register struct InodeSummary *rwIsp,
2253 register struct InodeSummary *thisIsp,
2254 register struct ViceInodeInfo *inodes, int check)
2256 int ilarge, ismall, ioffset, RW, nInodes;
2257 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2260 RW = (rwIsp == thisIsp);
2261 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2263 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2264 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2265 if (check && ismall == -1)
2268 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2269 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2270 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2274 SalvageIndex(Inode ino, VnodeClass class, int RW,
2275 register struct ViceInodeInfo *ip, int nInodes,
2276 struct VolumeSummary *volSummary, int check)
2278 VolumeId volumeNumber;
2279 char buf[SIZEOF_LARGEDISKVNODE];
2280 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2282 StreamHandle_t *file;
2283 struct VnodeClassInfo *vcp;
2285 afs_fsize_t vnodeLength;
2286 int vnodeIndex, nVnodes;
2287 afs_ino_str_t stmp1, stmp2;
2291 volumeNumber = volSummary->header.id;
2292 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2293 fdP = IH_OPEN(handle);
2294 assert(fdP != NULL);
2295 file = FDH_FDOPEN(fdP, "r+");
2296 assert(file != NULL);
2297 vcp = &VnodeClassInfo[class];
2298 size = OS_SIZE(fdP->fd_fd);
2300 nVnodes = (size / vcp->diskSize) - 1;
2302 assert((nVnodes + 1) * vcp->diskSize == size);
2303 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2307 for (vnodeIndex = 0;
2308 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2309 nVnodes--, vnodeIndex++) {
2310 if (vnode->type != vNull) {
2311 int vnodeChanged = 0;
2312 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2313 /* Log programs that belong to root (potentially suid root);
2314 * don't bother for read-only or backup volumes */
2315 #ifdef notdef /* This is done elsewhere */
2316 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2317 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);
2319 if (VNDISK_GET_INO(vnode) == 0) {
2321 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2322 memset(vnode, 0, vcp->diskSize);
2326 if (vcp->magic != vnode->vnodeMagic) {
2327 /* bad magic #, probably partially created vnode */
2328 Log("Partially allocated vnode %d deleted.\n",
2330 memset(vnode, 0, vcp->diskSize);
2334 /* ****** Should do a bit more salvage here: e.g. make sure
2335 * vnode type matches what it should be given the index */
2336 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2337 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2338 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2339 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2346 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2347 /* The following doesn't work, because the version number
2348 * is not maintained correctly by the file server */
2349 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2350 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2352 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2358 /* For RW volume, look for vnode with matching inode number;
2359 * if no such match, take the first determined by our sort
2361 register struct ViceInodeInfo *lip = ip;
2362 register int lnInodes = nInodes;
2364 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2365 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2374 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2375 /* "Matching" inode */
2379 vu = vnode->uniquifier;
2380 iu = ip->u.vnode.vnodeUniquifier;
2381 vd = vnode->dataVersion;
2382 id = ip->u.vnode.inodeDataVersion;
2384 * Because of the possibility of the uniquifier overflows (> 4M)
2385 * we compare them modulo the low 22-bits; we shouldn't worry
2386 * about mismatching since they shouldn't to many old
2387 * uniquifiers of the same vnode...
2389 if (IUnique(vu) != IUnique(iu)) {
2391 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2394 vnode->uniquifier = iu;
2395 #ifdef AFS_3DISPARES
2396 vnode->dataVersion = (id >= vd ?
2399 1887437 ? vd : id) :
2402 1887437 ? id : vd));
2404 #if defined(AFS_SGI_EXMAG)
2405 vnode->dataVersion = (id >= vd ?
2408 15099494 ? vd : id) :
2411 15099494 ? id : vd));
2413 vnode->dataVersion = (id > vd ? id : vd);
2414 #endif /* AFS_SGI_EXMAG */
2415 #endif /* AFS_3DISPARES */
2418 /* don't bother checking for vd > id any more, since
2419 * partial file transfers always result in this state,
2420 * and you can't do much else anyway (you've already
2421 * found the best data you can) */
2422 #ifdef AFS_3DISPARES
2423 if (!vnodeIsDirectory(vnodeNumber)
2424 && ((vd < id && (id - vd) < 1887437)
2425 || ((vd > id && (vd - id) > 1887437)))) {
2427 #if defined(AFS_SGI_EXMAG)
2428 if (!vnodeIsDirectory(vnodeNumber)
2429 && ((vd < id && (id - vd) < 15099494)
2430 || ((vd > id && (vd - id) > 15099494)))) {
2432 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2433 #endif /* AFS_SGI_EXMAG */
2436 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2437 vnode->dataVersion = id;
2442 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2445 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);
2447 VNDISK_SET_INO(vnode, ip->inodeNumber);
2452 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);
2454 VNDISK_SET_INO(vnode, ip->inodeNumber);
2457 VNDISK_GET_LEN(vnodeLength, vnode);
2458 if (ip->byteCount != vnodeLength) {
2461 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2466 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2467 VNDISK_SET_LEN(vnode, ip->byteCount);
2471 ip->linkCount--; /* Keep the inode around */
2474 } else { /* no matching inode */
2475 if (VNDISK_GET_INO(vnode) != 0
2476 || vnode->type == vDirectory) {
2477 /* No matching inode--get rid of the vnode */
2479 if (VNDISK_GET_INO(vnode)) {
2481 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2485 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2490 if (VNDISK_GET_INO(vnode)) {
2492 time_t serverModifyTime = vnode->serverModifyTime;
2493 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));
2497 time_t serverModifyTime = vnode->serverModifyTime;
2498 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2501 memset(vnode, 0, vcp->diskSize);
2504 /* Should not reach here becuase we checked for
2505 * (inodeNumber == 0) above. And where we zero the vnode,
2506 * we also goto vnodeDone.
2510 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2514 } /* VNDISK_GET_INO(vnode) != 0 */
2516 assert(!(vnodeChanged && check));
2517 if (vnodeChanged && !Testing) {
2519 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2520 (char *)vnode, vcp->diskSize)
2522 VolumeChanged = 1; /* For break call back */
2533 struct VnodeEssence *
2534 CheckVnodeNumber(VnodeId vnodeNumber)
2537 struct VnodeInfo *vip;
2540 class = vnodeIdToClass(vnodeNumber);
2541 vip = &vnodeInfo[class];
2542 offset = vnodeIdToBitNumber(vnodeNumber);
2543 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2547 CopyOnWrite(register struct DirSummary *dir)
2549 /* Copy the directory unconditionally if we are going to change it:
2550 * not just if was cloned.
2552 struct VnodeDiskObject vnode;
2553 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2554 Inode oldinode, newinode;
2557 if (dir->copied || Testing)
2559 DFlush(); /* Well justified paranoia... */
2562 IH_IREAD(vnodeInfo[vLarge].handle,
2563 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2565 assert(code == sizeof(vnode));
2566 oldinode = VNDISK_GET_INO(&vnode);
2567 /* Increment the version number by a whole lot to avoid problems with
2568 * clients that were promised new version numbers--but the file server
2569 * crashed before the versions were written to disk.
2572 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2573 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2575 assert(VALID_INO(newinode));
2576 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2578 VNDISK_SET_INO(&vnode, newinode);
2580 IH_IWRITE(vnodeInfo[vLarge].handle,
2581 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2583 assert(code == sizeof(vnode));
2585 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2586 fileSysDevice, newinode);
2587 /* Don't delete the original inode right away, because the directory is
2588 * still being scanned.
2594 * This function should either successfully create a new dir, or give up
2595 * and leave things the way they were. In particular, if it fails to write
2596 * the new dir properly, it should return w/o changing the reference to the
2600 CopyAndSalvage(register struct DirSummary *dir)
2602 struct VnodeDiskObject vnode;
2603 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2604 Inode oldinode, newinode;
2609 afs_int32 parentUnique = 1;
2610 struct VnodeEssence *vnodeEssence;
2615 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2617 IH_IREAD(vnodeInfo[vLarge].handle,
2618 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2620 assert(lcode == sizeof(vnode));
2621 oldinode = VNDISK_GET_INO(&vnode);
2622 /* Increment the version number by a whole lot to avoid problems with
2623 * clients that were promised new version numbers--but the file server
2624 * crashed before the versions were written to disk.
2627 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2628 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2630 assert(VALID_INO(newinode));
2631 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2633 /* Assign . and .. vnode numbers from dir and vnode.parent.
2634 * The uniquifier for . is in the vnode.
2635 * The uniquifier for .. might be set to a bogus value of 1 and
2636 * the salvager will later clean it up.
2638 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2639 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2642 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2644 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2649 /* didn't really build the new directory properly, let's just give up. */
2650 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2651 Log("Directory salvage returned code %d, continuing.\n", code);
2653 Log("also failed to decrement link count on new inode");
2657 Log("Checking the results of the directory salvage...\n");
2658 if (!DirOK(&newdir)) {
2659 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2660 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2665 VNDISK_SET_INO(&vnode, newinode);
2666 length = Length(&newdir);
2667 VNDISK_SET_LEN(&vnode, length);
2669 IH_IWRITE(vnodeInfo[vLarge].handle,
2670 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2672 assert(lcode == sizeof(vnode));
2675 nt_sync(fileSysDevice);
2677 sync(); /* this is slow, but hopefully rarely called. We don't have
2678 * an open FD on the file itself to fsync.
2682 vnodeInfo[vLarge].handle->ih_synced = 1;
2684 /* make sure old directory file is really closed */
2685 fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2686 FDH_REALLYCLOSE(fdP);
2688 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2690 dir->dirHandle = newdir;
2694 JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
2697 struct DirSummary *dir = (struct DirSummary *)dirVal;
2698 struct VnodeEssence *vnodeEssence;
2699 afs_int32 dirOrphaned, todelete;
2701 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2703 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2704 if (vnodeEssence == NULL) {
2706 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2710 assert(Delete(&dir->dirHandle, name) == 0);
2715 #ifndef AFS_NAMEI_ENV
2716 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2717 * mount inode for the partition. If this inode were deleted, it would crash
2720 if (vnodeEssence->InodeNumber == 0) {
2721 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"));
2724 assert(Delete(&dir->dirHandle, name) == 0);
2731 if (!(vnodeNumber & 1) && !Showmode
2732 && !(vnodeEssence->count || vnodeEssence->unique
2733 || vnodeEssence->modeBits)) {
2734 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2735 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2736 vnodeNumber, unique,
2737 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2742 assert(Delete(&dir->dirHandle, name) == 0);
2748 /* Check if the Uniquifiers match. If not, change the directory entry
2749 * so its unique matches the vnode unique. Delete if the unique is zero
2750 * or if the directory is orphaned.
2752 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2753 if (!vnodeEssence->unique
2754 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2755 /* This is an orphaned directory. Don't delete the . or ..
2756 * entry. Otherwise, it will get created in the next
2757 * salvage and deleted again here. So Just skip it.
2762 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2765 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")));
2769 fid.Vnode = vnodeNumber;
2770 fid.Unique = vnodeEssence->unique;
2772 assert(Delete(&dir->dirHandle, name) == 0);
2774 assert(Create(&dir->dirHandle, name, &fid) == 0);
2777 return 0; /* no need to continue */
2780 if (strcmp(name, ".") == 0) {
2781 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2784 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2787 assert(Delete(&dir->dirHandle, ".") == 0);
2788 fid.Vnode = dir->vnodeNumber;
2789 fid.Unique = dir->unique;
2790 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2793 vnodeNumber = fid.Vnode; /* Get the new Essence */
2794 unique = fid.Unique;
2795 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2798 } else if (strcmp(name, "..") == 0) {
2801 struct VnodeEssence *dotdot;
2802 pa.Vnode = dir->parent;
2803 dotdot = CheckVnodeNumber(pa.Vnode);
2804 assert(dotdot != NULL); /* XXX Should not be assert */
2805 pa.Unique = dotdot->unique;
2807 pa.Vnode = dir->vnodeNumber;
2808 pa.Unique = dir->unique;
2810 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2812 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2815 assert(Delete(&dir->dirHandle, "..") == 0);
2816 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2819 vnodeNumber = pa.Vnode; /* Get the new Essence */
2821 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2823 dir->haveDotDot = 1;
2824 } else if (strncmp(name, ".__afs", 6) == 0) {
2826 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);
2830 assert(Delete(&dir->dirHandle, name) == 0);
2832 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2833 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2836 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2837 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);
2838 if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
2839 && !(vnodeEssence->modeBits & 0111)) {
2845 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2846 vnodeEssence->InodeNumber);
2849 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
2853 size = FDH_SIZE(fdP);
2855 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
2856 FDH_REALLYCLOSE(fdP);
2863 code = FDH_READ(fdP, buf, size);
2866 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
2867 Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
2868 dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
2869 Testing ? "would convert" : "converted");
2870 vnodeEssence->modeBits |= 0111;
2871 vnodeEssence->changed = 1;
2872 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2873 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2874 dir->name ? dir->name : "??", name, buf);
2876 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
2877 dir->vname, vnodeNumber, size, code);
2879 FDH_REALLYCLOSE(fdP);
2882 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
2883 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);
2884 if (vnodeIdToClass(vnodeNumber) == vLarge
2885 && vnodeEssence->name == NULL) {
2887 if ((n = (char *)malloc(strlen(name) + 1)))
2889 vnodeEssence->name = n;
2892 /* The directory entry points to the vnode. Check to see if the
2893 * vnode points back to the directory. If not, then let the
2894 * directory claim it (else it might end up orphaned). Vnodes
2895 * already claimed by another directory are deleted from this
2896 * directory: hardlinks to the same vnode are not allowed
2897 * from different directories.
2899 if (vnodeEssence->parent != dir->vnodeNumber) {
2900 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
2901 /* Vnode does not point back to this directory.
2902 * Orphaned dirs cannot claim a file (it may belong to
2903 * another non-orphaned dir).
2906 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);
2908 vnodeEssence->parent = dir->vnodeNumber;
2909 vnodeEssence->changed = 1;
2911 /* Vnode was claimed by another directory */
2914 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 " : ""));
2915 } else if (vnodeNumber == 1) {
2916 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 " : ""));
2918 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 " : ""));
2923 assert(Delete(&dir->dirHandle, name) == 0);
2928 /* This directory claims the vnode */
2929 vnodeEssence->claimed = 1;
2931 vnodeEssence->count--;
2936 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
2938 register struct VnodeInfo *vip = &vnodeInfo[class];
2939 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2940 char buf[SIZEOF_LARGEDISKVNODE];
2941 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2943 StreamHandle_t *file;
2948 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2949 fdP = IH_OPEN(vip->handle);
2950 assert(fdP != NULL);
2951 file = FDH_FDOPEN(fdP, "r+");
2952 assert(file != NULL);
2953 size = OS_SIZE(fdP->fd_fd);
2955 vip->nVnodes = (size / vcp->diskSize) - 1;
2956 if (vip->nVnodes > 0) {
2957 assert((vip->nVnodes + 1) * vcp->diskSize == size);
2958 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2959 assert((vip->vnodes = (struct VnodeEssence *)
2960 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2961 if (class == vLarge) {
2962 assert((vip->inodes = (Inode *)
2963 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
2972 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2973 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2974 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2975 nVnodes--, vnodeIndex++) {
2976 if (vnode->type != vNull) {
2977 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2978 afs_fsize_t vnodeLength;
2979 vip->nAllocatedVnodes++;
2980 vep->count = vnode->linkCount;
2981 VNDISK_GET_LEN(vnodeLength, vnode);
2982 vep->blockCount = nBlocks(vnodeLength);
2983 vip->volumeBlockCount += vep->blockCount;
2984 vep->parent = vnode->parent;
2985 vep->unique = vnode->uniquifier;
2986 if (*maxu < vnode->uniquifier)
2987 *maxu = vnode->uniquifier;
2988 vep->modeBits = vnode->modeBits;
2989 vep->InodeNumber = VNDISK_GET_INO(vnode);
2990 vep->type = vnode->type;
2991 vep->author = vnode->author;
2992 vep->owner = vnode->owner;
2993 vep->group = vnode->group;
2994 if (vnode->type == vDirectory) {
2995 if (class != vLarge) {
2996 VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2997 vip->nAllocatedVnodes--;
2998 memset(vnode, 0, sizeof(vnode));
2999 IH_IWRITE(vnodeInfo[vSmall].handle,
3000 vnodeIndexOffset(vcp, vnodeNumber),
3001 (char *)&vnode, sizeof(vnode));
3004 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3013 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3015 struct VnodeEssence *parentvp;
3021 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3022 && GetDirName(vp->parent, parentvp, path)) {
3024 strcat(path, vp->name);
3030 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3031 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3034 IsVnodeOrphaned(VnodeId vnode)
3036 struct VnodeEssence *vep;
3039 return (1); /* Vnode zero does not exist */
3041 return (0); /* The root dir vnode is always claimed */
3042 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3043 if (!vep || !vep->claimed)
3044 return (1); /* Vnode is not claimed - it is orphaned */
3046 return (IsVnodeOrphaned(vep->parent));
3050 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3051 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3054 static struct DirSummary dir;
3055 static struct DirHandle dirHandle;
3056 struct VnodeEssence *parent;
3057 static char path[MAXPATHLEN];
3060 if (dirVnodeInfo->vnodes[i].salvaged)
3061 return; /* already salvaged */
3064 dirVnodeInfo->vnodes[i].salvaged = 1;
3066 if (dirVnodeInfo->inodes[i] == 0)
3067 return; /* Not allocated to a directory */
3069 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3070 if (dirVnodeInfo->vnodes[i].parent) {
3071 Log("Bad parent, vnode 1; %s...\n",
3072 (Testing ? "skipping" : "salvaging"));
3073 dirVnodeInfo->vnodes[i].parent = 0;
3074 dirVnodeInfo->vnodes[i].changed = 1;
3077 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3078 if (parent && parent->salvaged == 0)
3079 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3080 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3081 rootdir, rootdirfound);
3084 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3085 dir.unique = dirVnodeInfo->vnodes[i].unique;
3088 dir.parent = dirVnodeInfo->vnodes[i].parent;
3089 dir.haveDot = dir.haveDotDot = 0;
3090 dir.ds_linkH = alinkH;
3091 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3092 dirVnodeInfo->inodes[i]);
3094 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3097 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3098 (Testing ? "skipping" : "salvaging"));
3101 CopyAndSalvage(&dir);
3105 dirHandle = dir.dirHandle;
3108 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3109 &dirVnodeInfo->vnodes[i], path);
3112 /* If enumeration failed for random reasons, we will probably delete
3113 * too much stuff, so we guard against this instead.
3115 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3118 /* Delete the old directory if it was copied in order to salvage.
3119 * CopyOnWrite has written the new inode # to the disk, but we still
3120 * have the old one in our local structure here. Thus, we idec the
3124 if (dir.copied && !Testing) {
3125 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3127 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3130 /* Remember rootdir DirSummary _after_ it has been judged */
3131 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3132 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3140 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3142 /* This routine, for now, will only be called for read-write volumes */
3144 int BlocksInVolume = 0, FilesInVolume = 0;
3145 register VnodeClass class;
3146 struct DirSummary rootdir, oldrootdir;
3147 struct VnodeInfo *dirVnodeInfo;
3148 struct VnodeDiskObject vnode;
3149 VolumeDiskData volHeader;
3151 int orphaned, rootdirfound = 0;
3152 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3153 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3154 struct VnodeEssence *vep;
3157 afs_sfsize_t nBytes;
3159 VnodeId LFVnode, ThisVnode;
3160 Unique LFUnique, ThisUnique;
3163 vid = rwIsp->volSummary->header.id;
3164 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3165 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3166 assert(nBytes == sizeof(volHeader));
3167 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3168 assert(volHeader.destroyMe != DESTROY_ME);
3169 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3171 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3173 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3176 dirVnodeInfo = &vnodeInfo[vLarge];
3177 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3178 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3182 nt_sync(fileSysDevice);
3184 sync(); /* This used to be done lower level, for every dir */
3191 /* Parse each vnode looking for orphaned vnodes and
3192 * connect them to the tree as orphaned (if requested).
3194 oldrootdir = rootdir;
3195 for (class = 0; class < nVNODECLASSES; class++) {
3196 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3197 vep = &(vnodeInfo[class].vnodes[v]);
3198 ThisVnode = bitNumberToVnodeNumber(v, class);
3199 ThisUnique = vep->unique;
3201 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3202 continue; /* Ignore unused, claimed, and root vnodes */
3204 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3205 * entry in this vnode had incremented the parent link count (In
3206 * JudgeEntry()). We need to go to the parent and decrement that
3207 * link count. But if the parent's unique is zero, then the parent
3208 * link count was not incremented in JudgeEntry().
3210 if (class == vLarge) { /* directory vnode */
3211 pv = vnodeIdToBitNumber(vep->parent);
3212 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3213 vnodeInfo[vLarge].vnodes[pv].count++;
3217 continue; /* If no rootdir, can't attach orphaned files */
3219 /* Here we attach orphaned files and directories into the
3220 * root directory, LVVnode, making sure link counts stay correct.
3222 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3223 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3224 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3226 /* Update this orphaned vnode's info. Its parent info and
3227 * link count (do for orphaned directories and files).
3229 vep->parent = LFVnode; /* Parent is the root dir */
3230 vep->unique = LFUnique;
3233 vep->count--; /* Inc link count (root dir will pt to it) */
3235 /* If this orphaned vnode is a directory, change '..'.
3236 * The name of the orphaned dir/file is unknown, so we
3237 * build a unique name. No need to CopyOnWrite the directory
3238 * since it is not connected to tree in BK or RO volume and
3239 * won't be visible there.
3241 if (class == vLarge) {
3245 /* Remove and recreate the ".." entry in this orphaned directory */
3246 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3247 vnodeInfo[class].inodes[v]);
3249 pa.Unique = LFUnique;
3250 assert(Delete(&dh, "..") == 0);
3251 assert(Create(&dh, "..", &pa) == 0);
3253 /* The original parent's link count was decremented above.
3254 * Here we increment the new parent's link count.
3256 pv = vnodeIdToBitNumber(LFVnode);
3257 vnodeInfo[vLarge].vnodes[pv].count--;
3261 /* Go to the root dir and add this entry. The link count of the
3262 * root dir was incremented when ".." was created. Try 10 times.
3264 for (j = 0; j < 10; j++) {
3265 pa.Vnode = ThisVnode;
3266 pa.Unique = ThisUnique;
3268 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3270 vLarge) ? "__ORPHANDIR__" :
3271 "__ORPHANFILE__"), ThisVnode,
3274 CopyOnWrite(&rootdir);
3275 code = Create(&rootdir.dirHandle, npath, &pa);
3279 ThisUnique += 50; /* Try creating a different file */
3282 Log("Attaching orphaned %s to volume's root dir as %s\n",
3283 ((class == vLarge) ? "directory" : "file"), npath);
3285 } /* for each vnode in the class */
3286 } /* for each class of vnode */
3288 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3290 if (!oldrootdir.copied && rootdir.copied) {
3292 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3295 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3298 DFlush(); /* Flush the changes */
3299 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3300 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3301 orphans = ORPH_IGNORE;
3304 /* Write out all changed vnodes. Orphaned files and directories
3305 * will get removed here also (if requested).
3307 for (class = 0; class < nVNODECLASSES; class++) {
3308 int nVnodes = vnodeInfo[class].nVnodes;
3309 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3310 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3311 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3312 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3313 for (i = 0; i < nVnodes; i++) {
3314 register struct VnodeEssence *vnp = &vnodes[i];
3315 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3317 /* If the vnode is good but is unclaimed (not listed in
3318 * any directory entries), then it is orphaned.
3321 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3322 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3326 if (vnp->changed || vnp->count) {
3330 IH_IREAD(vnodeInfo[class].handle,
3331 vnodeIndexOffset(vcp, vnodeNumber),
3332 (char *)&vnode, sizeof(vnode));
3333 assert(nBytes == sizeof(vnode));
3335 vnode.parent = vnp->parent;
3336 oldCount = vnode.linkCount;
3337 vnode.linkCount = vnode.linkCount - vnp->count;
3340 orphaned = IsVnodeOrphaned(vnodeNumber);
3342 if (!vnp->todelete) {
3343 /* Orphans should have already been attached (if requested) */
3344 assert(orphans != ORPH_ATTACH);
3345 oblocks += vnp->blockCount;
3348 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3350 BlocksInVolume -= vnp->blockCount;
3352 if (VNDISK_GET_INO(&vnode)) {
3354 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3357 memset(&vnode, 0, sizeof(vnode));
3359 } else if (vnp->count) {
3361 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3364 vnode.modeBits = vnp->modeBits;
3367 vnode.dataVersion++;
3370 IH_IWRITE(vnodeInfo[class].handle,
3371 vnodeIndexOffset(vcp, vnodeNumber),
3372 (char *)&vnode, sizeof(vnode));
3373 assert(nBytes == sizeof(vnode));
3379 if (!Showmode && ofiles) {
3380 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3382 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3386 for (class = 0; class < nVNODECLASSES; class++) {
3387 register struct VnodeInfo *vip = &vnodeInfo[class];
3388 for (i = 0; i < vip->nVnodes; i++)
3389 if (vip->vnodes[i].name)
3390 free(vip->vnodes[i].name);
3397 /* Set correct resource utilization statistics */
3398 volHeader.filecount = FilesInVolume;
3399 volHeader.diskused = BlocksInVolume;
3401 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3402 if (volHeader.uniquifier < (maxunique + 1)) {
3404 Log("Volume uniquifier is too low; fixed\n");
3405 /* Plus 2,000 in case there are workstations out there with
3406 * cached vnodes that have since been deleted
3408 volHeader.uniquifier = (maxunique + 1 + 2000);
3411 /* Turn off the inUse bit; the volume's been salvaged! */
3412 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3413 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3414 volHeader.inService = 1; /* allow service again */
3415 volHeader.needsCallback = (VolumeChanged != 0);
3416 volHeader.dontSalvage = DONT_SALVAGE;
3419 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3420 assert(nBytes == sizeof(volHeader));
3423 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3424 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3425 FilesInVolume, BlocksInVolume);
3427 IH_RELEASE(vnodeInfo[vSmall].handle);
3428 IH_RELEASE(vnodeInfo[vLarge].handle);
3434 ClearROInUseBit(struct VolumeSummary *summary)
3436 IHandle_t *h = summary->volumeInfoHandle;
3437 afs_sfsize_t nBytes;
3439 VolumeDiskData volHeader;
3441 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3442 assert(nBytes == sizeof(volHeader));
3443 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3444 volHeader.inUse = 0;
3445 volHeader.needsSalvaged = 0;
3446 volHeader.inService = 1;
3447 volHeader.dontSalvage = DONT_SALVAGE;
3449 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3450 assert(nBytes == sizeof(volHeader));
3455 * Possible delete the volume.
3457 * deleteMe - Always do so, only a partial volume.
3460 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3463 if (readOnly(isp) || deleteMe) {
3464 if (isp->volSummary && isp->volSummary->fileName) {
3467 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);
3469 Log("It will be deleted on this server (you may find it elsewhere)\n");
3472 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3474 Log("it will be deleted instead. It should be recloned.\n");
3479 sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
3481 code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
3483 Log("Error %ld destroying volume disk header for volume %lu\n",
3484 afs_printable_int32_ld(code),
3485 afs_printable_uint32_lu(isp->volumeId));
3488 /* make sure we actually delete the fileName file; ENOENT
3489 * is fine, since VDestroyVolumeDiskHeader probably already
3491 if (unlink(path) && errno != ENOENT) {
3492 Log("Unable to unlink %s (errno = %d)\n", path, errno);
3496 } else if (!check) {
3497 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3499 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3505 AskOffline(VolumeId volumeId, char * partition)
3510 memset(&res, 0, sizeof(res));
3512 for (i = 0; i < 3; i++) {
3513 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
3515 if (code == SYNC_OK) {
3517 } else if (code == SYNC_DENIED) {
3518 #ifdef DEMAND_ATTACH_ENABLE
3519 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
3521 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3523 Abort("Salvage aborted\n");
3524 } else if (code == SYNC_BAD_COMMAND) {
3525 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
3527 #ifdef DEMAND_ATTACH_ENABLE
3528 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3530 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3532 Abort("Salvage aborted\n");
3535 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
3536 FSYNC_clientFinis();
3540 if (code != SYNC_OK) {
3541 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
3542 Abort("Salvage aborted\n");
3545 #ifdef AFS_DEMAND_ATTACH_FS
3546 /* set inUse = programType in the volume header. We do this in case
3547 * the fileserver restarts/crashes while we are salvaging.
3548 * Otherwise, the fileserver could attach the volume again on
3549 * startup while we are salvaging, which would be very bad, or
3550 * schedule another salvage while we are salvaging, which would be
3554 struct VolumeHeader header;
3555 struct VolumeDiskHeader diskHeader;
3556 struct VolumeDiskData volHeader;
3558 code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
3563 DiskToVolumeHeader(&header, &diskHeader);
3565 IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
3566 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
3567 volHeader.stamp.magic != VOLUMEINFOMAGIC) {
3573 volHeader.inUse = programType;
3575 /* If we can't re-write the header, bail out and error. We don't
3576 * assert when reading the header, since it's possible the
3577 * header isn't really there (when there's no data associated
3578 * with the volume; we just delete the vol header file in that
3579 * case). But if it's there enough that we can read it, but
3580 * somehow we cannot write to it to signify we're salvaging it,
3581 * we've got a big problem and we cannot continue. */
3582 assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
3586 #endif /* AFS_DEMAND_ATTACH_FS */
3590 AskOnline(VolumeId volumeId, char *partition)
3594 for (i = 0; i < 3; i++) {
3595 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
3597 if (code == SYNC_OK) {
3599 } else if (code == SYNC_DENIED) {
3600 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
3601 } else if (code == SYNC_BAD_COMMAND) {
3602 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
3604 #ifdef DEMAND_ATTACH_ENABLE
3605 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
3607 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
3612 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
3613 FSYNC_clientFinis();
3620 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3622 /* Volume parameter is passed in case iopen is upgraded in future to
3623 * require a volume Id to be passed
3626 IHandle_t *srcH, *destH;
3627 FdHandle_t *srcFdP, *destFdP;
3630 IH_INIT(srcH, device, rwvolume, inode1);
3631 srcFdP = IH_OPEN(srcH);
3632 assert(srcFdP != NULL);
3633 IH_INIT(destH, device, rwvolume, inode2);
3634 destFdP = IH_OPEN(destH);
3636 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3637 assert(FDH_WRITE(destFdP, buf, n) == n);
3639 FDH_REALLYCLOSE(srcFdP);
3640 FDH_REALLYCLOSE(destFdP);
3647 PrintInodeList(void)
3649 register struct ViceInodeInfo *ip;
3650 struct ViceInodeInfo *buf;
3651 struct afs_stat status;
3652 register int nInodes;
3654 assert(afs_fstat(inodeFd, &status) == 0);
3655 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3656 assert(buf != NULL);
3657 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3658 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3659 for (ip = buf; nInodes--; ip++) {
3660 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3661 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3662 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3663 ip->u.param[2], ip->u.param[3]);
3669 PrintInodeSummary(void)
3672 struct InodeSummary *isp;
3674 for (i = 0; i < nVolumesInInodeFile; i++) {
3675 isp = &inodeSummary[i];
3676 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);
3681 PrintVolumeSummary(void)
3684 struct VolumeSummary *vsp;
3686 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3687 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3697 assert(0); /* Fork is never executed in the NT code path */
3701 #ifdef AFS_DEMAND_ATTACH_FS
3702 if ((f == 0) && (programType == salvageServer)) {
3703 /* we are a salvageserver child */
3704 #ifdef FSSYNC_BUILD_CLIENT
3705 VChildProcReconnectFS_r();
3707 #ifdef SALVSYNC_BUILD_CLIENT
3711 #endif /* AFS_DEMAND_ATTACH_FS */
3712 #endif /* !AFS_NT40_ENV */
3722 #ifdef AFS_DEMAND_ATTACH_FS
3723 if (programType == salvageServer) {
3724 #ifdef SALVSYNC_BUILD_CLIENT
3727 #ifdef FSSYNC_BUILD_CLIENT
3731 #endif /* AFS_DEMAND_ATTACH_FS */
3734 if (main_thread != pthread_self())
3735 pthread_exit((void *)code);
3748 pid = wait(&status);
3750 if (WCOREDUMP(status))
3751 Log("\"%s\" core dumped!\n", prog);
3752 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3758 TimeStamp(time_t clock, int precision)
3761 static char timestamp[20];
3762 lt = localtime(&clock);
3764 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
3766 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3771 CheckLogFile(char * log_path)
3773 char oldSlvgLog[AFSDIR_PATH_MAX];
3775 #ifndef AFS_NT40_ENV
3782 strcpy(oldSlvgLog, log_path);
3783 strcat(oldSlvgLog, ".old");
3785 renamefile(log_path, oldSlvgLog);
3786 logFile = afs_fopen(log_path, "a");
3788 if (!logFile) { /* still nothing, use stdout */
3792 #ifndef AFS_NAMEI_ENV
3793 AFS_DEBUG_IOPS_LOG(logFile);
3798 #ifndef AFS_NT40_ENV
3800 TimeStampLogFile(char * log_path)
3802 char stampSlvgLog[AFSDIR_PATH_MAX];
3807 lt = localtime(&now);
3808 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3809 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3810 log_path, lt->tm_year + 1900,
3811 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3814 /* try to link the logfile to a timestamped filename */
3815 /* if it fails, oh well, nothing we can do */
3816 link(log_path, stampSlvgLog);
3825 #ifndef AFS_NT40_ENV
3827 printf("Can't show log since using syslog.\n");
3838 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3841 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3844 while (fgets(line, sizeof(line), logFile))
3851 Log(const char *format, ...)
3857 va_start(args, format);
3858 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3860 #ifndef AFS_NT40_ENV
3862 syslog(LOG_INFO, "%s", tmp);
3866 gettimeofday(&now, 0);
3867 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3873 Abort(const char *format, ...)
3878 va_start(args, format);
3879 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3881 #ifndef AFS_NT40_ENV
3883 syslog(LOG_INFO, "%s", tmp);
3887 fprintf(logFile, "%s", tmp);
3902 p = (char *)malloc(strlen(s) + 1);
3908 /* Remove the FORCESALVAGE file */
3910 RemoveTheForce(char *path)
3913 struct afs_stat force; /* so we can use afs_stat to find it */
3914 strcpy(target,path);
3915 strcat(target,"/FORCESALVAGE");
3916 if (!Testing && ForceSalvage) {
3917 if (afs_stat(target,&force) == 0) unlink(target);
3921 #ifndef AFS_AIX32_ENV
3923 * UseTheForceLuke - see if we can use the force
3926 UseTheForceLuke(char *path)
3928 struct afs_stat force;
3930 strcpy(target,path);
3931 strcat(target,"/FORCESALVAGE");
3933 return (afs_stat(target, &force) == 0);
3937 * UseTheForceLuke - see if we can use the force
3940 * The VRMIX fsck will not muck with the filesystem it is supposedly
3941 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
3942 * muck directly with the root inode, which is within the normal
3944 * ListViceInodes() has a side effect of setting ForceSalvage if
3945 * it detects a need, based on root inode examination.
3948 UseTheForceLuke(char *path)
3951 return 0; /* sorry OB1 */
3956 /* NT support routines */
3958 static char execpathname[MAX_PATH];
3960 nt_SalvagePartition(char *partName, int jobn)
3965 if (!*execpathname) {
3966 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3967 if (!n || n == 1023)
3970 job.cj_magic = SALVAGER_MAGIC;
3971 job.cj_number = jobn;
3972 (void)strcpy(job.cj_part, partName);
3973 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3978 nt_SetupPartitionSalvage(void *datap, int len)
3980 childJob_t *jobp = (childJob_t *) datap;
3981 char logname[AFSDIR_PATH_MAX];
3983 if (len != sizeof(childJob_t))
3985 if (jobp->cj_magic != SALVAGER_MAGIC)
3990 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3992 logFile = afs_fopen(logname, "w");
4000 #endif /* AFS_NT40_ENV */