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 "volume_inline.h"
187 #include "salvsync.h"
188 #include "viceinode.h"
190 #include "volinodes.h" /* header magic number, etc. stuff */
191 #include "vol-salvage.h"
193 #include "vol_internal.h"
195 #include <afs/prs_fs.h>
197 #ifdef FSSYNC_BUILD_CLIENT
198 #include "vg_cache.h"
205 /*@+fcnmacros +macrofcndecl@*/
208 extern off64_t afs_lseek(int FD, off64_t O, int F);
209 #endif /*S_SPLINT_S */
210 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
211 #define afs_stat stat64
212 #define afs_fstat fstat64
213 #define afs_open open64
214 #define afs_fopen fopen64
215 #else /* !O_LARGEFILE */
217 extern off_t afs_lseek(int FD, off_t O, int F);
218 #endif /*S_SPLINT_S */
219 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
220 #define afs_stat stat
221 #define afs_fstat fstat
222 #define afs_open open
223 #define afs_fopen fopen
224 #endif /* !O_LARGEFILE */
225 /*@=fcnmacros =macrofcndecl@*/
228 extern void *calloc();
230 static char *TimeStamp(time_t clock, int precision);
233 int debug; /* -d flag */
234 extern int Testing; /* -n flag */
235 int ListInodeOption; /* -i flag */
236 int ShowRootFiles; /* -r flag */
237 int RebuildDirs; /* -sal flag */
238 int Parallel = 4; /* -para X flag */
239 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
240 int forceR = 0; /* -b flag */
241 int ShowLog = 0; /* -showlog flag */
242 int ShowSuid = 0; /* -showsuid flag */
243 int ShowMounts = 0; /* -showmounts flag */
244 int orphans = ORPH_IGNORE; /* -orphans option */
249 int useSyslog = 0; /* -syslog flag */
250 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
259 #define MAXPARALLEL 32
261 int OKToZap; /* -o flag */
262 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
263 * in the volume header */
265 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
267 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
269 Device fileSysDevice; /* The device number of the current
270 * partition being salvaged */
274 char *fileSysPath; /* The path of the mounted partition currently
275 * being salvaged, i.e. the directory
276 * containing the volume headers */
278 char *fileSysPathName; /* NT needs this to make name pretty in log. */
279 IHandle_t *VGLinkH; /* Link handle for current volume group. */
280 int VGLinkH_cnt; /* # of references to lnk handle. */
281 struct DiskPartition64 *fileSysPartition; /* Partition being salvaged */
283 char *fileSysDeviceName; /* The block device where the file system
284 * being salvaged was mounted */
285 char *filesysfulldev;
287 int VolumeChanged; /* Set by any routine which would change the volume in
288 * a way which would require callback is to be broken if the
289 * volume was put back on line by an active file server */
291 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
293 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
294 int inodeFd; /* File descriptor for inode file */
297 struct VnodeInfo vnodeInfo[nVNODECLASSES];
300 struct VolumeSummary *volumeSummaryp = NULL; /* Holds all the volumes in a part */
301 int nVolumes; /* Number of volumes (read-write and read-only)
302 * in volume summary */
308 /* Forward declarations */
309 static int IsVnodeOrphaned(VnodeId vnode);
310 static int AskVolumeSummary(VolumeId singleVolumeNumber);
312 #ifdef AFS_DEMAND_ATTACH_FS
313 static int LockVolume(VolumeId volumeId);
314 #endif /* AFS_DEMAND_ATTACH_FS */
316 /* Uniquifier stored in the Inode */
321 return (u & 0x3fffff);
323 #if defined(AFS_SGI_EXMAG)
324 return (u & SGI_UNIQMASK);
327 #endif /* AFS_SGI_EXMAG */
332 BadError(register int aerror)
334 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
336 return 0; /* otherwise may be transient, e.g. EMFILE */
341 char *save_args[MAX_ARGS];
343 extern pthread_t main_thread;
344 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
348 * Get the salvage lock if not already held. Hold until process exits.
350 * @param[in] locktype READ_LOCK or WRITE_LOCK
353 _ObtainSalvageLock(int locktype)
355 struct VLockFile salvageLock;
360 VLockFileInit(&salvageLock, AFSDIR_SERVER_SLVGLOCK_FILEPATH);
362 code = VLockFileLock(&salvageLock, offset, locktype, nonblock);
365 "salvager: There appears to be another salvager running! "
370 "salvager: Error %d trying to acquire salvage lock! "
376 ObtainSalvageLock(void)
378 _ObtainSalvageLock(WRITE_LOCK);
381 ObtainSharedSalvageLock(void)
383 _ObtainSalvageLock(READ_LOCK);
387 #ifdef AFS_SGI_XFS_IOPS_ENV
388 /* Check if the given partition is mounted. For XFS, the root inode is not a
389 * constant. So we check the hard way.
392 IsPartitionMounted(char *part)
395 struct mntent *mntent;
397 assert(mntfp = setmntent(MOUNTED, "r"));
398 while (mntent = getmntent(mntfp)) {
399 if (!strcmp(part, mntent->mnt_dir))
404 return mntent ? 1 : 1;
407 /* Check if the given inode is the root of the filesystem. */
408 #ifndef AFS_SGI_XFS_IOPS_ENV
410 IsRootInode(struct afs_stat *status)
413 * The root inode is not a fixed value in XFS partitions. So we need to
414 * see if the partition is in the list of mounted partitions. This only
415 * affects the SalvageFileSys path, so we check there.
417 return (status->st_ino == ROOTINODE);
422 #ifndef AFS_NAMEI_ENV
423 /* We don't want to salvage big files filesystems, since we can't put volumes on
427 CheckIfBigFilesFS(char *mountPoint, char *devName)
429 struct superblock fs;
432 if (strncmp(devName, "/dev/", 5)) {
433 (void)sprintf(name, "/dev/%s", devName);
435 (void)strcpy(name, devName);
438 if (ReadSuper(&fs, name) < 0) {
439 Log("Unable to read superblock. Not salvaging partition %s.\n",
443 if (IsBigFilesFileSystem(&fs)) {
444 Log("Partition %s is a big files filesystem, not salvaging.\n",
454 #define HDSTR "\\Device\\Harddisk"
455 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
457 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
462 static int dowarn = 1;
464 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
466 if (strncmp(res, HDSTR, HDLEN)) {
469 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
470 res, HDSTR, p1->devName);
474 d1 = atoi(&res[HDLEN]);
476 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
478 if (strncmp(res, HDSTR, HDLEN)) {
481 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
482 res, HDSTR, p2->devName);
486 d2 = atoi(&res[HDLEN]);
491 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
494 /* This assumes that two partitions with the same device number divided by
495 * PartsPerDisk are on the same disk.
498 SalvageFileSysParallel(struct DiskPartition64 *partP)
501 struct DiskPartition64 *partP;
502 int pid; /* Pid for this job */
503 int jobnumb; /* Log file job number */
504 struct job *nextjob; /* Next partition on disk to salvage */
506 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
507 struct job *thisjob = 0;
508 static int numjobs = 0;
509 static int jobcount = 0;
515 char logFileName[256];
519 /* We have a partition to salvage. Copy it into thisjob */
520 thisjob = (struct job *)malloc(sizeof(struct job));
522 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
525 memset(thisjob, 0, sizeof(struct job));
526 thisjob->partP = partP;
527 thisjob->jobnumb = jobcount;
529 } else if (jobcount == 0) {
530 /* We are asking to wait for all jobs (partp == 0), yet we never
533 Log("No file system partitions named %s* found; not salvaged\n",
534 VICE_PARTITION_PREFIX);
538 if (debug || Parallel == 1) {
540 SalvageFileSys(thisjob->partP, 0);
547 /* Check to see if thisjob is for a disk that we are already
548 * salvaging. If it is, link it in as the next job to do. The
549 * jobs array has 1 entry per disk being salvages. numjobs is
550 * the total number of disks currently being salvaged. In
551 * order to keep thejobs array compact, when a disk is
552 * completed, the hightest element in the jobs array is moved
553 * down to now open slot.
555 for (j = 0; j < numjobs; j++) {
556 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
557 /* On same disk, add it to this list and return */
558 thisjob->nextjob = jobs[j]->nextjob;
559 jobs[j]->nextjob = thisjob;
566 /* Loop until we start thisjob or until all existing jobs are finished */
567 while (thisjob || (!partP && (numjobs > 0))) {
568 startjob = -1; /* No new job to start */
570 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
571 /* Either the max jobs are running or we have to wait for all
572 * the jobs to finish. In either case, we wait for at least one
573 * job to finish. When it's done, clean up after it.
575 pid = wait(&wstatus);
577 for (j = 0; j < numjobs; j++) { /* Find which job it is */
578 if (pid == jobs[j]->pid)
582 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
583 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
586 numjobs--; /* job no longer running */
587 oldjob = jobs[j]; /* remember */
588 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
589 free(oldjob); /* free the old job */
591 /* If there is another partition on the disk to salvage, then
592 * say we will start it (startjob). If not, then put thisjob there
593 * and say we will start it.
595 if (jobs[j]) { /* Another partitions to salvage */
596 startjob = j; /* Will start it */
597 } else { /* There is not another partition to salvage */
599 jobs[j] = thisjob; /* Add thisjob */
601 startjob = j; /* Will start it */
603 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
604 startjob = -1; /* Don't start it - already running */
608 /* We don't have to wait for a job to complete */
610 jobs[numjobs] = thisjob; /* Add this job */
612 startjob = numjobs; /* Will start it */
616 /* Start up a new salvage job on a partition in job slot "startjob" */
617 if (startjob != -1) {
619 Log("Starting salvage of file system partition %s\n",
620 jobs[startjob]->partP->name);
622 /* For NT, we not only fork, but re-exec the salvager. Pass in the
623 * commands and pass the child job number via the data path.
626 nt_SalvagePartition(jobs[startjob]->partP->name,
627 jobs[startjob]->jobnumb);
628 jobs[startjob]->pid = pid;
633 jobs[startjob]->pid = pid;
639 for (fd = 0; fd < 16; fd++)
646 openlog("salvager", LOG_PID, useSyslogFacility);
650 (void)afs_snprintf(logFileName, sizeof logFileName,
652 AFSDIR_SERVER_SLVGLOG_FILEPATH,
653 jobs[startjob]->jobnumb);
654 logFile = afs_fopen(logFileName, "w");
659 SalvageFileSys1(jobs[startjob]->partP, 0);
664 } /* while ( thisjob || (!partP && numjobs > 0) ) */
666 /* If waited for all jobs to complete, now collect log files and return */
668 if (!useSyslog) /* if syslogging - no need to collect */
671 for (i = 0; i < jobcount; i++) {
672 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
673 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
674 if ((passLog = afs_fopen(logFileName, "r"))) {
675 while (fgets(buf, sizeof(buf), passLog)) {
680 (void)unlink(logFileName);
689 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
691 if (!canfork || debug || Fork() == 0) {
692 SalvageFileSys1(partP, singleVolumeNumber);
693 if (canfork && !debug) {
698 Wait("SalvageFileSys");
702 get_DevName(char *pbuffer, char *wpath)
704 char pbuf[128], *ptr;
705 strcpy(pbuf, pbuffer);
706 ptr = (char *)strrchr(pbuf, '/');
712 ptr = (char *)strrchr(pbuffer, '/');
714 strcpy(pbuffer, ptr + 1);
721 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
724 char inodeListPath[256];
725 FILE *inodeFile = NULL;
726 static char tmpDevName[100];
727 static char wpath[100];
728 struct VolumeSummary *vsp, *esp;
739 if (tries > VOL_MAX_CHECKOUT_RETRIES) {
740 Abort("Raced too many times with fileserver restarts while trying to "
741 "checkout/lock volumes; Aborted\n");
743 #ifdef AFS_DEMAND_ATTACH_FS
745 /* unlock all previous volume locks, since we're about to lock them
747 VLockFileReinit(&partP->volLockFile);
749 #endif /* AFS_DEMAND_ATTACH_FS */
751 fileSysPartition = partP;
752 fileSysDevice = fileSysPartition->device;
753 fileSysPathName = VPartitionPath(fileSysPartition);
756 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
757 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
758 name = partP->devName;
760 fileSysPath = fileSysPathName;
761 strcpy(tmpDevName, partP->devName);
762 name = get_DevName(tmpDevName, wpath);
763 fileSysDeviceName = name;
764 filesysfulldev = wpath;
767 if (singleVolumeNumber) {
768 #ifndef AFS_DEMAND_ATTACH_FS
769 /* only non-DAFS locks the partition when salvaging a single volume;
770 * DAFS will lock the individual volumes in the VG */
771 VLockPartition(partP->name);
772 #endif /* !AFS_DEMAND_ATTACH_FS */
776 /* salvageserver already setup fssync conn for us */
777 if ((programType != salvageServer) && !VConnectFS()) {
778 Abort("Couldn't connect to file server\n");
781 AskOffline(singleVolumeNumber, partP->name);
782 #ifdef AFS_DEMAND_ATTACH_FS
783 if (LockVolume(singleVolumeNumber)) {
786 #endif /* AFS_DEMAND_ATTACH_FS */
789 VLockPartition(partP->name);
793 ForceSalvage = UseTheForceLuke(fileSysPath);
796 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
797 partP->name, name, (Testing ? "(READONLY mode)" : ""));
799 Log("***Forced salvage of all volumes on this partition***\n");
804 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
811 assert((dirp = opendir(fileSysPath)) != NULL);
812 while ((dp = readdir(dirp))) {
813 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
814 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
816 Log("Removing old salvager temp files %s\n", dp->d_name);
817 strcpy(npath, fileSysPath);
819 strcat(npath, dp->d_name);
825 tdir = (tmpdir ? tmpdir : fileSysPath);
827 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
828 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
830 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
834 inodeFile = fopen(inodeListPath, "w+b");
836 Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
839 /* Using nt_unlink here since we're really using the delete on close
840 * semantics of unlink. In most places in the salvager, we really do
841 * mean to unlink the file at that point. Those places have been
842 * modified to actually do that so that the NT crt can be used there.
844 code = nt_unlink(inodeListPath);
846 code = unlink(inodeListPath);
849 Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
852 if (GetInodeSummary(inodeFile, singleVolumeNumber) < 0) {
856 inodeFd = fileno(inodeFile);
858 Abort("Temporary file %s is missing...\n", inodeListPath);
859 afs_lseek(inodeFd, 0L, SEEK_SET);
860 if (ListInodeOption) {
864 /* enumerate volumes in the partition.
865 * figure out sets of read-only + rw volumes.
866 * salvage each set, read-only volumes first, then read-write.
867 * Fix up inodes on last volume in set (whether it is read-write
870 if (GetVolumeSummary(singleVolumeNumber)) {
874 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
875 i < nVolumesInInodeFile; i = j) {
876 VolumeId rwvid = inodeSummary[i].RWvolumeId;
878 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
880 VolumeId vid = inodeSummary[j].volumeId;
881 struct VolumeSummary *tsp;
882 /* Scan volume list (from partition root directory) looking for the
883 * current rw volume number in the volume list from the inode scan.
884 * If there is one here that is not in the inode volume list,
886 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
888 DeleteExtraVolumeHeaderFile(vsp);
890 /* Now match up the volume summary info from the root directory with the
891 * entry in the volume list obtained from scanning inodes */
892 inodeSummary[j].volSummary = NULL;
893 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
894 if (tsp->header.id == vid) {
895 inodeSummary[j].volSummary = tsp;
901 /* Salvage the group of volumes (several read-only + 1 read/write)
902 * starting with the current read-only volume we're looking at.
904 SalvageVolumeGroup(&inodeSummary[i], j - i);
907 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
908 for (; vsp < esp; vsp++) {
910 DeleteExtraVolumeHeaderFile(vsp);
913 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
914 RemoveTheForce(fileSysPath);
916 if (!Testing && singleVolumeNumber) {
917 #ifdef AFS_DEMAND_ATTACH_FS
918 /* unlock vol headers so the fs can attach them when we AskOnline */
919 VLockFileReinit(&fileSysPartition->volLockFile);
920 #endif /* AFS_DEMAND_ATTACH_FS */
922 AskOnline(singleVolumeNumber, fileSysPartition->name);
924 /* Step through the volumeSummary list and set all volumes on-line.
925 * The volumes were taken off-line in GetVolumeSummary.
927 for (j = 0; j < nVolumes; j++) {
928 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
932 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
933 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
936 fclose(inodeFile); /* SalvageVolumeGroup was the last which needed it. */
940 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
943 sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
946 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
949 code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
951 Log("Error %ld destroying volume disk header for volume %lu\n",
952 afs_printable_int32_ld(code),
953 afs_printable_uint32_lu(vsp->header.id));
956 /* make sure we actually delete the fileName file; ENOENT
957 * is fine, since VDestroyVolumeDiskHeader probably already
959 if (unlink(path) && errno != ENOENT) {
960 Log("Unable to unlink %s (errno = %d)\n", path, errno);
967 CompareInodes(const void *_p1, const void *_p2)
969 register const struct ViceInodeInfo *p1 = _p1;
970 register const struct ViceInodeInfo *p2 = _p2;
971 if (p1->u.vnode.vnodeNumber == INODESPECIAL
972 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
973 VolumeId p1rwid, p2rwid;
975 (p1->u.vnode.vnodeNumber ==
976 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
978 (p2->u.vnode.vnodeNumber ==
979 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
984 if (p1->u.vnode.vnodeNumber == INODESPECIAL
985 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
986 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
987 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
988 if (p1->u.vnode.volumeId == p1rwid)
990 if (p2->u.vnode.volumeId == p2rwid)
992 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
994 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
995 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
996 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
998 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1000 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1002 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1004 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1006 /* The following tests are reversed, so that the most desirable
1007 * of several similar inodes comes first */
1008 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1009 #ifdef AFS_3DISPARES
1010 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1011 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1014 #ifdef AFS_SGI_EXMAG
1015 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1016 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1021 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1022 #ifdef AFS_3DISPARES
1023 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1024 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1027 #ifdef AFS_SGI_EXMAG
1028 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1029 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1034 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1035 #ifdef AFS_3DISPARES
1036 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1037 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1040 #ifdef AFS_SGI_EXMAG
1041 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1042 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1047 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1048 #ifdef AFS_3DISPARES
1049 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1050 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1053 #ifdef AFS_SGI_EXMAG
1054 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1055 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1064 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1065 register struct InodeSummary *summary)
1067 VolumeId volume = ip->u.vnode.volumeId;
1068 VolumeId rwvolume = volume;
1069 register int n, nSpecial;
1070 register Unique maxunique;
1073 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1075 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1077 rwvolume = ip->u.special.parentId;
1078 /* This isn't quite right, as there could (in error) be different
1079 * parent inodes in different special vnodes */
1081 if (maxunique < ip->u.vnode.vnodeUniquifier)
1082 maxunique = ip->u.vnode.vnodeUniquifier;
1086 summary->volumeId = volume;
1087 summary->RWvolumeId = rwvolume;
1088 summary->nInodes = n;
1089 summary->nSpecialInodes = nSpecial;
1090 summary->maxUniquifier = maxunique;
1094 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1096 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1097 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1098 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1103 * Collect list of inodes in file named by path. If a truly fatal error,
1104 * unlink the file and abort. For lessor errors, return -1. The file will
1105 * be unlinked by the caller.
1108 GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
1110 struct afs_stat status;
1113 struct ViceInodeInfo *ip, *ip_save;
1114 struct InodeSummary summary;
1115 char summaryFileName[50];
1118 char *dev = fileSysPath;
1119 char *wpath = fileSysPath;
1121 char *dev = fileSysDeviceName;
1122 char *wpath = filesysfulldev;
1124 char *part = fileSysPath;
1127 /* This file used to come from vfsck; cobble it up ourselves now... */
1129 ListViceInodes(dev, fileSysPath, inodeFile,
1130 singleVolumeNumber ? OnlyOneVolume : 0,
1131 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1133 Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1136 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1138 if (forceSal && !ForceSalvage) {
1139 Log("***Forced salvage of all volumes on this partition***\n");
1142 fseek(inodeFile, 0L, SEEK_SET);
1143 inodeFd = fileno(inodeFile);
1144 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1145 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1147 tdir = (tmpdir ? tmpdir : part);
1149 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1150 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1152 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1153 "%s/salvage.temp.%d", tdir, getpid());
1155 summaryFile = afs_fopen(summaryFileName, "a+");
1156 if (summaryFile == NULL) {
1157 Abort("Unable to create inode summary file\n");
1161 /* Using nt_unlink here since we're really using the delete on close
1162 * semantics of unlink. In most places in the salvager, we really do
1163 * mean to unlink the file at that point. Those places have been
1164 * modified to actually do that so that the NT crt can be used there.
1166 code = nt_unlink(summaryFileName);
1168 code = unlink(summaryFileName);
1171 Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1174 if (!canfork || debug || Fork() == 0) {
1176 unsigned long st_size=(unsigned long) status.st_size;
1177 nInodes = st_size / sizeof(struct ViceInodeInfo);
1179 fclose(summaryFile);
1180 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1181 RemoveTheForce(fileSysPath);
1183 struct VolumeSummary *vsp;
1186 GetVolumeSummary(singleVolumeNumber);
1188 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1190 DeleteExtraVolumeHeaderFile(vsp);
1193 Log("%s vice inodes on %s; not salvaged\n",
1194 singleVolumeNumber ? "No applicable" : "No", dev);
1197 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1199 fclose(summaryFile);
1201 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1204 if (read(inodeFd, ip, st_size) != st_size) {
1205 fclose(summaryFile);
1206 Abort("Unable to read inode table; %s not salvaged\n", dev);
1208 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1209 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1210 || write(inodeFd, ip, st_size) != st_size) {
1211 fclose(summaryFile);
1212 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1217 CountVolumeInodes(ip, nInodes, &summary);
1218 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1219 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1220 fclose(summaryFile);
1223 summary.index += (summary.nInodes);
1224 nInodes -= summary.nInodes;
1225 ip += summary.nInodes;
1228 ip = ip_save = NULL;
1229 /* Following fflush is not fclose, because if it was debug mode would not work */
1230 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1231 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1232 fclose(summaryFile);
1235 if (canfork && !debug) {
1240 if (Wait("Inode summary") == -1) {
1241 fclose(summaryFile);
1242 Exit(1); /* salvage of this partition aborted */
1245 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1246 if (status.st_size != 0) {
1248 unsigned long st_status=(unsigned long)status.st_size;
1249 inodeSummary = (struct InodeSummary *)malloc(st_status);
1250 assert(inodeSummary != NULL);
1251 /* For GNU we need to do lseek to get the file pointer moved. */
1252 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1253 ret = read(fileno(summaryFile), inodeSummary, st_status);
1254 assert(ret == st_status);
1256 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1257 Log("%d nVolumesInInodeFile %lu \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1258 fclose(summaryFile);
1262 /* Comparison routine for volume sort.
1263 This is setup so that a read-write volume comes immediately before
1264 any read-only clones of that volume */
1266 CompareVolumes(const void *_p1, const void *_p2)
1268 register const struct VolumeSummary *p1 = _p1;
1269 register const struct VolumeSummary *p2 = _p2;
1270 if (p1->header.parent != p2->header.parent)
1271 return p1->header.parent < p2->header.parent ? -1 : 1;
1272 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1274 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1276 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1280 * Gleans volumeSummary information by asking the fileserver
1282 * @param[in] singleVolumeNumber the volume we're salvaging. 0 if we're
1283 * salvaging a whole partition
1285 * @return whether we obtained the volume summary information or not
1286 * @retval 0 success; we obtained the volume summary information
1287 * @retval -1 we raced with a fileserver restart; volume locks and checkout
1289 * @retval 1 we did not get the volume summary information; either the
1290 * fileserver responded with an error, or we are not supposed to
1291 * ask the fileserver for the information (e.g. we are salvaging
1292 * the entire partition or we are not the salvageserver)
1294 * @note for non-DAFS, always returns 1
1297 AskVolumeSummary(VolumeId singleVolumeNumber)
1300 #if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
1301 if (programType == salvageServer) {
1302 if (singleVolumeNumber) {
1303 FSSYNC_VGQry_response_t q_res;
1305 struct VolumeSummary *vsp;
1307 struct VolumeDiskHeader diskHdr;
1309 memset(&res, 0, sizeof(res));
1311 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1314 * We must wait for the partition to finish scanning before
1315 * can continue, since we will not know if we got the entire
1316 * VG membership unless the partition is fully scanned.
1317 * We could, in theory, just scan the partition ourselves if
1318 * the VG cache is not ready, but we would be doing the exact
1319 * same scan the fileserver is doing; it will almost always
1320 * be faster to wait for the fileserver. The only exceptions
1321 * are if the partition does not take very long to scan, and
1322 * in that case it's fast either way, so who cares?
1324 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1325 Log("waiting for fileserver to finish scanning partition %s...\n",
1326 fileSysPartition->name);
1328 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1329 /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1330 * just so small partitions don't need to wait over 10
1331 * seconds every time, and large partitions are generally
1332 * polled only once every ten seconds. */
1333 sleep((i > 10) ? (i = 10) : i);
1335 code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1339 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1340 /* This can happen if there's no header for the volume
1341 * we're salvaging, or no headers exist for the VG (if
1342 * we're salvaging an RW). Act as if we got a response
1343 * with no VG members. The headers may be created during
1344 * salvaging, if there are inodes in this VG. */
1346 memset(&q_res, 0, sizeof(q_res));
1347 q_res.rw = singleVolumeNumber;
1351 Log("fileserver refused VGCQuery request for volume %lu on "
1352 "partition %s, code %ld reason %ld\n",
1353 afs_printable_uint32_lu(singleVolumeNumber),
1354 fileSysPartition->name,
1355 afs_printable_int32_ld(code),
1356 afs_printable_int32_ld(res.hdr.reason));
1360 if (q_res.rw != singleVolumeNumber) {
1361 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1362 afs_printable_uint32_lu(singleVolumeNumber),
1363 afs_printable_uint32_lu(q_res.rw));
1364 #ifdef SALVSYNC_BUILD_CLIENT
1365 if (SALVSYNC_LinkVolume(q_res.rw,
1367 fileSysPartition->name,
1369 Log("schedule request failed\n");
1371 #endif /* SALVSYNC_BUILD_CLIENT */
1372 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1375 volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
1376 assert(volumeSummaryp != NULL);
1379 vsp = volumeSummaryp;
1381 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1382 char name[VMAXPATHLEN];
1384 if (!q_res.children[i]) {
1388 /* AskOffline for singleVolumeNumber was called much earlier */
1389 if (q_res.children[i] != singleVolumeNumber) {
1390 AskOffline(q_res.children[i], fileSysPartition->name);
1391 if (LockVolume(q_res.children[i])) {
1397 code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
1399 Log("Cannot read header for %lu; trying to salvage group anyway\n",
1400 afs_printable_uint32_lu(q_res.children[i]));
1405 DiskToVolumeHeader(&vsp->header, &diskHdr);
1406 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1407 vsp->fileName = ToString(name);
1412 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1417 Log("Cannot get volume summary from fileserver; falling back to scanning "
1418 "entire partition\n");
1421 #endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
1426 * count how many volume headers are found by VWalkVolumeHeaders.
1428 * @param[in] dp the disk partition (unused)
1429 * @param[in] name full path to the .vol header (unused)
1430 * @param[in] hdr the header data (unused)
1431 * @param[in] last whether this is the last try or not (unused)
1432 * @param[in] rock actually an afs_int32*; the running count of how many
1433 * volumes we have found
1438 CountHeader(struct DiskPartition64 *dp, const char *name,
1439 struct VolumeDiskHeader *hdr, int last, void *rock)
1441 afs_int32 *nvols = (afs_int32 *)rock;
1447 * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1450 struct SalvageScanParams {
1451 VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1452 * vol id of the VG we're salvaging */
1453 struct VolumeSummary *vsp; /**< ptr to the current volume summary object
1454 * we're filling in */
1455 afs_int32 nVolumes; /**< # of vols we've encountered */
1456 afs_int32 totalVolumes; /**< max # of vols we should encounter (the
1457 * # of vols we've alloc'd memory for) */
1458 int retry; /**< do we need to retry vol lock/checkout? */
1462 * records volume summary info found from VWalkVolumeHeaders.
1464 * Found volumes are also taken offline if they are in the specific volume
1465 * group we are looking for.
1467 * @param[in] dp the disk partition
1468 * @param[in] name full path to the .vol header
1469 * @param[in] hdr the header data
1470 * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1471 * @param[in] rock actually a struct SalvageScanParams*, containing the
1472 * information needed to record the volume summary data
1474 * @return operation status
1476 * @retval -1 volume locking raced with fileserver restart; checking out
1477 * and locking volumes needs to be retried
1478 * @retval 1 volume header is mis-named and should be deleted
1481 RecordHeader(struct DiskPartition64 *dp, const char *name,
1482 struct VolumeDiskHeader *hdr, int last, void *rock)
1484 char nameShouldBe[64];
1485 struct SalvageScanParams *params;
1486 struct VolumeSummary summary;
1487 VolumeId singleVolumeNumber;
1489 params = (struct SalvageScanParams *)rock;
1491 singleVolumeNumber = params->singleVolumeNumber;
1493 DiskToVolumeHeader(&summary.header, hdr);
1495 if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1496 && summary.header.parent != singleVolumeNumber) {
1498 if (programType == salvageServer) {
1499 #ifdef SALVSYNC_BUILD_CLIENT
1500 Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1501 summary.header.id, summary.header.parent);
1502 if (SALVSYNC_LinkVolume(summary.header.parent,
1506 Log("schedule request failed\n");
1509 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1512 Log("%u is a read-only volume; not salvaged\n",
1513 singleVolumeNumber);
1518 if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1519 || summary.header.parent == singleVolumeNumber) {
1521 /* check if the header file is incorrectly named */
1523 const char *base = strrchr(name, '/');
1530 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1531 VFORMAT, afs_printable_uint32_lu(summary.header.id));
1534 if (strcmp(nameShouldBe, base)) {
1535 /* .vol file has wrong name; retry/delete */
1539 if (!badname || last) {
1540 /* only offline the volume if the header is good, or if this is
1541 * the last try looking at it; avoid AskOffline'ing the same vol
1544 if (singleVolumeNumber
1545 && summary.header.id != singleVolumeNumber) {
1546 /* don't offline singleVolumeNumber; we already did that
1549 AskOffline(summary.header.id, fileSysPartition->name);
1551 #ifdef AFS_DEMAND_ATTACH_FS
1553 /* don't lock the volume if the header is bad, since we're
1554 * about to delete it anyway. */
1555 if (LockVolume(summary.header.id)) {
1560 #endif /* AFS_DEMAND_ATTACH_FS */
1564 if (last && !Showmode) {
1565 Log("Volume header file %s is incorrectly named (should be %s "
1566 "not %s); %sdeleted (it will be recreated later, if "
1567 "necessary)\n", name, nameShouldBe, base,
1568 (Testing ? "it would have been " : ""));
1573 summary.fileName = ToString(base);
1576 if (params->nVolumes > params->totalVolumes) {
1577 /* We found more volumes than we found on the first partition walk;
1578 * apparently something created a volume while we were
1579 * partition-salvaging, or we found more than 20 vols when salvaging a
1580 * particular volume. Abort if we detect this, since other programs
1581 * supposed to not touch the partition while it is partition-salvaging,
1582 * and we shouldn't find more than 20 vols in a VG.
1584 Abort("Found %ld vol headers, but should have found at most %ld! "
1585 "Make sure the volserver/fileserver are not running at the "
1586 "same time as a partition salvage\n",
1587 afs_printable_int32_ld(params->nVolumes),
1588 afs_printable_int32_ld(params->totalVolumes));
1591 memcpy(params->vsp, &summary, sizeof(summary));
1599 * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1601 * If the header could not be read in at all, the header is always unlinked.
1602 * If instead RecordHeader said the header was bad (that is, the header file
1603 * is mis-named), we only unlink if we are doing a partition salvage, as
1604 * opposed to salvaging a specific volume group.
1606 * @param[in] dp the disk partition
1607 * @param[in] name full path to the .vol header
1608 * @param[in] hdr header data, or NULL if the header could not be read
1609 * @param[in] rock actually a struct SalvageScanParams*, with some information
1613 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1614 struct VolumeDiskHeader *hdr, void *rock)
1616 struct SalvageScanParams *params;
1619 params = (struct SalvageScanParams *)rock;
1622 /* no header; header is too bogus to read in at all */
1624 Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1630 } else if (!params->singleVolumeNumber) {
1631 /* We were able to read in a header, but RecordHeader said something
1632 * was wrong with it. We only unlink those if we are doing a partition
1639 if (dounlink && unlink(name)) {
1640 Log("Error %d while trying to unlink %s\n", errno, name);
1645 * Populates volumeSummaryp with volume summary information, either by asking
1646 * the fileserver for VG information, or by scanning the /vicepX partition.
1648 * @param[in] singleVolumeNumber the volume ID of the single volume group we
1649 * are salvaging, or 0 if this is a partition
1652 * @return operation status
1654 * @retval -1 we raced with a fileserver restart; checking out and locking
1655 * volumes must be retried
1658 GetVolumeSummary(VolumeId singleVolumeNumber)
1660 afs_int32 nvols = 0;
1661 struct SalvageScanParams params;
1664 code = AskVolumeSummary(singleVolumeNumber);
1666 /* we successfully got the vol information from the fileserver; no
1667 * need to scan the partition */
1671 /* we need to retry volume checkout */
1675 if (!singleVolumeNumber) {
1676 /* Count how many volumes we have in /vicepX */
1677 code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
1680 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1685 nvols = VOL_VG_MAX_VOLS;
1688 volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
1689 assert(volumeSummaryp != NULL);
1691 params.singleVolumeNumber = singleVolumeNumber;
1692 params.vsp = volumeSummaryp;
1693 params.nVolumes = 0;
1694 params.totalVolumes = nvols;
1697 /* walk the partition directory of volume headers and record the info
1698 * about them; unlinking invalid headers */
1699 code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
1700 UnlinkHeader, ¶ms);
1702 /* we apparently need to retry checking-out/locking volumes */
1706 Abort("Failed to get volume header summary\n");
1708 nVolumes = params.nVolumes;
1710 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1716 /* Find the link table. This should be associated with the RW volume or, if
1717 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1720 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1721 struct ViceInodeInfo *allInodes)
1724 struct ViceInodeInfo *ip;
1726 for (i = 0; i < nVols; i++) {
1727 ip = allInodes + isp[i].index;
1728 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1729 if (ip[j].u.special.type == VI_LINKTABLE)
1730 return ip[j].inodeNumber;
1737 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1739 struct versionStamp version;
1742 if (!VALID_INO(ino))
1744 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1745 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1746 if (!VALID_INO(ino))
1748 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1749 isp->RWvolumeId, errno);
1750 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1751 fdP = IH_OPEN(VGLinkH);
1753 Abort("Can't open link table for volume %u (error = %d)\n",
1754 isp->RWvolumeId, errno);
1756 if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1757 Abort("Can't truncate link table for volume %u (error = %d)\n",
1758 isp->RWvolumeId, errno);
1760 version.magic = LINKTABLEMAGIC;
1761 version.version = LINKTABLEVERSION;
1763 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1765 Abort("Can't truncate link table for volume %u (error = %d)\n",
1766 isp->RWvolumeId, errno);
1768 FDH_REALLYCLOSE(fdP);
1770 /* If the volume summary exits (i.e., the V*.vol header file exists),
1771 * then set this inode there as well.
1773 if (isp->volSummary)
1774 isp->volSummary->header.linkTable = ino;
1783 SVGParms_t *parms = (SVGParms_t *) arg;
1784 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1789 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1792 pthread_attr_t tattr;
1796 /* Initialize per volume global variables, even if later code does so */
1800 memset(&VolInfo, 0, sizeof(VolInfo));
1802 parms.svgp_inodeSummaryp = isp;
1803 parms.svgp_count = nVols;
1804 code = pthread_attr_init(&tattr);
1806 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1810 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1812 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1815 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1817 Log("Failed to create thread to salvage volume group %u\n",
1821 (void)pthread_join(tid, NULL);
1823 #endif /* AFS_NT40_ENV */
1826 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1828 struct ViceInodeInfo *inodes, *allInodes, *ip;
1829 int i, totalInodes, size, salvageTo;
1833 int dec_VGLinkH = 0;
1835 FdHandle_t *fdP = NULL;
1838 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1839 && isp->nSpecialInodes > 0);
1840 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1841 if (!ForceSalvage && QuickCheck(isp, nVols))
1844 if (ShowMounts && !haveRWvolume)
1846 if (canfork && !debug && Fork() != 0) {
1847 (void)Wait("Salvage volume group");
1850 for (i = 0, totalInodes = 0; i < nVols; i++)
1851 totalInodes += isp[i].nInodes;
1852 size = totalInodes * sizeof(struct ViceInodeInfo);
1853 inodes = (struct ViceInodeInfo *)malloc(size);
1854 allInodes = inodes - isp->index; /* this would the base of all the inodes
1855 * for the partition, if all the inodes
1856 * had been read into memory */
1858 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1860 assert(read(inodeFd, inodes, size) == size);
1862 /* Don't try to salvage a read write volume if there isn't one on this
1864 salvageTo = haveRWvolume ? 0 : 1;
1866 #ifdef AFS_NAMEI_ENV
1867 ino = FindLinkHandle(isp, nVols, allInodes);
1868 if (VALID_INO(ino)) {
1869 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1870 fdP = IH_OPEN(VGLinkH);
1872 if (!VALID_INO(ino) || fdP == NULL) {
1873 Log("%s link table for volume %u.\n",
1874 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1876 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1879 struct ViceInodeInfo *ip;
1880 CreateLinkTable(isp, ino);
1881 fdP = IH_OPEN(VGLinkH);
1882 /* Sync fake 1 link counts to the link table, now that it exists */
1884 for (i = 0; i < nVols; i++) {
1885 ip = allInodes + isp[i].index;
1886 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
1888 nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1890 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
1898 FDH_REALLYCLOSE(fdP);
1900 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1903 /* Salvage in reverse order--read/write volume last; this way any
1904 * Inodes not referenced by the time we salvage the read/write volume
1905 * can be picked up by the read/write volume */
1906 /* ACTUALLY, that's not done right now--the inodes just vanish */
1907 for (i = nVols - 1; i >= salvageTo; i--) {
1909 struct InodeSummary *lisp = &isp[i];
1910 #ifdef AFS_NAMEI_ENV
1911 /* If only the RO is present on this partition, the link table
1912 * shows up as a RW volume special file. Need to make sure the
1913 * salvager doesn't try to salvage the non-existent RW.
1915 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1916 /* If this only special inode is the link table, continue */
1917 if (inodes->u.special.type == VI_LINKTABLE) {
1924 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1925 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1926 /* Check inodes twice. The second time do things seriously. This
1927 * way the whole RO volume can be deleted, below, if anything goes wrong */
1928 for (check = 1; check >= 0; check--) {
1930 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1932 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1933 if (rw && deleteMe) {
1934 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1935 * volume won't be called */
1941 if (rw && check == 1)
1943 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
1944 MaybeZapVolume(lisp, "Vnode index", 0, check);
1950 /* Fix actual inode counts */
1952 Log("totalInodes %d\n",totalInodes);
1953 for (ip = inodes; totalInodes; ip++, totalInodes--) {
1954 static int TraceBadLinkCounts = 0;
1955 #ifdef AFS_NAMEI_ENV
1956 if (VGLinkH->ih_ino == ip->inodeNumber) {
1957 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1958 VGLinkH_p1 = ip->u.param[0];
1959 continue; /* Deal with this last. */
1962 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1963 TraceBadLinkCounts--; /* Limit reports, per volume */
1964 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]);
1966 while (ip->linkCount > 0) {
1967 /* below used to assert, not break */
1969 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1970 Log("idec failed. inode %s errno %d\n",
1971 PrintInode(NULL, ip->inodeNumber), errno);
1977 while (ip->linkCount < 0) {
1978 /* these used to be asserts */
1980 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1981 Log("iinc failed. inode %s errno %d\n",
1982 PrintInode(NULL, ip->inodeNumber), errno);
1989 #ifdef AFS_NAMEI_ENV
1990 while (dec_VGLinkH > 0) {
1991 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1992 Log("idec failed on link table, errno = %d\n", errno);
1996 while (dec_VGLinkH < 0) {
1997 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
1998 Log("iinc failed on link table, errno = %d\n", errno);
2005 /* Directory consistency checks on the rw volume */
2007 SalvageVolume(isp, VGLinkH);
2008 IH_RELEASE(VGLinkH);
2010 if (canfork && !debug) {
2017 QuickCheck(register struct InodeSummary *isp, int nVols)
2019 /* Check headers BEFORE forking */
2023 for (i = 0; i < nVols; i++) {
2024 struct VolumeSummary *vs = isp[i].volSummary;
2025 VolumeDiskData volHeader;
2027 /* Don't salvage just because phantom rw volume is there... */
2028 /* (If a read-only volume exists, read/write inodes must also exist) */
2029 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2033 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2034 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2035 == sizeof(volHeader)
2036 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2037 && volHeader.dontSalvage == DONT_SALVAGE
2038 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2039 if (volHeader.inUse != 0) {
2040 volHeader.inUse = 0;
2041 volHeader.inService = 1;
2043 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2044 != sizeof(volHeader)) {
2060 /* SalvageVolumeHeaderFile
2062 * Salvage the top level V*.vol header file. Make sure the special files
2063 * exist and that there are no duplicates.
2065 * Calls SalvageHeader for each possible type of volume special file.
2069 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2070 register struct ViceInodeInfo *inodes, int RW,
2071 int check, int *deleteMe)
2074 register struct ViceInodeInfo *ip;
2075 int allinodesobsolete = 1;
2076 struct VolumeDiskHeader diskHeader;
2077 afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
2080 /* keeps track of special inodes that are probably 'good'; they are
2081 * referenced in the vol header, and are included in the given inodes
2086 } goodspecial[MAXINODETYPE];
2091 memset(goodspecial, 0, sizeof(goodspecial));
2093 skip = malloc(isp->nSpecialInodes * sizeof(*skip));
2095 memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
2097 Log("cannot allocate memory for inode skip array when salvaging "
2098 "volume %lu; not performing duplicate special inode recovery\n",
2099 afs_printable_uint32_lu(isp->volumeId));
2100 /* still try to perform the salvage; the skip array only does anything
2101 * if we detect duplicate special inodes */
2105 * First, look at the special inodes and see if any are referenced by
2106 * the existing volume header. If we find duplicate special inodes, we
2107 * can use this information to use the referenced inode (it's more
2108 * likely to be the 'good' one), and throw away the duplicates.
2110 if (isp->volSummary && skip) {
2111 /* use tempHeader, so we can use the stuff[] array to easily index
2112 * into the isp->volSummary special inodes */
2113 memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2115 for (i = 0; i < isp->nSpecialInodes; i++) {
2116 ip = &inodes[isp->index + i];
2117 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2118 /* will get taken care of in a later loop */
2121 if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2122 goodspecial[ip->u.special.type-1].valid = 1;
2123 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2128 memset(&tempHeader, 0, sizeof(tempHeader));
2129 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2130 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2131 tempHeader.id = isp->volumeId;
2132 tempHeader.parent = isp->RWvolumeId;
2134 /* Check for duplicates (inodes are sorted by type field) */
2135 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2136 ip = &inodes[isp->index + i];
2137 if (ip->u.special.type == (ip + 1)->u.special.type) {
2138 afs_ino_str_t stmp1, stmp2;
2140 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2141 /* Will be caught in the loop below */
2145 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2146 ip->u.special.type, isp->volumeId,
2147 PrintInode(stmp1, ip->inodeNumber),
2148 PrintInode(stmp2, (ip+1)->inodeNumber));
2150 if (skip && goodspecial[ip->u.special.type-1].valid) {
2151 Inode gi = goodspecial[ip->u.special.type-1].inode;
2154 Log("using special inode referenced by vol header (%s)\n",
2155 PrintInode(stmp1, gi));
2158 /* the volume header references some special inode of
2159 * this type in the inodes array; are we it? */
2160 if (ip->inodeNumber != gi) {
2162 } else if ((ip+1)->inodeNumber != gi) {
2163 /* in case this is the last iteration; we need to
2164 * make sure we check ip+1, too */
2169 Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2177 for (i = 0; i < isp->nSpecialInodes; i++) {
2178 ip = &inodes[isp->index + i];
2179 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2181 Log("Rubbish header inode %s of type %d\n",
2182 PrintInode(NULL, ip->inodeNumber),
2183 ip->u.special.type);
2189 Log("Rubbish header inode %s of type %d; deleted\n",
2190 PrintInode(NULL, ip->inodeNumber),
2191 ip->u.special.type);
2192 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2193 if (skip && skip[i]) {
2194 if (orphans == ORPH_REMOVE) {
2195 Log("Removing orphan special inode %s of type %d\n",
2196 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2199 Log("Ignoring orphan special inode %s of type %d\n",
2200 PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
2201 /* fall through to the ip->linkCount--; line below */
2204 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2205 allinodesobsolete = 0;
2207 if (!check && ip->u.special.type != VI_LINKTABLE)
2208 ip->linkCount--; /* Keep the inode around */
2216 if (allinodesobsolete) {
2223 VGLinkH_cnt++; /* one for every header. */
2225 if (!RW && !check && isp->volSummary) {
2226 ClearROInUseBit(isp->volSummary);
2230 for (i = 0; i < MAXINODETYPE; i++) {
2231 if (stuff[i].inodeType == VI_LINKTABLE) {
2232 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2233 * And we may have recreated the link table earlier, so set the
2234 * RW header as well.
2236 if (VALID_INO(VGLinkH->ih_ino)) {
2237 *stuff[i].inode = VGLinkH->ih_ino;
2241 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2245 if (isp->volSummary == NULL) {
2247 char headerName[64];
2248 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2249 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2251 Log("No header file for volume %u\n", isp->volumeId);
2255 Log("No header file for volume %u; %screating %s\n",
2256 isp->volumeId, (Testing ? "it would have been " : ""),
2258 isp->volSummary = (struct VolumeSummary *)
2259 malloc(sizeof(struct VolumeSummary));
2260 isp->volSummary->fileName = ToString(headerName);
2262 writefunc = VCreateVolumeDiskHeader;
2265 char headerName[64];
2266 /* hack: these two fields are obsolete... */
2267 isp->volSummary->header.volumeAcl = 0;
2268 isp->volSummary->header.volumeMountTable = 0;
2271 (&isp->volSummary->header, &tempHeader,
2272 sizeof(struct VolumeHeader))) {
2273 /* We often remove the name before calling us, so we make a fake one up */
2274 if (isp->volSummary->fileName) {
2275 strcpy(headerName, isp->volSummary->fileName);
2277 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2278 isp->volSummary->fileName = ToString(headerName);
2280 (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
2282 Log("Header file %s is damaged or no longer valid%s\n", path,
2283 (check ? "" : "; repairing"));
2287 writefunc = VWriteVolumeDiskHeader;
2291 memcpy(&isp->volSummary->header, &tempHeader,
2292 sizeof(struct VolumeHeader));
2295 Log("It would have written a new header file for volume %u\n",
2299 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2300 code = (*writefunc)(&diskHeader, fileSysPartition);
2302 Log("Error %ld writing volume header file for volume %lu\n",
2303 afs_printable_int32_ld(code),
2304 afs_printable_uint32_lu(diskHeader.id));
2309 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2310 isp->volSummary->header.volumeInfo);
2315 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2319 VolumeDiskData volumeInfo;
2320 struct versionStamp fileHeader;
2329 #ifndef AFS_NAMEI_ENV
2330 if (sp->inodeType == VI_LINKTABLE)
2333 if (*(sp->inode) == 0) {
2335 Log("Missing inode in volume header (%s)\n", sp->description);
2339 Log("Missing inode in volume header (%s); %s\n", sp->description,
2340 (Testing ? "it would have recreated it" : "recreating"));
2343 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2344 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2345 if (!VALID_INO(*(sp->inode)))
2347 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2348 sp->description, errno);
2353 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2354 fdP = IH_OPEN(specH);
2355 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2356 /* bail out early and destroy the volume */
2358 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2365 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2366 sp->description, errno);
2369 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2370 || header.fileHeader.magic != sp->stamp.magic)) {
2372 Log("Part of the header (%s) is corrupted\n", sp->description);
2373 FDH_REALLYCLOSE(fdP);
2377 Log("Part of the header (%s) is corrupted; recreating\n",
2380 /* header can be garbage; make sure we don't read garbage data from
2382 memset(&header, 0, sizeof(header));
2384 if (sp->inodeType == VI_VOLINFO
2385 && header.volumeInfo.destroyMe == DESTROY_ME) {
2388 FDH_REALLYCLOSE(fdP);
2392 if (recreate && !Testing) {
2395 ("Internal error: recreating volume header (%s) in check mode\n",
2397 nBytes = FDH_TRUNC(fdP, 0);
2399 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2400 sp->description, errno);
2402 /* The following code should be moved into vutil.c */
2403 if (sp->inodeType == VI_VOLINFO) {
2405 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2406 header.volumeInfo.stamp = sp->stamp;
2407 header.volumeInfo.id = isp->volumeId;
2408 header.volumeInfo.parentId = isp->RWvolumeId;
2409 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2410 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2411 isp->volumeId, isp->volumeId);
2412 header.volumeInfo.inService = 0;
2413 header.volumeInfo.blessed = 0;
2414 /* The + 1000 is a hack in case there are any files out in venus caches */
2415 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2416 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2417 header.volumeInfo.needsCallback = 0;
2418 gettimeofday(&tp, 0);
2419 header.volumeInfo.creationDate = tp.tv_sec;
2420 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2422 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2423 sp->description, errno);
2426 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2427 sizeof(header.volumeInfo));
2428 if (nBytes != sizeof(header.volumeInfo)) {
2431 ("Unable to write volume header file (%s) (errno = %d)\n",
2432 sp->description, errno);
2433 Abort("Unable to write entire volume header file (%s)\n",
2437 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2439 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2440 sp->description, errno);
2442 nBytes = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2443 if (nBytes != sizeof(sp->stamp)) {
2446 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2447 sp->description, errno);
2449 ("Unable to write entire version stamp in volume header file (%s)\n",
2454 FDH_REALLYCLOSE(fdP);
2456 if (sp->inodeType == VI_VOLINFO) {
2457 VolInfo = header.volumeInfo;
2460 if (VolInfo.updateDate) {
2461 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2463 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2464 (Testing ? "it would have been " : ""), update);
2466 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2468 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2469 VolInfo.id, update);
2479 SalvageVnodes(register struct InodeSummary *rwIsp,
2480 register struct InodeSummary *thisIsp,
2481 register struct ViceInodeInfo *inodes, int check)
2483 int ilarge, ismall, ioffset, RW, nInodes;
2484 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2487 RW = (rwIsp == thisIsp);
2488 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2490 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2491 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2492 if (check && ismall == -1)
2495 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2496 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2497 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2501 SalvageIndex(Inode ino, VnodeClass class, int RW,
2502 register struct ViceInodeInfo *ip, int nInodes,
2503 struct VolumeSummary *volSummary, int check)
2505 VolumeId volumeNumber;
2506 char buf[SIZEOF_LARGEDISKVNODE];
2507 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2509 StreamHandle_t *file;
2510 struct VnodeClassInfo *vcp;
2512 afs_sfsize_t nVnodes;
2513 afs_fsize_t vnodeLength;
2515 afs_ino_str_t stmp1, stmp2;
2519 volumeNumber = volSummary->header.id;
2520 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2521 fdP = IH_OPEN(handle);
2522 assert(fdP != NULL);
2523 file = FDH_FDOPEN(fdP, "r+");
2524 assert(file != NULL);
2525 vcp = &VnodeClassInfo[class];
2526 size = OS_SIZE(fdP->fd_fd);
2528 nVnodes = (size / vcp->diskSize) - 1;
2530 assert((nVnodes + 1) * vcp->diskSize == size);
2531 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2535 for (vnodeIndex = 0;
2536 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2537 nVnodes--, vnodeIndex++) {
2538 if (vnode->type != vNull) {
2539 int vnodeChanged = 0;
2540 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2541 /* Log programs that belong to root (potentially suid root);
2542 * don't bother for read-only or backup volumes */
2543 #ifdef notdef /* This is done elsewhere */
2544 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2545 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);
2547 if (VNDISK_GET_INO(vnode) == 0) {
2549 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2550 memset(vnode, 0, vcp->diskSize);
2554 if (vcp->magic != vnode->vnodeMagic) {
2555 /* bad magic #, probably partially created vnode */
2556 Log("Partially allocated vnode %d deleted.\n",
2558 memset(vnode, 0, vcp->diskSize);
2562 /* ****** Should do a bit more salvage here: e.g. make sure
2563 * vnode type matches what it should be given the index */
2564 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2565 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2566 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2567 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2574 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2575 /* The following doesn't work, because the version number
2576 * is not maintained correctly by the file server */
2577 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2578 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2580 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2586 /* For RW volume, look for vnode with matching inode number;
2587 * if no such match, take the first determined by our sort
2589 register struct ViceInodeInfo *lip = ip;
2590 register int lnInodes = nInodes;
2592 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2593 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2602 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2603 /* "Matching" inode */
2607 vu = vnode->uniquifier;
2608 iu = ip->u.vnode.vnodeUniquifier;
2609 vd = vnode->dataVersion;
2610 id = ip->u.vnode.inodeDataVersion;
2612 * Because of the possibility of the uniquifier overflows (> 4M)
2613 * we compare them modulo the low 22-bits; we shouldn't worry
2614 * about mismatching since they shouldn't to many old
2615 * uniquifiers of the same vnode...
2617 if (IUnique(vu) != IUnique(iu)) {
2619 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2622 vnode->uniquifier = iu;
2623 #ifdef AFS_3DISPARES
2624 vnode->dataVersion = (id >= vd ?
2627 1887437 ? vd : id) :
2630 1887437 ? id : vd));
2632 #if defined(AFS_SGI_EXMAG)
2633 vnode->dataVersion = (id >= vd ?
2636 15099494 ? vd : id) :
2639 15099494 ? id : vd));
2641 vnode->dataVersion = (id > vd ? id : vd);
2642 #endif /* AFS_SGI_EXMAG */
2643 #endif /* AFS_3DISPARES */
2646 /* don't bother checking for vd > id any more, since
2647 * partial file transfers always result in this state,
2648 * and you can't do much else anyway (you've already
2649 * found the best data you can) */
2650 #ifdef AFS_3DISPARES
2651 if (!vnodeIsDirectory(vnodeNumber)
2652 && ((vd < id && (id - vd) < 1887437)
2653 || ((vd > id && (vd - id) > 1887437)))) {
2655 #if defined(AFS_SGI_EXMAG)
2656 if (!vnodeIsDirectory(vnodeNumber)
2657 && ((vd < id && (id - vd) < 15099494)
2658 || ((vd > id && (vd - id) > 15099494)))) {
2660 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2661 #endif /* AFS_SGI_EXMAG */
2664 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2665 vnode->dataVersion = id;
2670 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2673 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);
2675 VNDISK_SET_INO(vnode, ip->inodeNumber);
2680 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);
2682 VNDISK_SET_INO(vnode, ip->inodeNumber);
2685 VNDISK_GET_LEN(vnodeLength, vnode);
2686 if (ip->byteCount != vnodeLength) {
2689 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2694 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2695 VNDISK_SET_LEN(vnode, ip->byteCount);
2699 ip->linkCount--; /* Keep the inode around */
2702 } else { /* no matching inode */
2703 if (VNDISK_GET_INO(vnode) != 0
2704 || vnode->type == vDirectory) {
2705 /* No matching inode--get rid of the vnode */
2707 if (VNDISK_GET_INO(vnode)) {
2709 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2713 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2718 if (VNDISK_GET_INO(vnode)) {
2720 time_t serverModifyTime = vnode->serverModifyTime;
2721 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));
2725 time_t serverModifyTime = vnode->serverModifyTime;
2726 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2729 memset(vnode, 0, vcp->diskSize);
2732 /* Should not reach here becuase we checked for
2733 * (inodeNumber == 0) above. And where we zero the vnode,
2734 * we also goto vnodeDone.
2738 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2742 } /* VNDISK_GET_INO(vnode) != 0 */
2744 assert(!(vnodeChanged && check));
2745 if (vnodeChanged && !Testing) {
2747 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2748 (char *)vnode, vcp->diskSize)
2750 VolumeChanged = 1; /* For break call back */
2761 struct VnodeEssence *
2762 CheckVnodeNumber(VnodeId vnodeNumber)
2765 struct VnodeInfo *vip;
2768 class = vnodeIdToClass(vnodeNumber);
2769 vip = &vnodeInfo[class];
2770 offset = vnodeIdToBitNumber(vnodeNumber);
2771 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2775 CopyOnWrite(register struct DirSummary *dir)
2777 /* Copy the directory unconditionally if we are going to change it:
2778 * not just if was cloned.
2780 struct VnodeDiskObject vnode;
2781 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2782 Inode oldinode, newinode;
2785 if (dir->copied || Testing)
2787 DFlush(); /* Well justified paranoia... */
2790 IH_IREAD(vnodeInfo[vLarge].handle,
2791 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2793 assert(code == sizeof(vnode));
2794 oldinode = VNDISK_GET_INO(&vnode);
2795 /* Increment the version number by a whole lot to avoid problems with
2796 * clients that were promised new version numbers--but the file server
2797 * crashed before the versions were written to disk.
2800 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2801 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2803 assert(VALID_INO(newinode));
2804 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2806 VNDISK_SET_INO(&vnode, newinode);
2808 IH_IWRITE(vnodeInfo[vLarge].handle,
2809 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2811 assert(code == sizeof(vnode));
2813 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2814 fileSysDevice, newinode);
2815 /* Don't delete the original inode right away, because the directory is
2816 * still being scanned.
2822 * This function should either successfully create a new dir, or give up
2823 * and leave things the way they were. In particular, if it fails to write
2824 * the new dir properly, it should return w/o changing the reference to the
2828 CopyAndSalvage(register struct DirSummary *dir)
2830 struct VnodeDiskObject vnode;
2831 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2832 Inode oldinode, newinode;
2837 afs_int32 parentUnique = 1;
2838 struct VnodeEssence *vnodeEssence;
2843 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2845 IH_IREAD(vnodeInfo[vLarge].handle,
2846 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2848 assert(lcode == sizeof(vnode));
2849 oldinode = VNDISK_GET_INO(&vnode);
2850 /* Increment the version number by a whole lot to avoid problems with
2851 * clients that were promised new version numbers--but the file server
2852 * crashed before the versions were written to disk.
2855 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2856 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2858 assert(VALID_INO(newinode));
2859 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2861 /* Assign . and .. vnode numbers from dir and vnode.parent.
2862 * The uniquifier for . is in the vnode.
2863 * The uniquifier for .. might be set to a bogus value of 1 and
2864 * the salvager will later clean it up.
2866 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2867 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2870 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2872 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2877 /* didn't really build the new directory properly, let's just give up. */
2878 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2879 Log("Directory salvage returned code %d, continuing.\n", code);
2881 Log("also failed to decrement link count on new inode");
2885 Log("Checking the results of the directory salvage...\n");
2886 if (!DirOK(&newdir)) {
2887 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2888 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2893 VNDISK_SET_INO(&vnode, newinode);
2894 length = Length(&newdir);
2895 VNDISK_SET_LEN(&vnode, length);
2897 IH_IWRITE(vnodeInfo[vLarge].handle,
2898 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2900 assert(lcode == sizeof(vnode));
2903 nt_sync(fileSysDevice);
2905 sync(); /* this is slow, but hopefully rarely called. We don't have
2906 * an open FD on the file itself to fsync.
2910 vnodeInfo[vLarge].handle->ih_synced = 1;
2912 /* make sure old directory file is really closed */
2913 fdP = IH_OPEN(dir->dirHandle.dirh_handle);
2914 FDH_REALLYCLOSE(fdP);
2916 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2918 dir->dirHandle = newdir;
2922 JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
2925 struct DirSummary *dir = (struct DirSummary *)dirVal;
2926 struct VnodeEssence *vnodeEssence;
2927 afs_int32 dirOrphaned, todelete;
2929 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2931 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2932 if (vnodeEssence == NULL) {
2934 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2938 assert(Delete(&dir->dirHandle, name) == 0);
2943 #ifndef AFS_NAMEI_ENV
2944 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2945 * mount inode for the partition. If this inode were deleted, it would crash
2948 if (vnodeEssence->InodeNumber == 0) {
2949 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"));
2952 assert(Delete(&dir->dirHandle, name) == 0);
2959 if (!(vnodeNumber & 1) && !Showmode
2960 && !(vnodeEssence->count || vnodeEssence->unique
2961 || vnodeEssence->modeBits)) {
2962 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2963 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2964 vnodeNumber, unique,
2965 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2970 assert(Delete(&dir->dirHandle, name) == 0);
2976 /* Check if the Uniquifiers match. If not, change the directory entry
2977 * so its unique matches the vnode unique. Delete if the unique is zero
2978 * or if the directory is orphaned.
2980 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2981 if (!vnodeEssence->unique
2982 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2983 /* This is an orphaned directory. Don't delete the . or ..
2984 * entry. Otherwise, it will get created in the next
2985 * salvage and deleted again here. So Just skip it.
2990 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2993 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")));
2997 fid.Vnode = vnodeNumber;
2998 fid.Unique = vnodeEssence->unique;
3000 assert(Delete(&dir->dirHandle, name) == 0);
3002 assert(Create(&dir->dirHandle, name, &fid) == 0);
3005 return 0; /* no need to continue */
3008 if (strcmp(name, ".") == 0) {
3009 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
3012 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3015 assert(Delete(&dir->dirHandle, ".") == 0);
3016 fid.Vnode = dir->vnodeNumber;
3017 fid.Unique = dir->unique;
3018 assert(Create(&dir->dirHandle, ".", &fid) == 0);
3021 vnodeNumber = fid.Vnode; /* Get the new Essence */
3022 unique = fid.Unique;
3023 vnodeEssence = CheckVnodeNumber(vnodeNumber);
3026 } else if (strcmp(name, "..") == 0) {
3029 struct VnodeEssence *dotdot;
3030 pa.Vnode = dir->parent;
3031 dotdot = CheckVnodeNumber(pa.Vnode);
3032 assert(dotdot != NULL); /* XXX Should not be assert */
3033 pa.Unique = dotdot->unique;
3035 pa.Vnode = dir->vnodeNumber;
3036 pa.Unique = dir->unique;
3038 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
3040 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3043 assert(Delete(&dir->dirHandle, "..") == 0);
3044 assert(Create(&dir->dirHandle, "..", &pa) == 0);
3047 vnodeNumber = pa.Vnode; /* Get the new Essence */
3049 vnodeEssence = CheckVnodeNumber(vnodeNumber);
3051 dir->haveDotDot = 1;
3052 } else if (strncmp(name, ".__afs", 6) == 0) {
3054 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);
3058 assert(Delete(&dir->dirHandle, name) == 0);
3060 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
3061 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
3064 if (ShowSuid && (vnodeEssence->modeBits & 06000))
3065 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);
3066 if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
3067 && !(vnodeEssence->modeBits & 0111)) {
3074 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3075 vnodeEssence->InodeNumber);
3078 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
3082 size = FDH_SIZE(fdP);
3084 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, (int)size, vnodeNumber);
3085 FDH_REALLYCLOSE(fdP);
3092 nBytes = FDH_READ(fdP, buf, size);
3093 if (nBytes == size) {
3095 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
3096 Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
3097 dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
3098 Testing ? "would convert" : "converted");
3099 vnodeEssence->modeBits |= 0111;
3100 vnodeEssence->changed = 1;
3101 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3102 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3103 dir->name ? dir->name : "??", name, buf);
3105 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3106 dir->vname, vnodeNumber, (int)size, (int)nBytes);
3108 FDH_REALLYCLOSE(fdP);
3111 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3112 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);
3113 if (vnodeIdToClass(vnodeNumber) == vLarge
3114 && vnodeEssence->name == NULL) {
3116 if ((n = (char *)malloc(strlen(name) + 1)))
3118 vnodeEssence->name = n;
3121 /* The directory entry points to the vnode. Check to see if the
3122 * vnode points back to the directory. If not, then let the
3123 * directory claim it (else it might end up orphaned). Vnodes
3124 * already claimed by another directory are deleted from this
3125 * directory: hardlinks to the same vnode are not allowed
3126 * from different directories.
3128 if (vnodeEssence->parent != dir->vnodeNumber) {
3129 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3130 /* Vnode does not point back to this directory.
3131 * Orphaned dirs cannot claim a file (it may belong to
3132 * another non-orphaned dir).
3135 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);
3137 vnodeEssence->parent = dir->vnodeNumber;
3138 vnodeEssence->changed = 1;
3140 /* Vnode was claimed by another directory */
3143 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 " : ""));
3144 } else if (vnodeNumber == 1) {
3145 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 " : ""));
3147 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 " : ""));
3152 assert(Delete(&dir->dirHandle, name) == 0);
3157 /* This directory claims the vnode */
3158 vnodeEssence->claimed = 1;
3160 vnodeEssence->count--;
3165 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3167 register struct VnodeInfo *vip = &vnodeInfo[class];
3168 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3169 char buf[SIZEOF_LARGEDISKVNODE];
3170 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3172 StreamHandle_t *file;
3177 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3178 fdP = IH_OPEN(vip->handle);
3179 assert(fdP != NULL);
3180 file = FDH_FDOPEN(fdP, "r+");
3181 assert(file != NULL);
3182 size = OS_SIZE(fdP->fd_fd);
3184 vip->nVnodes = (size / vcp->diskSize) - 1;
3185 if (vip->nVnodes > 0) {
3186 assert((vip->nVnodes + 1) * vcp->diskSize == size);
3187 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3188 assert((vip->vnodes = (struct VnodeEssence *)
3189 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3190 if (class == vLarge) {
3191 assert((vip->inodes = (Inode *)
3192 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3201 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3202 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3203 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3204 nVnodes--, vnodeIndex++) {
3205 if (vnode->type != vNull) {
3206 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3207 afs_fsize_t vnodeLength;
3208 vip->nAllocatedVnodes++;
3209 vep->count = vnode->linkCount;
3210 VNDISK_GET_LEN(vnodeLength, vnode);
3211 vep->blockCount = nBlocks(vnodeLength);
3212 vip->volumeBlockCount += vep->blockCount;
3213 vep->parent = vnode->parent;
3214 vep->unique = vnode->uniquifier;
3215 if (*maxu < vnode->uniquifier)
3216 *maxu = vnode->uniquifier;
3217 vep->modeBits = vnode->modeBits;
3218 vep->InodeNumber = VNDISK_GET_INO(vnode);
3219 vep->type = vnode->type;
3220 vep->author = vnode->author;
3221 vep->owner = vnode->owner;
3222 vep->group = vnode->group;
3223 if (vnode->type == vDirectory) {
3224 if (class != vLarge) {
3225 VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3226 vip->nAllocatedVnodes--;
3227 memset(vnode, 0, sizeof(vnode));
3228 IH_IWRITE(vnodeInfo[vSmall].handle,
3229 vnodeIndexOffset(vcp, vnodeNumber),
3230 (char *)&vnode, sizeof(vnode));
3233 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3242 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3244 struct VnodeEssence *parentvp;
3250 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3251 && GetDirName(vp->parent, parentvp, path)) {
3253 strcat(path, vp->name);
3259 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3260 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3263 IsVnodeOrphaned(VnodeId vnode)
3265 struct VnodeEssence *vep;
3268 return (1); /* Vnode zero does not exist */
3270 return (0); /* The root dir vnode is always claimed */
3271 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3272 if (!vep || !vep->claimed)
3273 return (1); /* Vnode is not claimed - it is orphaned */
3275 return (IsVnodeOrphaned(vep->parent));
3279 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3280 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3283 static struct DirSummary dir;
3284 static struct DirHandle dirHandle;
3285 struct VnodeEssence *parent;
3286 static char path[MAXPATHLEN];
3289 if (dirVnodeInfo->vnodes[i].salvaged)
3290 return; /* already salvaged */
3293 dirVnodeInfo->vnodes[i].salvaged = 1;
3295 if (dirVnodeInfo->inodes[i] == 0)
3296 return; /* Not allocated to a directory */
3298 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3299 if (dirVnodeInfo->vnodes[i].parent) {
3300 Log("Bad parent, vnode 1; %s...\n",
3301 (Testing ? "skipping" : "salvaging"));
3302 dirVnodeInfo->vnodes[i].parent = 0;
3303 dirVnodeInfo->vnodes[i].changed = 1;
3306 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3307 if (parent && parent->salvaged == 0)
3308 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3309 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3310 rootdir, rootdirfound);
3313 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3314 dir.unique = dirVnodeInfo->vnodes[i].unique;
3317 dir.parent = dirVnodeInfo->vnodes[i].parent;
3318 dir.haveDot = dir.haveDotDot = 0;
3319 dir.ds_linkH = alinkH;
3320 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3321 dirVnodeInfo->inodes[i]);
3323 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3326 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3327 (Testing ? "skipping" : "salvaging"));
3330 CopyAndSalvage(&dir);
3334 dirHandle = dir.dirHandle;
3337 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3338 &dirVnodeInfo->vnodes[i], path);
3341 /* If enumeration failed for random reasons, we will probably delete
3342 * too much stuff, so we guard against this instead.
3344 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3347 /* Delete the old directory if it was copied in order to salvage.
3348 * CopyOnWrite has written the new inode # to the disk, but we still
3349 * have the old one in our local structure here. Thus, we idec the
3353 if (dir.copied && !Testing) {
3354 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3356 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3359 /* Remember rootdir DirSummary _after_ it has been judged */
3360 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3361 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3369 * Get a new FID that can be used to create a new file.
3371 * @param[in] volHeader vol header for the volume
3372 * @param[in] class what type of vnode we'll be creating (vLarge or vSmall)
3373 * @param[out] afid the FID that we can use (only Vnode and Unique are set)
3374 * @param[inout] maxunique max uniquifier for all vnodes in the volume;
3375 * updated to the new max unique if we create a new
3379 GetNewFID(VolumeDiskData *volHeader, VnodeClass class, AFSFid *afid,
3383 for (i = 0; i < vnodeInfo[class].nVnodes; i++) {
3384 if (vnodeInfo[class].vnodes[i].type == vNull) {
3388 if (i == vnodeInfo[class].nVnodes) {
3389 /* no free vnodes; make a new one */
3390 vnodeInfo[class].nVnodes++;
3391 vnodeInfo[class].vnodes = realloc(vnodeInfo[class].vnodes,
3392 sizeof(struct VnodeEssence) * (i+1));
3393 vnodeInfo[class].vnodes[i].type = vNull;
3396 afid->Vnode = bitNumberToVnodeNumber(i, class);
3398 if (volHeader->uniquifier < (*maxunique + 1)) {
3399 /* header uniq is bad; it will get bumped by 2000 later */
3400 afid->Unique = *maxunique + 1 + 2000;
3403 /* header uniq seems okay; just use that */
3404 afid->Unique = *maxunique = volHeader->uniquifier++;
3409 * Create a vnode for a README file explaining not to use a recreated-root vol.
3411 * @param[in] volHeader vol header for the volume
3412 * @param[in] alinkH ihandle for i/o for the volume
3413 * @param[in] vid volume id
3414 * @param[inout] maxunique max uniquifier for all vnodes in the volume;
3415 * updated to the new max unique if we create a new
3417 * @param[out] afid FID for the new readme vnode
3418 * @param[out] ainode the inode for the new readme file
3420 * @return operation status
3425 CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
3426 VolumeId vid, Unique *maxunique, AFSFid *afid, Inode *ainode)
3429 struct VnodeDiskObject *rvnode = NULL;
3431 IHandle_t *readmeH = NULL;
3432 struct VnodeEssence *vep;
3434 time_t now = time(NULL);
3436 /* Try to make the note brief, but informative. Only administrators should
3437 * be able to read this file at first, so we can hopefully assume they
3438 * know what AFS is, what a volume is, etc. */
3440 "This volume has been salvaged, but has lost its original root directory.\n"
3441 "The root directory that exists now has been recreated from orphan files\n"
3442 "from the rest of the volume. This recreated root directory may interfere\n"
3443 "with old cached data on clients, and there is no way the salvager can\n"
3444 "reasonably prevent that. So, it is recommended that you do not continue to\n"
3445 "use this volume, but only copy the salvaged data to a new volume.\n"
3446 "Continuing to use this volume as it exists now may cause some clients to\n"
3447 "behave oddly when accessing this volume.\n"
3448 "\n\t -- Your friendly neighborhood OpenAFS salvager\n";
3449 /* ^ the person reading this probably just lost some data, so they could
3450 * use some cheering up. */
3452 /* -1 for the trailing NUL */
3453 length = sizeof(readme) - 1;
3455 GetNewFID(volHeader, vSmall, afid, maxunique);
3457 vep = &vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
3459 /* create the inode and write the contents */
3460 readmeinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid,
3461 afid->Vnode, afid->Unique, 1);
3462 if (!VALID_INO(readmeinode)) {
3463 Log("CreateReadme: readme IH_CREATE failed\n");
3467 IH_INIT(readmeH, fileSysDevice, vid, readmeinode);
3468 bytes = IH_IWRITE(readmeH, 0, readme, length);
3469 IH_RELEASE(readmeH);
3471 if (bytes != length) {
3472 Log("CreateReadme: IWRITE failed (%d/%d)\n", (int)bytes,
3473 (int)sizeof(readme));
3477 /* create the vnode and write it out */
3478 rvnode = calloc(1, SIZEOF_SMALLDISKVNODE);
3480 Log("CreateRootDir: error alloc'ing memory\n");
3484 rvnode->type = vFile;
3486 rvnode->modeBits = 0777;
3487 rvnode->linkCount = 1;
3488 VNDISK_SET_LEN(rvnode, length);
3489 rvnode->uniquifier = afid->Unique;
3490 rvnode->dataVersion = 1;
3491 VNDISK_SET_INO(rvnode, readmeinode);
3492 rvnode->unixModifyTime = rvnode->serverModifyTime = now;
3497 rvnode->vnodeMagic = VnodeClassInfo[vSmall].magic;
3499 bytes = IH_IWRITE(vnodeInfo[vSmall].handle,
3500 vnodeIndexOffset(&VnodeClassInfo[vSmall], afid->Vnode),
3501 (char*)rvnode, SIZEOF_SMALLDISKVNODE);
3503 if (bytes != SIZEOF_SMALLDISKVNODE) {
3504 Log("CreateReadme: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3505 (int)SIZEOF_SMALLDISKVNODE);
3509 /* update VnodeEssence for new readme vnode */
3510 vnodeInfo[vSmall].nAllocatedVnodes++;
3512 vep->blockCount = nBlocks(length);
3513 vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
3514 vep->parent = rvnode->parent;
3515 vep->unique = rvnode->uniquifier;
3516 vep->modeBits = rvnode->modeBits;
3517 vep->InodeNumber = VNDISK_GET_INO(rvnode);
3518 vep->type = rvnode->type;
3519 vep->author = rvnode->author;
3520 vep->owner = rvnode->owner;
3521 vep->group = rvnode->group;
3531 *ainode = readmeinode;
3536 if (IH_DEC(alinkH, readmeinode, vid)) {
3537 Log("CreateReadme (recovery): IH_DEC failed\n");
3549 * create a root dir for a volume that lacks one.
3551 * @param[in] volHeader vol header for the volume
3552 * @param[in] alinkH ihandle for disk access for this volume group
3553 * @param[in] vid volume id we're dealing with
3554 * @param[out] rootdir populated with info about the new root dir
3555 * @param[inout] maxunique max uniquifier for all vnodes in the volume;
3556 * updated to the new max unique if we create a new
3559 * @return operation status
3564 CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
3565 struct DirSummary *rootdir, Unique *maxunique)
3568 int decroot = 0, decreadme = 0;
3569 AFSFid did, readmeid;
3572 struct VnodeDiskObject *rootvnode = NULL;
3573 struct acl_accessList *ACL;
3576 struct VnodeEssence *vep;
3578 time_t now = time(NULL);
3580 if (!vnodeInfo[vLarge].vnodes && !vnodeInfo[vSmall].vnodes) {
3581 Log("Not creating new root dir; volume appears to lack any vnodes\n");
3585 if (!vnodeInfo[vLarge].vnodes) {
3586 /* We don't have any large vnodes in the volume; allocate room
3587 * for one so we can recreate the root dir */
3588 vnodeInfo[vLarge].nVnodes = 1;
3589 vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
3590 vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
3592 assert(vnodeInfo[vLarge].vnodes);
3593 assert(vnodeInfo[vLarge].inodes);
3596 vep = &vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
3597 ip = &vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
3598 if (vep->type != vNull) {
3599 Log("Not creating new root dir; existing vnode 1 is non-null\n");
3603 if (CreateReadme(volHeader, alinkH, vid, maxunique, &readmeid, &readmeinode)) {
3608 /* set the DV to a very high number, so it is unlikely that we collide
3609 * with a cached DV */
3612 rootinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid, 1, 1, dv);
3613 if (!VALID_INO(rootinode)) {
3614 Log("CreateRootDir: IH_CREATE failed\n");
3619 SetSalvageDirHandle(&rootdir->dirHandle, vid, fileSysDevice, rootinode);
3623 if (MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
3624 Log("CreateRootDir: MakeDir failed\n");
3627 if (Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
3628 Log("CreateRootDir: Create failed\n");
3632 length = Length(&rootdir->dirHandle);
3633 DZap((void *)&rootdir->dirHandle);
3635 /* create the new root dir vnode */
3636 rootvnode = calloc(1, SIZEOF_LARGEDISKVNODE);
3638 Log("CreateRootDir: malloc failed\n");
3642 /* only give 'rl' permissions to 'system:administrators'. We do this to
3643 * try to catch the attention of an administrator, that they should not
3644 * be writing to this directory or continue to use it. */
3645 ACL = VVnodeDiskACL(rootvnode);
3646 ACL->size = sizeof(struct acl_accessList);
3647 ACL->version = ACL_ACLVERSION;
3651 ACL->entries[0].id = -204; /* system:administrators */
3652 ACL->entries[0].rights = PRSFS_READ | PRSFS_LOOKUP;
3654 rootvnode->type = vDirectory;
3655 rootvnode->cloned = 0;
3656 rootvnode->modeBits = 0777;
3657 rootvnode->linkCount = 2;
3658 VNDISK_SET_LEN(rootvnode, length);
3659 rootvnode->uniquifier = 1;
3660 rootvnode->dataVersion = dv;
3661 VNDISK_SET_INO(rootvnode, rootinode);
3662 rootvnode->unixModifyTime = rootvnode->serverModifyTime = now;
3663 rootvnode->author = 0;
3664 rootvnode->owner = 0;
3665 rootvnode->parent = 0;
3666 rootvnode->group = 0;
3667 rootvnode->vnodeMagic = VnodeClassInfo[vLarge].magic;
3669 /* write it out to disk */
3670 bytes = IH_IWRITE(vnodeInfo[vLarge].handle,
3671 vnodeIndexOffset(&VnodeClassInfo[vLarge], 1),
3672 (char*)rootvnode, SIZEOF_LARGEDISKVNODE);
3674 if (bytes != SIZEOF_LARGEDISKVNODE) {
3675 /* just cast to int and don't worry about printing real 64-bit ints;
3676 * a large disk vnode isn't anywhere near the 32-bit limit */
3677 Log("CreateRootDir: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3678 (int)SIZEOF_LARGEDISKVNODE);
3682 /* update VnodeEssence for the new root vnode */
3683 vnodeInfo[vLarge].nAllocatedVnodes++;
3685 vep->blockCount = nBlocks(length);
3686 vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
3687 vep->parent = rootvnode->parent;
3688 vep->unique = rootvnode->uniquifier;
3689 vep->modeBits = rootvnode->modeBits;
3690 vep->InodeNumber = VNDISK_GET_INO(rootvnode);
3691 vep->type = rootvnode->type;
3692 vep->author = rootvnode->author;
3693 vep->owner = rootvnode->owner;
3694 vep->group = rootvnode->group;
3704 /* update DirSummary for the new root vnode */
3705 rootdir->vnodeNumber = 1;
3706 rootdir->unique = 1;
3707 rootdir->haveDot = 1;
3708 rootdir->haveDotDot = 1;
3709 rootdir->rwVid = vid;
3710 rootdir->copied = 0;
3711 rootdir->parent = 0;
3712 rootdir->name = strdup(".");
3713 rootdir->vname = volHeader->name;
3714 rootdir->ds_linkH = alinkH;
3721 if (decroot && IH_DEC(alinkH, rootinode, vid)) {
3722 Log("CreateRootDir (recovery): IH_DEC (root) failed\n");
3724 if (decreadme && IH_DEC(alinkH, readmeinode, vid)) {
3725 Log("CreateRootDir (recovery): IH_DEC (readme) failed\n");
3735 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3737 /* This routine, for now, will only be called for read-write volumes */
3739 int BlocksInVolume = 0, FilesInVolume = 0;
3740 register VnodeClass class;
3741 struct DirSummary rootdir, oldrootdir;
3742 struct VnodeInfo *dirVnodeInfo;
3743 struct VnodeDiskObject vnode;
3744 VolumeDiskData volHeader;
3746 int orphaned, rootdirfound = 0;
3747 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3748 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3749 struct VnodeEssence *vep;
3752 afs_sfsize_t nBytes;
3754 VnodeId LFVnode, ThisVnode;
3755 Unique LFUnique, ThisUnique;
3759 vid = rwIsp->volSummary->header.id;
3760 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3761 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3762 assert(nBytes == sizeof(volHeader));
3763 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3764 assert(volHeader.destroyMe != DESTROY_ME);
3765 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3767 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3769 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3772 dirVnodeInfo = &vnodeInfo[vLarge];
3773 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3774 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3778 nt_sync(fileSysDevice);
3780 sync(); /* This used to be done lower level, for every dir */
3787 if (!rootdirfound && (orphans == ORPH_ATTACH) && !Testing) {
3789 Log("Cannot find root directory for volume %lu; attempting to create "
3790 "a new one\n", afs_printable_uint32_lu(vid));
3792 code = CreateRootDir(&volHeader, alinkH, vid, &rootdir, &maxunique);
3800 /* Parse each vnode looking for orphaned vnodes and
3801 * connect them to the tree as orphaned (if requested).
3803 oldrootdir = rootdir;
3804 for (class = 0; class < nVNODECLASSES; class++) {
3805 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3806 vep = &(vnodeInfo[class].vnodes[v]);
3807 ThisVnode = bitNumberToVnodeNumber(v, class);
3808 ThisUnique = vep->unique;
3810 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3811 continue; /* Ignore unused, claimed, and root vnodes */
3813 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3814 * entry in this vnode had incremented the parent link count (In
3815 * JudgeEntry()). We need to go to the parent and decrement that
3816 * link count. But if the parent's unique is zero, then the parent
3817 * link count was not incremented in JudgeEntry().
3819 if (class == vLarge) { /* directory vnode */
3820 pv = vnodeIdToBitNumber(vep->parent);
3821 if (vnodeInfo[vLarge].vnodes[pv].unique != 0) {
3822 if (vep->parent == 1 && newrootdir) {
3823 /* this vnode's parent was the volume root, and
3824 * we just created the volume root. So, the parent
3825 * dir didn't exist during JudgeEntry, so the link
3826 * count was not inc'd there, so don't dec it here.
3832 vnodeInfo[vLarge].vnodes[pv].count++;
3838 continue; /* If no rootdir, can't attach orphaned files */
3840 /* Here we attach orphaned files and directories into the
3841 * root directory, LVVnode, making sure link counts stay correct.
3843 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3844 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3845 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3847 /* Update this orphaned vnode's info. Its parent info and
3848 * link count (do for orphaned directories and files).
3850 vep->parent = LFVnode; /* Parent is the root dir */
3851 vep->unique = LFUnique;
3854 vep->count--; /* Inc link count (root dir will pt to it) */
3856 /* If this orphaned vnode is a directory, change '..'.
3857 * The name of the orphaned dir/file is unknown, so we
3858 * build a unique name. No need to CopyOnWrite the directory
3859 * since it is not connected to tree in BK or RO volume and
3860 * won't be visible there.
3862 if (class == vLarge) {
3866 /* Remove and recreate the ".." entry in this orphaned directory */
3867 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3868 vnodeInfo[class].inodes[v]);
3870 pa.Unique = LFUnique;
3871 assert(Delete(&dh, "..") == 0);
3872 assert(Create(&dh, "..", &pa) == 0);
3874 /* The original parent's link count was decremented above.
3875 * Here we increment the new parent's link count.
3877 pv = vnodeIdToBitNumber(LFVnode);
3878 vnodeInfo[vLarge].vnodes[pv].count--;
3882 /* Go to the root dir and add this entry. The link count of the
3883 * root dir was incremented when ".." was created. Try 10 times.
3885 for (j = 0; j < 10; j++) {
3886 pa.Vnode = ThisVnode;
3887 pa.Unique = ThisUnique;
3889 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3891 vLarge) ? "__ORPHANDIR__" :
3892 "__ORPHANFILE__"), ThisVnode,
3895 CopyOnWrite(&rootdir);
3896 code = Create(&rootdir.dirHandle, npath, &pa);
3900 ThisUnique += 50; /* Try creating a different file */
3903 Log("Attaching orphaned %s to volume's root dir as %s\n",
3904 ((class == vLarge) ? "directory" : "file"), npath);
3906 } /* for each vnode in the class */
3907 } /* for each class of vnode */
3909 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3911 if (!oldrootdir.copied && rootdir.copied) {
3913 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3916 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3919 DFlush(); /* Flush the changes */
3920 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3921 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3922 orphans = ORPH_IGNORE;
3925 /* Write out all changed vnodes. Orphaned files and directories
3926 * will get removed here also (if requested).
3928 for (class = 0; class < nVNODECLASSES; class++) {
3929 int nVnodes = vnodeInfo[class].nVnodes;
3930 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3931 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3932 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3933 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3934 for (i = 0; i < nVnodes; i++) {
3935 register struct VnodeEssence *vnp = &vnodes[i];
3936 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3938 /* If the vnode is good but is unclaimed (not listed in
3939 * any directory entries), then it is orphaned.
3942 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3943 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3947 if (vnp->changed || vnp->count) {
3951 IH_IREAD(vnodeInfo[class].handle,
3952 vnodeIndexOffset(vcp, vnodeNumber),
3953 (char *)&vnode, sizeof(vnode));
3954 assert(nBytes == sizeof(vnode));
3956 vnode.parent = vnp->parent;
3957 oldCount = vnode.linkCount;
3958 vnode.linkCount = vnode.linkCount - vnp->count;
3961 orphaned = IsVnodeOrphaned(vnodeNumber);
3963 if (!vnp->todelete) {
3964 /* Orphans should have already been attached (if requested) */
3965 assert(orphans != ORPH_ATTACH);
3966 oblocks += vnp->blockCount;
3969 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3971 BlocksInVolume -= vnp->blockCount;
3973 if (VNDISK_GET_INO(&vnode)) {
3975 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3978 memset(&vnode, 0, sizeof(vnode));
3980 } else if (vnp->count) {
3982 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3985 vnode.modeBits = vnp->modeBits;
3988 vnode.dataVersion++;
3991 IH_IWRITE(vnodeInfo[class].handle,
3992 vnodeIndexOffset(vcp, vnodeNumber),
3993 (char *)&vnode, sizeof(vnode));
3994 assert(nBytes == sizeof(vnode));
4000 if (!Showmode && ofiles) {
4001 Log("%s %d orphaned files and directories (approx. %u KB)\n",
4003 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
4007 for (class = 0; class < nVNODECLASSES; class++) {
4008 register struct VnodeInfo *vip = &vnodeInfo[class];
4009 for (i = 0; i < vip->nVnodes; i++)
4010 if (vip->vnodes[i].name)
4011 free(vip->vnodes[i].name);
4018 /* Set correct resource utilization statistics */
4019 volHeader.filecount = FilesInVolume;
4020 volHeader.diskused = BlocksInVolume;
4022 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
4023 if (volHeader.uniquifier < (maxunique + 1)) {
4025 Log("Volume uniquifier is too low; fixed\n");
4026 /* Plus 2,000 in case there are workstations out there with
4027 * cached vnodes that have since been deleted
4029 volHeader.uniquifier = (maxunique + 1 + 2000);
4033 Log("*** WARNING: Root directory recreated, but volume is fragile! "
4034 "Only use this salvaged volume to copy data to another volume; "
4035 "do not continue to use this volume (%lu) as-is.\n",
4036 afs_printable_uint32_lu(vid));
4039 #ifdef FSSYNC_BUILD_CLIENT
4040 if (!Testing && VolumeChanged) {
4041 afs_int32 fsync_code;
4043 fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
4045 Log("Error trying to tell the fileserver to break callbacks for "
4046 "changed volume %lu; error code %ld\n",
4047 afs_printable_uint32_lu(vid),
4048 afs_printable_int32_ld(fsync_code));
4053 #endif /* FSSYNC_BUILD_CLIENT */
4055 /* Turn off the inUse bit; the volume's been salvaged! */
4056 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
4057 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
4058 volHeader.inService = 1; /* allow service again */
4059 volHeader.needsCallback = (VolumeChanged != 0);
4060 volHeader.dontSalvage = DONT_SALVAGE;
4063 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4064 assert(nBytes == sizeof(volHeader));
4067 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
4068 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
4069 FilesInVolume, BlocksInVolume);
4071 IH_RELEASE(vnodeInfo[vSmall].handle);
4072 IH_RELEASE(vnodeInfo[vLarge].handle);
4078 ClearROInUseBit(struct VolumeSummary *summary)
4080 IHandle_t *h = summary->volumeInfoHandle;
4081 afs_sfsize_t nBytes;
4083 VolumeDiskData volHeader;
4085 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
4086 assert(nBytes == sizeof(volHeader));
4087 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
4088 volHeader.inUse = 0;
4089 volHeader.needsSalvaged = 0;
4090 volHeader.inService = 1;
4091 volHeader.dontSalvage = DONT_SALVAGE;
4093 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4094 assert(nBytes == sizeof(volHeader));
4099 * Possible delete the volume.
4101 * deleteMe - Always do so, only a partial volume.
4104 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
4107 if (readOnly(isp) || deleteMe) {
4108 if (isp->volSummary && isp->volSummary->fileName) {
4111 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);
4113 Log("It will be deleted on this server (you may find it elsewhere)\n");
4116 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
4118 Log("it will be deleted instead. It should be recloned.\n");
4123 sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
4125 code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
4127 Log("Error %ld destroying volume disk header for volume %lu\n",
4128 afs_printable_int32_ld(code),
4129 afs_printable_uint32_lu(isp->volumeId));
4132 /* make sure we actually delete the fileName file; ENOENT
4133 * is fine, since VDestroyVolumeDiskHeader probably already
4135 if (unlink(path) && errno != ENOENT) {
4136 Log("Unable to unlink %s (errno = %d)\n", path, errno);
4140 } else if (!check) {
4141 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
4143 Abort("Salvage of volume %u aborted\n", isp->volumeId);
4147 #ifdef AFS_DEMAND_ATTACH_FS
4149 * Locks a volume on disk for salvaging.
4151 * @param[in] volumeId volume ID to lock
4153 * @return operation status
4155 * @retval -1 volume lock raced with a fileserver restart; all volumes must
4156 * checked out and locked again
4161 LockVolume(VolumeId volumeId)
4166 /* should always be WRITE_LOCK, but keep the lock-type logic all
4167 * in one place, in VVolLockType. Params will be ignored, but
4168 * try to provide what we're logically doing. */
4169 locktype = VVolLockType(V_VOLUPD, 1);
4171 code = VLockVolumeByIdNB(volumeId, fileSysPartition, locktype);
4173 if (code == EBUSY) {
4174 Abort("Someone else appears to be using volume %lu; Aborted\n",
4175 afs_printable_uint32_lu(volumeId));
4177 Abort("Error %ld trying to lock volume %lu; Aborted\n",
4178 afs_printable_int32_ld(code),
4179 afs_printable_uint32_lu(volumeId));
4182 code = FSYNC_VerifyCheckout(volumeId, fileSysPathName, FSYNC_VOL_OFF, FSYNC_SALVAGE);
4183 if (code == SYNC_DENIED) {
4184 /* need to retry checking out volumes */
4187 if (code != SYNC_OK) {
4188 Abort("FSYNC_VerifyCheckout failed for volume %lu with code %ld\n",
4189 afs_printable_uint32_lu(volumeId), afs_printable_int32_ld(code));
4192 /* set inUse = programType in the volume header to ensure that nobody
4193 * tries to use this volume again without salvaging, if we somehow crash
4194 * or otherwise exit before finishing the salvage.
4198 struct VolumeHeader header;
4199 struct VolumeDiskHeader diskHeader;
4200 struct VolumeDiskData volHeader;
4202 code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
4207 DiskToVolumeHeader(&header, &diskHeader);
4209 IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
4210 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
4211 volHeader.stamp.magic != VOLUMEINFOMAGIC) {
4217 volHeader.inUse = programType;
4219 /* If we can't re-write the header, bail out and error. We don't
4220 * assert when reading the header, since it's possible the
4221 * header isn't really there (when there's no data associated
4222 * with the volume; we just delete the vol header file in that
4223 * case). But if it's there enough that we can read it, but
4224 * somehow we cannot write to it to signify we're salvaging it,
4225 * we've got a big problem and we cannot continue. */
4226 assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
4233 #endif /* AFS_DEMAND_ATTACH_FS */
4236 AskOffline(VolumeId volumeId, char * partition)
4241 memset(&res, 0, sizeof(res));
4243 for (i = 0; i < 3; i++) {
4244 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
4246 if (code == SYNC_OK) {
4248 } else if (code == SYNC_DENIED) {
4249 #ifdef DEMAND_ATTACH_ENABLE
4250 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
4252 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
4254 Abort("Salvage aborted\n");
4255 } else if (code == SYNC_BAD_COMMAND) {
4256 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
4258 #ifdef DEMAND_ATTACH_ENABLE
4259 Log("AskOffline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
4261 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
4263 Abort("Salvage aborted\n");
4266 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
4267 FSYNC_clientFinis();
4271 if (code != SYNC_OK) {
4272 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
4273 Abort("Salvage aborted\n");
4278 AskOnline(VolumeId volumeId, char *partition)
4282 for (i = 0; i < 3; i++) {
4283 code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
4285 if (code == SYNC_OK) {
4287 } else if (code == SYNC_DENIED) {
4288 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, partition);
4289 } else if (code == SYNC_BAD_COMMAND) {
4290 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
4292 #ifdef DEMAND_ATTACH_ENABLE
4293 Log("AskOnline: please make sure fileserver, volserver, salvageserver and salvager binaries are same version.\n");
4295 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
4300 Log("AskOnline: request for fileserver to take volume offline failed; trying again...\n");
4301 FSYNC_clientFinis();
4308 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
4310 /* Volume parameter is passed in case iopen is upgraded in future to
4311 * require a volume Id to be passed
4314 IHandle_t *srcH, *destH;
4315 FdHandle_t *srcFdP, *destFdP;
4318 IH_INIT(srcH, device, rwvolume, inode1);
4319 srcFdP = IH_OPEN(srcH);
4320 assert(srcFdP != NULL);
4321 IH_INIT(destH, device, rwvolume, inode2);
4322 destFdP = IH_OPEN(destH);
4323 while ((nBytes = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
4324 assert(FDH_WRITE(destFdP, buf, nBytes) == nBytes);
4325 assert(nBytes == 0);
4326 FDH_REALLYCLOSE(srcFdP);
4327 FDH_REALLYCLOSE(destFdP);
4334 PrintInodeList(void)
4336 register struct ViceInodeInfo *ip;
4337 struct ViceInodeInfo *buf;
4338 struct afs_stat status;
4339 register int nInodes;
4341 assert(afs_fstat(inodeFd, &status) == 0);
4342 buf = (struct ViceInodeInfo *)malloc(status.st_size);
4343 assert(buf != NULL);
4344 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
4345 assert(read(inodeFd, buf, status.st_size) == status.st_size);
4346 for (ip = buf; nInodes--; ip++) {
4347 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
4348 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
4349 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
4350 ip->u.param[2], ip->u.param[3]);
4356 PrintInodeSummary(void)
4359 struct InodeSummary *isp;
4361 for (i = 0; i < nVolumesInInodeFile; i++) {
4362 isp = &inodeSummary[i];
4363 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);
4368 PrintVolumeSummary(void)
4371 struct VolumeSummary *vsp;
4373 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
4374 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
4384 assert(0); /* Fork is never executed in the NT code path */
4388 #ifdef AFS_DEMAND_ATTACH_FS
4389 if ((f == 0) && (programType == salvageServer)) {
4390 /* we are a salvageserver child */
4391 #ifdef FSSYNC_BUILD_CLIENT
4392 VChildProcReconnectFS_r();
4394 #ifdef SALVSYNC_BUILD_CLIENT
4398 #endif /* AFS_DEMAND_ATTACH_FS */
4399 #endif /* !AFS_NT40_ENV */
4409 #ifdef AFS_DEMAND_ATTACH_FS
4410 if (programType == salvageServer) {
4411 #ifdef SALVSYNC_BUILD_CLIENT
4414 #ifdef FSSYNC_BUILD_CLIENT
4418 #endif /* AFS_DEMAND_ATTACH_FS */
4421 if (main_thread != pthread_self())
4422 pthread_exit((void *)code);
4435 pid = wait(&status);
4437 if (WCOREDUMP(status))
4438 Log("\"%s\" core dumped!\n", prog);
4439 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
4445 TimeStamp(time_t clock, int precision)
4448 static char timestamp[20];
4449 lt = localtime(&clock);
4451 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
4453 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
4458 CheckLogFile(char * log_path)
4460 char oldSlvgLog[AFSDIR_PATH_MAX];
4462 #ifndef AFS_NT40_ENV
4469 strcpy(oldSlvgLog, log_path);
4470 strcat(oldSlvgLog, ".old");
4472 renamefile(log_path, oldSlvgLog);
4473 logFile = afs_fopen(log_path, "a");
4475 if (!logFile) { /* still nothing, use stdout */
4479 #ifndef AFS_NAMEI_ENV
4480 AFS_DEBUG_IOPS_LOG(logFile);
4485 #ifndef AFS_NT40_ENV
4487 TimeStampLogFile(char * log_path)
4489 char stampSlvgLog[AFSDIR_PATH_MAX];
4494 lt = localtime(&now);
4495 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
4496 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
4497 log_path, lt->tm_year + 1900,
4498 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
4501 /* try to link the logfile to a timestamped filename */
4502 /* if it fails, oh well, nothing we can do */
4503 link(log_path, stampSlvgLog);
4512 #ifndef AFS_NT40_ENV
4514 printf("Can't show log since using syslog.\n");
4525 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
4528 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
4531 while (fgets(line, sizeof(line), logFile))
4538 Log(const char *format, ...)
4544 va_start(args, format);
4545 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4547 #ifndef AFS_NT40_ENV
4549 syslog(LOG_INFO, "%s", tmp);
4553 gettimeofday(&now, 0);
4554 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
4560 Abort(const char *format, ...)
4565 va_start(args, format);
4566 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4568 #ifndef AFS_NT40_ENV
4570 syslog(LOG_INFO, "%s", tmp);
4574 fprintf(logFile, "%s", tmp);
4586 ToString(const char *s)
4589 p = (char *)malloc(strlen(s) + 1);
4595 /* Remove the FORCESALVAGE file */
4597 RemoveTheForce(char *path)
4600 struct afs_stat force; /* so we can use afs_stat to find it */
4601 strcpy(target,path);
4602 strcat(target,"/FORCESALVAGE");
4603 if (!Testing && ForceSalvage) {
4604 if (afs_stat(target,&force) == 0) unlink(target);
4608 #ifndef AFS_AIX32_ENV
4610 * UseTheForceLuke - see if we can use the force
4613 UseTheForceLuke(char *path)
4615 struct afs_stat force;
4617 strcpy(target,path);
4618 strcat(target,"/FORCESALVAGE");
4620 return (afs_stat(target, &force) == 0);
4624 * UseTheForceLuke - see if we can use the force
4627 * The VRMIX fsck will not muck with the filesystem it is supposedly
4628 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
4629 * muck directly with the root inode, which is within the normal
4631 * ListViceInodes() has a side effect of setting ForceSalvage if
4632 * it detects a need, based on root inode examination.
4635 UseTheForceLuke(char *path)
4638 return 0; /* sorry OB1 */
4643 /* NT support routines */
4645 static char execpathname[MAX_PATH];
4647 nt_SalvagePartition(char *partName, int jobn)
4652 if (!*execpathname) {
4653 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
4654 if (!n || n == 1023)
4657 job.cj_magic = SALVAGER_MAGIC;
4658 job.cj_number = jobn;
4659 (void)strcpy(job.cj_part, partName);
4660 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
4665 nt_SetupPartitionSalvage(void *datap, int len)
4667 childJob_t *jobp = (childJob_t *) datap;
4668 char logname[AFSDIR_PATH_MAX];
4670 if (len != sizeof(childJob_t))
4672 if (jobp->cj_magic != SALVAGER_MAGIC)
4677 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
4679 logFile = afs_fopen(logname, "w");
4687 #endif /* AFS_NT40_ENV */