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
14 * Module: vol-salvage.c
15 * Institution: The Information Technology Center, Carnegie-Mellon University
19 Correct handling of bad "." and ".." entries.
20 Message if volume has "destroyMe" flag set--but doesn't delete yet.
21 Link count bug fixed--bug was that vnodeEssence link count was unsigned
22 14 bits. Needs to be signed.
25 Change to DirHandle stuff to make sure that cache entries are reused at the
26 right time (this parallels the file server change, but is not identical).
28 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
31 Fixed bug which was causing inode link counts to go bad (thus leaking
33 Vnodes with 0 inode pointers in RW volumes are now deleted.
34 An inode with a matching inode number to the vnode is preferred to an
35 inode with a higer data version.
36 Bug is probably fixed that was causing data version to remain wrong,
37 despite assurances from the salvager to the contrary.
40 Added limited salvaging: unless ForceSalvage is on, then the volume will
41 not be salvaged if the dontSalvage flag is set in the Volume Header.
42 The ForceSalvage flag is turned on if an individual volume is salvaged or
43 if the file FORCESALVAGE exists in the partition header of the file system
44 being salvaged. This isn't used for anything but could be set by vfsck.
45 A -f flag was also added to force salvage.
48 It now deletes obsolete volume inodes without complaining
51 Repairs rw volume headers (again).
54 Correlates volume headers & inodes correctly, thus preventing occasional deletion
55 of read-only volumes...
56 No longer forces a directory salvage for volume 144 (which may be a good volume
58 Some of the messages are cleaned up or made more explicit. One or two added.
60 A bug was fixed which forced salvage of read-only volumes without a corresponding
64 When a volume header is recreated, the new name will be "bogus.volume#"
67 Directory salvaging turned on!!!
70 Prints warning messages for setuid programs.
73 Logs missing inode numbers.
76 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.
79 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
80 Time stamps on log entries.
81 Fcntl on stdout to cause all entries to be appended.
82 Problems writing to temporary files are now all detected.
83 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
84 Some cleanup of error messages.
88 #define SalvageVersion "2.4"
90 /* Main program file. Define globals. */
93 #include <afs/param.h>
103 #include <WINNT/afsevent.h>
105 #include <sys/param.h>
106 #include <sys/file.h>
108 #include <sys/time.h>
109 #endif /* ITIMER_REAL */
111 #if defined(AFS_AIX_ENV)
112 #define WCOREDUMP(x) (x & 0200)
115 #include <afs/afsint.h>
116 #include <afs/assert.h>
117 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
118 #if defined(AFS_VFSINCL_ENV)
119 #include <sys/vnode.h>
121 #include <sys/fs/ufs_inode.h>
123 #include <ufs/inode.h>
125 #else /* AFS_VFSINCL_ENV */
127 #include <ufs/inode.h>
128 #else /* AFS_OSF_ENV */
129 #ifndef AFS_LINUX20_ENV
130 #include <sys/inode.h>
133 #endif /* AFS_VFSINCL_ENV */
134 #endif /* AFS_SGI_ENV */
137 #include <sys/lockf.h>
141 #include <checklist.h>
143 #if defined(AFS_SGI_ENV)
148 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
151 #include <sys/mnttab.h>
152 #include <sys/mntent.h>
157 #endif /* AFS_SGI_ENV */
158 #endif /* AFS_HPUX_ENV */
163 #include <afs/osi_inode.h>
166 #include <afs/afsutil.h>
167 #include <afs/fileutil.h>
168 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
173 #include <afs/afssyscalls.h>
177 #include "partition.h"
179 #include "viceinode.h"
181 #include "volinodes.h" /* header magic number, etc. stuff */
187 extern void *calloc();
189 extern char *vol_DevName();
190 static char *TimeStamp(time_t clock, int precision);
192 #define ORPH_IGNORE 0
193 #define ORPH_REMOVE 1
194 #define ORPH_ATTACH 2
197 int debug; /* -d flag */
198 int Testing=0; /* -n flag */
199 int ListInodeOption; /* -i flag */
200 int ShowRootFiles; /* -r flag */
201 int RebuildDirs; /* -sal flag */
202 int Parallel = 4; /* -para X flag */
203 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
204 int forceR = 0; /* -b flag */
205 int ShowLog = 0; /* -showlog flag */
206 int ShowSuid = 0; /* -showsuid flag */
207 int ShowMounts = 0; /* -showmounts flag */
208 int orphans = ORPH_IGNORE; /* -orphans option */
210 #define MAXPARALLEL 32
212 int OKToZap; /* -o flag */
213 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
214 in the volume header */
216 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
218 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
220 Device fileSysDevice; /* The device number of the current
221 partition being salvaged */
225 char *fileSysPath; /* The path of the mounted partition currently
226 being salvaged, i.e. the directory
227 containing the volume headers */
229 char *fileSysPathName; /* NT needs this to make name pretty in log. */
230 IHandle_t *VGLinkH; /* Link handle for current volume group. */
231 int VGLinkH_cnt; /* # of references to lnk handle. */
232 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
234 char *fileSysDeviceName; /* The block device where the file system
235 being salvaged was mounted */
236 char *filesysfulldev;
238 int VolumeChanged; /* Set by any routine which would change the volume in
239 a way which would require callback is to be broken if the
240 volume was put back on line by an active file server */
242 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
244 struct InodeSummary { /* Inode summary file--an entry for each
245 volume in the inode file for a partition */
246 VolId volumeId; /* Volume id */
247 VolId RWvolumeId; /* RW volume associated */
248 int index; /* index into inode file (0, 1, 2 ...) */
249 int nInodes; /* Number of inodes for this volume */
250 int nSpecialInodes; /* Number of special inodes, i.e. volume
251 header, index, etc. These are all
252 marked (viceinode.h) and will all be sorted
253 to the beginning of the information for
254 this volume. Read-only volumes should
255 ONLY have special inodes (all the other
256 inodes look as if they belong to the
257 original RW volume). */
258 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
259 This is only useful for RW volumes and is used
260 to compute a new volume uniquifier in the event
261 that the header needs to be recreated. The inode
262 uniquifier may be a truncated version of vnode
263 uniquifier (AFS_3DISPARES). The real maxUniquifer
264 is from the vnodes and later calcuated from it */
265 struct VolumeSummary *volSummary;
266 /* Either a pointer to the original volume
267 header summary, or constructed summary
270 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
271 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
272 int inodeFd; /* File descriptor for inode file */
275 struct VolumeSummary { /* Volume summary an entry for each
276 volume in a volume directory.
277 Assumption: one volume directory per
279 char *fileName; /* File name on the partition for the volume
281 struct VolumeHeader header;
282 /* volume number, rw volume number, inode
283 numbers of each major component of
285 IHandle_t *volumeInfoHandle;
286 byte wouldNeedCallback; /* set if the file server should issue
287 call backs for all the files in this volume when
288 the volume goes back on line */
292 IHandle_t *handle; /* Inode containing this index */
293 int nVnodes; /* Total number of vnodes in index */
294 int nAllocatedVnodes; /* Total number actually used */
295 int volumeBlockCount; /* Total number of blocks used by volume */
296 Inode *inodes; /* Directory only */
297 struct VnodeEssence {
298 short count; /* Number of references to vnode; MUST BE SIGNED */
299 unsigned claimed:1; /* Set when a parent directory containing an entry
300 referencing this vnode is found. The claim
301 is that the parent in "parent" can point to
302 this vnode, and no other */
303 unsigned changed:1; /* Set if any parameters (other than the count)
304 in the vnode change. It is determined if the
305 link count has changed by noting whether it is
306 0 after scanning all directories */
307 unsigned salvaged:1;/* Set if this directory vnode has already been salvaged. */
308 unsigned todelete:1;/* Set if this vnode is to be deleted (should not be claimed) */
309 afs_uint32 blockCount;
310 /* Number of blocks (1K) used by this vnode,
312 VnodeId parent; /* parent in vnode */
313 Unique unique; /* Must match entry! */
314 char *name; /* Name of directory entry */
315 int modeBits; /* File mode bits */
316 Inode InodeNumber; /* file's inode */
317 int type; /* File type */
318 int author; /* File author */
319 int owner; /* File owner */
320 int group; /* File group */
322 } vnodeInfo[nVNODECLASSES];
325 struct DirHandle dirHandle;
328 unsigned haveDot, haveDotDot;
330 int copied; /* If the copy-on-write stuff has been applied */
338 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
339 int nVolumes; /* Number of volumes (read-write and read-only)
343 /* For NT, we can fork the per partition salvagers to gain the required
344 * safety against Aborts. But there's too many complex data structures at
345 * the per volume salvager layer to easilty copy the data across.
346 * childJobNumber is resset from -1 to the job number if this is a
347 * per partition child of the main salvager. This information is passed
348 * out-of-band in the extra data area setup for the now unused parent/child
351 #define SALVAGER_MAGIC 0x00BBaaDD
352 #define NOT_CHILD -1 /* job numbers start at 0 */
353 /* If new options need to be passed to child, add them here. */
360 /* Child job this process is running. */
361 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD};
363 int nt_SalvagePartition(char *partName, int jobn);
364 int nt_SetupPartitionSalvage(void *datap, int len);
367 struct InodeSummary *svgp_inodeSummaryp;
377 /* Forward declarations */
378 void Log(), Abort(), Exit();
380 int Wait(char *prog);
381 char * ToString(char *s);
382 void AskOffline(VolumeId volumeId);
383 void AskOnline(VolumeId volumeId, char *partition);
384 void CheckLogFile(void);
385 void ClearROInUseBit(struct VolumeSummary *summary);
386 void CopyAndSalvage(register struct DirSummary *dir);
387 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
388 void CopyOnWrite(register struct DirSummary *dir);
389 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
390 register struct InodeSummary * summary);
391 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
392 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
394 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
395 void GetVolumeSummary(VolumeId singleVolumeNumber);
396 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
398 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
401 void ObtainSalvageLock(void);
402 void PrintInodeList(void);
403 void PrintInodeSummary(void);
404 void PrintVolumeSummary(void);
405 int QuickCheck(register struct InodeSummary *isp, int nVols);
406 void RemoveTheForce(char *path);
407 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
408 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
410 void SalvageFileSysParallel(struct DiskPartition *partP);
411 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
412 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
413 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
414 int check, int *deleteMe);
415 int SalvageIndex(Inode ino, VnodeClass class, int RW,
416 register struct ViceInodeInfo *ip,
417 int nInodes, struct VolumeSummary *volSummary, int check);
418 int SalvageVnodes(register struct InodeSummary *rwIsp,
419 register struct InodeSummary * thisIsp,
420 register struct ViceInodeInfo * inodes, int check);
421 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
422 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
424 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
426 #define SalvageVolumeGroup DoSalvageVolumeGroup
428 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
429 register struct ViceInodeInfo *inodes,
430 int RW, int check, int *deleteMe);
432 int UseTheForceLuke(char *path);
436 /* Uniquifier stored in the Inode */
437 static Unique IUnique(u)
441 return(u & 0x3fffff);
443 #if defined(AFS_SGI_EXMAG)
444 return(u & SGI_UNIQMASK);
447 #endif /* AFS_SGI_EXMAG */
451 static int BadError(aerror)
452 register int aerror; {
453 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
455 return 0; /* otherwise may be transient, e.g. EMFILE */
461 struct cmd_syndesc *as;
463 register struct cmd_item *ti;
464 char pname[100], *temp;
465 afs_int32 seenpart = 0, seenvol = 0, vid = 0;
466 struct DiskPartition *partP;
468 #ifdef AFS_SGI_VNODE_GLUE
469 if (afs_init_kernel_config(-1) <0) {
470 printf("Can't determine NUMA configuration, not starting salvager.\n");
475 if (ti = as->parms[0].items) { /* -partition */
477 strncpy(pname, ti->data, 100);
479 if (ti = as->parms[1].items) { /* -volumeid */
481 printf("You must also specify '-partition' option with the '-volumeid' option\n");
485 vid = atoi(ti->data);
487 if (as->parms[2].items) /* -debug */
489 if (as->parms[3].items) /* -nowrite */
491 if (as->parms[4].items) /* -inodes */
493 if (as->parms[5].items) /* -force */
495 if (as->parms[6].items) /* -oktozap */
497 if (as->parms[7].items) /* -rootinodes */
499 if (as->parms[8].items) /* -RebuildDirs */
501 if (as->parms[9].items) /* -ForceReads */
503 if (ti = as->parms[10].items) {/* -Parallel # */
505 if (strncmp(temp,"all",3) == 0) {
509 if (strlen(temp) != 0) {
510 Parallel = atoi(temp);
511 if (Parallel < 1) Parallel = 1;
512 if (Parallel > MAXPARALLEL) {
513 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
514 Parallel = MAXPARALLEL;
518 if (ti = as->parms[11].items) {/* -tmpdir */
522 dirp = opendir(tmpdir);
524 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
529 if (ti = as->parms[12].items) /* -showlog */
531 if (ti = as->parms[13].items) { /* -log */
536 if (ti = as->parms[14].items) { /* -showmounts */
541 if (ti = as->parms[15].items) { /* -orphans */
543 orphans = ORPH_IGNORE;
544 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
545 orphans = ORPH_REMOVE;
546 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
547 orphans = ORPH_ATTACH;
550 /* Note: if seemvol we initialize this as a standard volume utility: this has the
551 implication that the file server may be running; negotations have to be made with
552 the file server in this case to take the read write volume and associated read-only
553 volumes off line before salvaging */
556 if (afs_winsockInit()<0) {
557 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
558 AFSDIR_SALVAGER_FILE, 0);
559 Log("Failed to initailize winsock, exiting.\n");
564 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
567 if (myjob.cj_number != NOT_CHILD) {
570 (void) strcpy(pname, myjob.cj_part);
575 for (partP = DiskPartitionList; partP; partP = partP->next) {
576 SalvageFileSysParallel(partP);
578 SalvageFileSysParallel(0);
581 partP = VGetPartition(pname, 0);
583 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
588 SalvageFileSys(partP, 0);
590 /* Salvage individual volume */
592 Log("salvage: invalid volume id specified; salvage aborted\n");
595 SalvageFileSys (partP, vid);
603 #include "AFS_component_version_number.c"
607 char *save_args[MAX_ARGS];
609 pthread_t main_thread;
615 struct cmd_syndesc *ts;
617 char commandLine[150];
620 extern char cml_version_number[];
624 * The following signal action for AIX is necessary so that in case of a
625 * crash (i.e. core is generated) we can include the user's data section
626 * in the core dump. Unfortunately, by default, only a partial core is
627 * generated which, in many cases, isn't too useful.
629 struct sigaction nsa;
631 sigemptyset(&nsa.sa_mask);
632 nsa.sa_handler = SIG_DFL;
633 nsa.sa_flags = SA_FULLDUMP;
634 sigaction(SIGABRT, &nsa, NULL);
635 sigaction(SIGSEGV, &nsa, NULL);
638 /* Initialize directory paths */
639 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
641 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
643 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
647 main_thread = pthread_self();
648 if (spawnDatap && spawnDataLen) {
649 /* This is a child per partition salvager. Don't setup log or
650 * try to lock the salvager lock.
652 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
657 for (commandLine[0] = '\0', i=0; i<argc; i++) {
658 if (i > 0) strcat(commandLine, " ");
659 strcat(commandLine, argv[i]);
662 /* All entries to the log will be appended. Useful if there are
663 * multiple salvagers appending to the log.
668 #ifdef AFS_LINUX20_ENV
669 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
671 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
677 if (geteuid() != 0) {
678 printf("Salvager must be run as root.\n");
684 /* bad for normal help flag processing, but can do nada */
686 fprintf(logFile, "%s\n", cml_version_number);
687 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
689 /* Get and hold a lock for the duration of the salvage to make sure
690 * that no other salvage runs at the same time. The routine
691 * VInitVolumePackage (called below) makes sure that a file server or
692 * other volume utilities don't interfere with the salvage.
699 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
700 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
701 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
702 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
703 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
704 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
705 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
706 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
707 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
708 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
709 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
710 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
711 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
712 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
713 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
714 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
715 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
716 err = cmd_Dispatch(argc, argv);
720 /* Get the salvage lock if not already held. Hold until process exits. */
721 void ObtainSalvageLock(void)
726 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
727 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
729 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
731 "salvager: There appears to be another salvager running! Aborted.\n");
735 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
736 assert(salvageLock >= 0);
737 if (lockf(salvageLock, F_LOCK, 0) == -1) {
739 "salvager: There appears to be another salvager running! Aborted.\n");
746 #ifdef AFS_SGI_XFS_IOPS_ENV
747 /* Check if the given partition is mounted. For XFS, the root inode is not a
748 * constant. So we check the hard way.
750 int IsPartitionMounted(char *part)
753 struct mntent *mntent;
755 assert(mntfp = setmntent(MOUNTED, "r"));
756 while (mntent = getmntent(mntfp)) {
757 if (!strcmp(part, mntent->mnt_dir))
762 return mntent ? 1 : 1;
765 /* Check if the given inode is the root of the filesystem. */
766 #ifndef AFS_SGI_XFS_IOPS_ENV
767 int IsRootInode(status)
770 /* The root inode is not a fixed value in XFS partitions. So we need to see if
771 * the partition is in the list of mounted partitions. This only affects the
772 * SalvageFileSys path, so we check there.
774 return (status->st_ino == ROOTINODE);
779 /* We don't want to salvage big files filesystems, since we can't put volumes on
782 int CheckIfBigFilesFS(mountPoint, devName)
786 struct superblock fs;
789 if (strncmp(devName, "/dev/", 5)) {
790 (void) sprintf(name, "/dev/%s", devName);
793 (void) strcpy(name, devName);
796 if (ReadSuper(&fs, name)<0) {
797 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
800 if (IsBigFilesFileSystem(&fs)) {
801 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
809 #define HDSTR "\\Device\\Harddisk"
810 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
811 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
818 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
820 if (strncmp(res, HDSTR, HDLEN)) {
823 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
824 res, HDSTR, p1->devName);
828 d1 = atoi(&res[HDLEN]);
830 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
832 if (strncmp(res, HDSTR, HDLEN)) {
835 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
836 res, HDSTR, p2->devName);
840 d2 = atoi(&res[HDLEN]);
845 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
848 /* This assumes that two partitions with the same device number divided by
849 * PartsPerDisk are on the same disk.
851 void SalvageFileSysParallel(struct DiskPartition *partP)
854 struct DiskPartition *partP;
855 int pid; /* Pid for this job */
856 int jobnumb; /* Log file job number */
857 struct job *nextjob; /* Next partition on disk to salvage */
859 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
860 struct job *thisjob = 0;
861 static int numjobs = 0;
862 static int jobcount = 0;
868 char logFileName[256];
872 /* We have a partition to salvage. Copy it into thisjob */
873 thisjob = (struct job *) malloc(sizeof(struct job));
875 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
878 bzero(thisjob, sizeof(struct job));
879 thisjob->partP = partP;
880 thisjob->jobnumb = jobcount;
883 else if (jobcount == 0) {
884 /* We are asking to wait for all jobs (partp == 0), yet we never
887 Log("No file system partitions named %s* found; not salvaged\n",
888 VICE_PARTITION_PREFIX);
892 if (debug || Parallel == 1) {
894 SalvageFileSys(thisjob->partP, 0);
901 /* Check to see if thisjob is for a disk that we are already
902 * salvaging. If it is, link it in as the next job to do. The
903 * jobs array has 1 entry per disk being salvages. numjobs is
904 * the total number of disks currently being salvaged. In
905 * order to keep thejobs array compact, when a disk is
906 * completed, the hightest element in the jobs array is moved
907 * down to now open slot.
909 for (j=0; j<numjobs; j++) {
910 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
911 /* On same disk, add it to this list and return */
912 thisjob->nextjob = jobs[j]->nextjob;
913 jobs[j]->nextjob = thisjob;
920 /* Loop until we start thisjob or until all existing jobs are finished */
921 while ( thisjob || (!partP && (numjobs > 0)) ) {
922 startjob = -1; /* No new job to start */
924 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
925 /* Either the max jobs are running or we have to wait for all
926 * the jobs to finish. In either case, we wait for at least one
927 * job to finish. When it's done, clean up after it.
929 pid = wait(&wstatus);
931 for (j=0; j<numjobs; j++) { /* Find which job it is */
932 if (pid == jobs[j]->pid) break;
935 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
936 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
939 numjobs--; /* job no longer running */
940 oldjob = jobs[j]; /* remember */
941 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
942 free(oldjob); /* free the old job */
944 /* If there is another partition on the disk to salvage, then
945 * say we will start it (startjob). If not, then put thisjob there
946 * and say we will start it.
948 if (jobs[j]) { /* Another partitions to salvage */
949 startjob = j; /* Will start it */
950 } else { /* There is not another partition to salvage */
952 jobs[j] = thisjob; /* Add thisjob */
954 startjob = j; /* Will start it */
956 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
957 startjob = -1; /* Don't start it - already running */
961 /* We don't have to wait for a job to complete */
963 jobs[numjobs] = thisjob; /* Add this job */
965 startjob = numjobs; /* Will start it */
969 /* Start up a new salvage job on a partition in job slot "startjob" */
970 if (startjob != -1) {
972 Log("Starting salvage of file system partition %s\n",
973 jobs[startjob]->partP->name);
975 /* For NT, we not only fork, but re-exec the salvager. Pass in the
976 * commands and pass the child job number via the data path.
978 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
979 jobs[startjob]->jobnumb);
980 jobs[startjob]->pid = pid;
985 jobs[startjob]->pid = pid;
991 for (fd =0; fd < 16; fd++) close(fd);
992 open("/", 0); dup2(0, 1); dup2(0, 2);
993 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
994 logFile = fopen(logFileName, "w");
995 if (!logFile) logFile = stdout;
997 SalvageFileSys1(jobs[startjob]->partP, 0);
1002 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1004 /* If waited for all jobs to complete, now collect log files and return */
1006 for (i=0; i<jobcount; i++) {
1007 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1008 if (passLog = fopen(logFileName, "r")) {
1009 while(fgets(buf, sizeof(buf), passLog)) {
1010 fputs(buf, logFile);
1014 (void)unlink(logFileName);
1022 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1024 if (!canfork || debug || Fork() == 0) {
1025 SalvageFileSys1(partP, singleVolumeNumber);
1026 if (canfork && !debug) {
1032 Wait("SalvageFileSys");
1035 char *get_DevName(pbuffer, wpath)
1036 char *wpath, *pbuffer;
1038 char pbuf[128], *ptr;
1039 strcpy(pbuf, pbuffer);
1040 ptr = (char *)strrchr(pbuf, '/');
1043 strcpy(wpath, pbuf);
1046 ptr = (char *)strrchr(pbuffer, '/');
1048 strcpy(pbuffer, ptr+1);
1054 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1057 char inodeListPath[50];
1058 static char tmpDevName[100];
1059 static char wpath[100];
1060 struct VolumeSummary *vsp, *esp;
1063 fileSysPartition = partP;
1064 fileSysDevice = fileSysPartition->device;
1065 fileSysPathName = VPartitionPath(fileSysPartition);
1068 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1069 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1070 name = partP->devName;
1072 fileSysPath = fileSysPathName;
1073 strcpy(tmpDevName, partP->devName);
1074 name = get_DevName(tmpDevName, wpath);
1075 fileSysDeviceName = name;
1076 filesysfulldev = wpath;
1079 VLockPartition(partP->name);
1080 if (singleVolumeNumber || ForceSalvage)
1083 ForceSalvage = UseTheForceLuke(fileSysPath);
1085 if (singleVolumeNumber) {
1086 if (!VConnectFS()) {
1087 Abort("Couldn't connect to file server\n");
1089 AskOffline(singleVolumeNumber);
1092 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1094 Log("***Forced salvage of all volumes on this partition***\n");
1099 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1106 assert((dirp = opendir(fileSysPath)) != NULL);
1107 while (dp = readdir(dirp)) {
1108 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1109 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1111 Log("Removing old salvager temp files %s\n", dp->d_name);
1112 strcpy(npath, fileSysPath);
1114 strcat(npath, dp->d_name);
1120 tdir = (tmpdir ? tmpdir : fileSysPath);
1122 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1123 (void) strcpy(inodeListPath, _tempnam(tdir, "salvage.inodes."));
1125 sprintf(inodeListPath, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1127 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1128 unlink(inodeListPath);
1132 /* Using nt_unlink here since we're really using the delete on close
1133 * semantics of unlink. In most places in the salvager, we really do
1134 * mean to unlink the file at that point. Those places have been
1135 * modified to actually do that so that the NT crt can be used there.
1137 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1138 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1140 inodeFd = open(inodeListPath, O_RDONLY);
1141 unlink(inodeListPath);
1144 Abort("Temporary file %s is missing...\n",
1146 if (ListInodeOption) {
1150 /* enumerate volumes in the partition.
1151 figure out sets of read-only + rw volumes.
1152 salvage each set, read-only volumes first, then read-write.
1153 Fix up inodes on last volume in set (whether it is read-write
1156 GetVolumeSummary(singleVolumeNumber);
1158 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1159 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1160 for (j=i; j < nVolumesInInodeFile
1161 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1162 VolumeId vid = inodeSummary[j].volumeId;
1163 struct VolumeSummary *tsp;
1164 /* Scan volume list (from partition root directory) looking for the
1165 current rw volume number in the volume list from the inode scan.
1166 If there is one here that is not in the inode volume list,
1168 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1170 DeleteExtraVolumeHeaderFile(vsp);
1172 /* Now match up the volume summary info from the root directory with the
1173 entry in the volume list obtained from scanning inodes */
1174 inodeSummary[j].volSummary = NULL;
1175 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1176 if (tsp->header.id == vid) {
1177 inodeSummary[j].volSummary = tsp;
1183 /* Salvage the group of volumes (several read-only + 1 read/write)
1184 * starting with the current read-only volume we're looking at.
1186 SalvageVolumeGroup(&inodeSummary[i], j-i);
1189 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1190 for ( ; vsp<esp; vsp++) {
1192 DeleteExtraVolumeHeaderFile(vsp);
1195 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1196 RemoveTheForce(fileSysPath);
1198 if (!Testing && singleVolumeNumber) {
1199 AskOnline(singleVolumeNumber, fileSysPartition->name);
1201 /* Step through the volumeSummary list and set all volumes on-line.
1202 * The volumes were taken off-line in GetVolumeSummary.
1204 for (j=0; j<nVolumes; j++) {
1205 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1210 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1211 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1214 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1217 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1219 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1220 vsp->fileName, (Testing? "would have been ":""));
1222 unlink(vsp->fileName);
1226 CompareInodes(_p1,_p2)
1227 const void *_p1,*_p2;
1229 register const struct ViceInodeInfo *p1 = _p1;
1230 register const struct ViceInodeInfo *p2 = _p2;
1231 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1232 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1233 VolumeId p1rwid, p2rwid;
1234 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1235 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1236 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1237 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1238 if (p1rwid < p2rwid)
1240 if (p1rwid > p2rwid)
1242 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1243 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1244 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1245 return (p1->u.special.type < p2->u.special.type? -1: 1);
1246 if (p1->u.vnode.volumeId == p1rwid)
1248 if (p2->u.vnode.volumeId == p2rwid)
1250 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1252 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1253 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1254 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1256 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1258 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1260 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1262 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1264 /* The following tests are reversed, so that the most desirable
1265 of several similar inodes comes first */
1266 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1267 #ifdef AFS_3DISPARES
1268 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1269 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1272 #ifdef AFS_SGI_EXMAG
1273 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1274 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1279 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1280 #ifdef AFS_3DISPARES
1281 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1282 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1285 #ifdef AFS_SGI_EXMAG
1286 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1287 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1292 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1293 #ifdef AFS_3DISPARES
1294 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1295 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1298 #ifdef AFS_SGI_EXMAG
1299 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1300 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1305 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1306 #ifdef AFS_3DISPARES
1307 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1308 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1311 #ifdef AFS_SGI_EXMAG
1312 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1313 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1321 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1322 register struct InodeSummary * summary)
1324 int volume = ip->u.vnode.volumeId;
1325 int rwvolume = volume;
1326 register n, nSpecial;
1327 register Unique maxunique;
1330 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1332 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1334 rwvolume = ip->u.special.parentId;
1335 /* This isn't quite right, as there could (in error) be different
1336 parent inodes in different special vnodes */
1339 if (maxunique < ip->u.vnode.vnodeUniquifier)
1340 maxunique = ip->u.vnode.vnodeUniquifier;
1344 summary->volumeId = volume;
1345 summary->RWvolumeId = rwvolume;
1346 summary->nInodes =n;
1347 summary->nSpecialInodes = nSpecial;
1348 summary->maxUniquifier = maxunique;
1351 int OnlyOneVolume(inodeinfo, singleVolumeNumber)
1352 struct ViceInodeInfo *inodeinfo;
1353 VolumeId singleVolumeNumber;
1355 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1356 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1357 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1362 * Collect list of inodes in file named by path. If a truly fatal error,
1363 * unlink the file and abort. For lessor errors, return -1. The file will
1364 * be unlinked by the caller.
1366 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1369 int summaryFd, forceSal, err;
1370 struct ViceInodeInfo *ip;
1371 struct InodeSummary summary;
1372 char summaryFileName[50];
1375 char *dev = fileSysPath;
1376 char *wpath = fileSysPath;
1378 char *dev = fileSysDeviceName;
1379 char *wpath = filesysfulldev;
1381 char *part = fileSysPath;
1384 /* This file used to come from vfsck; cobble it up ourselves now... */
1385 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1387 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1392 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1394 if (forceSal && !ForceSalvage) {
1395 Log("***Forced salvage of all volumes on this partition***\n");
1398 inodeFd = open(path, O_RDWR);
1399 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1401 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1403 tdir = (tmpdir ? tmpdir : part);
1405 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1406 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1408 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1410 summaryFile = fopen(summaryFileName, "a+");
1411 if (summaryFile == NULL) {
1414 Abort("Unable to create inode summary file\n");
1416 if (!canfork || debug || Fork() == 0) {
1418 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1420 fclose(summaryFile); close(inodeFd);
1421 unlink(summaryFileName);
1422 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1423 RemoveTheForce(fileSysPath);
1424 Log("%s vice inodes on %s; not salvaged\n",
1425 singleVolumeNumber? "No applicable": "No", dev);
1428 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1430 fclose(summaryFile); close(inodeFd);
1432 unlink(summaryFileName);
1433 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1435 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1436 fclose(summaryFile); close(inodeFd);
1438 unlink(summaryFileName);
1439 Abort("Unable to read inode table; %s not salvaged\n", dev);
1441 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1442 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1443 write(inodeFd, ip, status.st_size) != status.st_size) {
1444 fclose(summaryFile); close(inodeFd);
1446 unlink(summaryFileName);
1447 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1451 CountVolumeInodes(ip, nInodes, &summary);
1452 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1453 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1454 fclose(summaryFile); close(inodeFd);
1457 summary.index += (summary.nInodes);
1458 nInodes -= summary.nInodes;
1459 ip += summary.nInodes;
1461 /* Following fflush is not fclose, because if it was debug mode would not work */
1462 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1463 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1464 fclose(summaryFile); close(inodeFd);
1467 if (canfork && !debug) {
1473 if (Wait("Inode summary") == -1) {
1474 fclose(summaryFile); close(inodeFd);
1476 unlink(summaryFileName);
1477 Exit(1); /* salvage of this partition aborted */
1480 assert(fstat(fileno(summaryFile), &status) != -1);
1481 if ( status.st_size != 0 ) {
1483 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1484 assert(inodeSummary != NULL);
1485 /* For GNU we need to do lseek to get the file pointer moved. */
1486 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1487 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1488 assert(ret == status.st_size);
1490 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1491 fclose(summaryFile);
1493 unlink(summaryFileName);
1497 /* Comparison routine for volume sort.
1498 This is setup so that a read-write volume comes immediately before
1499 any read-only clones of that volume */
1500 CompareVolumes(_p1,_p2)
1501 const void *_p1,*_p2;
1503 register const struct VolumeSummary *p1 = _p1;
1504 register const struct VolumeSummary *p2 = _p2;
1505 if (p1->header.parent != p2->header.parent)
1506 return p1->header.parent < p2->header.parent? -1: 1;
1507 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1509 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1511 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1514 void GetVolumeSummary(VolumeId singleVolumeNumber)
1517 afs_int32 nvols = 0;
1518 struct VolumeSummary *vsp, vs;
1519 struct VolumeDiskHeader diskHeader;
1522 /* Get headers from volume directory */
1523 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1524 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1525 if (!singleVolumeNumber) {
1526 while (dp = readdir(dirp)) {
1527 char *p = dp->d_name;
1528 p = strrchr(dp->d_name, '.');
1529 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1531 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1532 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1533 == sizeof (diskHeader) &&
1534 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1535 DiskToVolumeHeader(&vs.header, &diskHeader);
1542 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1546 if (!nvols) nvols = 1;
1547 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1549 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1550 assert(volumeSummaryp != NULL);
1553 vsp = volumeSummaryp;
1554 while (dp = readdir(dirp)) {
1555 char *p = dp->d_name;
1556 p = strrchr(dp->d_name, '.');
1557 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1560 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1561 || read(fd, &diskHeader, sizeof (diskHeader))
1562 != sizeof (diskHeader)
1563 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1568 if (!singleVolumeNumber) {
1569 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1570 dp->d_name, (Testing? "it would have been ":""));
1576 char nameShouldBe[64];
1577 DiskToVolumeHeader(&vsp->header, &diskHeader);
1578 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1579 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1582 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1583 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1584 if (singleVolumeNumber)
1585 AskOffline(vsp->header.id);
1586 if (strcmp(nameShouldBe, dp->d_name)) {
1587 if (!Showmode) Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", dp->d_name, (Testing ? "it would have been ":""));
1592 vsp->fileName = ToString(dp->d_name);
1602 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1605 /* Find the link table. This should be associated with the RW volume or, if
1606 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1608 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1609 struct ViceInodeInfo *allInodes)
1612 struct ViceInodeInfo *ip;
1614 for (i=0; i<nVols; i++) {
1615 ip = allInodes + isp[i].index;
1616 for (j=0; j<isp[i].nSpecialInodes; j++) {
1617 if (ip[j].u.special.type == VI_LINKTABLE)
1618 return ip[j].inodeNumber;
1624 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1626 struct versionStamp version;
1629 if (!VALID_INO(ino))
1630 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1631 isp->volumeId, INODESPECIAL,
1632 VI_LINKTABLE, isp->RWvolumeId);
1633 if (!VALID_INO(ino))
1634 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1635 isp->RWvolumeId, errno);
1636 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1637 fdP = IH_OPEN(VGLinkH);
1639 Abort("Can't open link table for volume %u (error = %d)\n",
1640 isp->RWvolumeId, errno);
1642 if (FDH_TRUNC(fdP, 0)<0)
1643 Abort("Can't truncate link table for volume %u (error = %d)\n",
1644 isp->RWvolumeId, errno);
1646 version.magic = LINKTABLEMAGIC;
1647 version.version = LINKTABLEVERSION;
1649 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1651 Abort("Can't truncate link table for volume %u (error = %d)\n",
1652 isp->RWvolumeId, errno);
1654 FDH_REALLYCLOSE(fdP);
1656 /* If the volume summary exits (i.e., the V*.vol header file exists),
1657 * then set this inode there as well.
1659 if (isp->volSummary)
1660 isp->volSummary->header.linkTable = ino;
1666 void *nt_SVG(void *arg)
1668 SVGParms_t *parms = (SVGParms_t*)arg;
1669 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1673 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1676 pthread_attr_t tattr;
1680 /* Initialize per volume global variables, even if later code does so */
1684 memset(&VolInfo, 0, sizeof(VolInfo));
1686 parms.svgp_inodeSummaryp = isp;
1687 parms.svgp_count = nVols;
1688 code = pthread_attr_init(&tattr);
1690 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1694 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1696 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1700 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1702 Log("Failed to create thread to salvage volume group %u\n",
1706 (void) pthread_join(tid, NULL);
1708 #endif /* AFS_NT40_ENV */
1710 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1712 struct ViceInodeInfo *inodes,*allInodes,*ip;
1713 int i, totalInodes, size, salvageTo;
1717 int dec_VGLinkH = 0;
1719 FdHandle_t *fdP = NULL;
1722 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1723 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1724 if (!ForceSalvage && QuickCheck(isp, nVols))
1727 if (ShowMounts && !haveRWvolume)
1729 if (canfork && !debug && Fork() != 0) {
1730 (void) Wait("Salvage volume group");
1733 for (i = 0, totalInodes = 0; i<nVols; i++)
1734 totalInodes += isp[i].nInodes;
1735 size = totalInodes * sizeof (struct ViceInodeInfo);
1736 inodes = (struct ViceInodeInfo *) malloc(size);
1737 allInodes = inodes - isp->index; /* this would the base of all the inodes
1738 for the partition, if all the inodes
1739 had been read into memory */
1740 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1)
1741 assert(read(inodeFd,inodes,size) == size)
1743 /* Don't try to salvage a read write volume if there isn't one on this
1745 salvageTo = haveRWvolume? 0:1;
1747 #ifdef AFS_NAMEI_ENV
1748 ino = FindLinkHandle(isp, nVols, allInodes);
1749 if (VALID_INO(ino)) {
1750 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1751 fdP = IH_OPEN(VGLinkH);
1753 if (!VALID_INO(ino) || fdP == NULL) {
1754 Log("%s link table for volume %u.\n",
1755 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1757 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1760 CreateLinkTable(isp, ino);
1764 FDH_REALLYCLOSE(fdP);
1766 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1769 /* Salvage in reverse order--read/write volume last; this way any
1770 Inodes not referenced by the time we salvage the read/write volume
1771 can be picked up by the read/write volume */
1772 /* ACTUALLY, that's not done right now--the inodes just vanish */
1773 for (i = nVols-1; i>=salvageTo; i--) {
1775 struct InodeSummary *lisp = &isp[i];
1776 #ifdef AFS_NAMEI_ENV
1777 /* If only the RO is present on this partition, the link table
1778 * shows up as a RW volume special file. Need to make sure the
1779 * salvager doesn't try to salvage the non-existent RW.
1781 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1782 /* If this only special inode is the link table, continue */
1783 if (inodes->u.special.type == VI_LINKTABLE) {
1789 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1790 lisp->volumeId, (Testing?"(READONLY mode)":""));
1791 /* Check inodes twice. The second time do things seriously. This
1792 way the whole RO volume can be deleted, below, if anything goes wrong */
1793 for (check = 1; check>=0; check--) {
1795 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1796 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1797 if (rw && deleteMe) {
1798 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1799 volume won't be called */
1805 if (rw && check == 1)
1807 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1808 MaybeZapVolume(lisp,"Vnode index", 0, check);
1814 /* Fix actual inode counts */
1816 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1817 static int TraceBadLinkCounts = 0;
1818 #ifdef AFS_NAMEI_ENV
1819 if (VGLinkH->ih_ino == ip->inodeNumber) {
1820 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1821 VGLinkH_p1 = ip->u.param[0];
1822 continue; /* Deal with this last. */
1825 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1826 TraceBadLinkCounts--; /* Limit reports, per volume */
1827 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1828 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1829 ip->byteCount, ip->u.param[0], ip->u.param[1],
1830 ip->u.param[2], ip->u.param[3]);
1832 while (ip->linkCount > 0) {
1833 /* below used to assert, not break */
1835 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1836 Log ("idec failed. inode %s errno %d\n",
1837 PrintInode(NULL, ip->inodeNumber), errno);
1843 while (ip->linkCount < 0) {
1844 /* these used to be asserts */
1846 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1847 Log ("iinc failed. inode %s errno %d\n",
1848 PrintInode(NULL, ip->inodeNumber) ,errno);
1855 #ifdef AFS_NAMEI_ENV
1856 while (dec_VGLinkH > 0) {
1857 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1858 Log("idec failed on link table, errno = %d\n", errno);
1862 while (dec_VGLinkH < 0) {
1863 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1864 Log("iinc failed on link table, errno = %d\n", errno);
1871 /* Directory consistency checks on the rw volume */
1873 SalvageVolume(isp, VGLinkH);
1874 IH_RELEASE(VGLinkH);
1876 if (canfork && !debug) {
1882 int QuickCheck(register struct InodeSummary *isp, int nVols)
1884 /* Check headers BEFORE forking */
1888 for (i = 0; i<nVols; i++) {
1889 struct VolumeSummary *vs = isp[i].volSummary;
1890 VolumeDiskData volHeader;
1892 /* Don't salvage just because phantom rw volume is there... */
1893 /* (If a read-only volume exists, read/write inodes must also exist) */
1894 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1898 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1899 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1900 == sizeof(volHeader)
1901 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1902 && volHeader.dontSalvage == DONT_SALVAGE
1903 && volHeader.needsSalvaged == 0
1904 && volHeader.destroyMe == 0) {
1905 if (volHeader.inUse == 1) {
1906 volHeader.inUse = 0;
1907 volHeader.inService = 1;
1909 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1910 != sizeof(volHeader)) {
1927 /* SalvageVolumeHeaderFile
1929 * Salvage the top level V*.vol header file. Make sure the special files
1930 * exist and that there are no duplicates.
1932 * Calls SalvageHeader for each possible type of volume special file.
1935 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1936 register struct ViceInodeInfo *inodes,
1937 int RW, int check, int *deleteMe)
1941 register struct ViceInodeInfo *ip;
1942 int allinodesobsolete = 1;
1943 struct VolumeDiskHeader diskHeader;
1947 bzero(&tempHeader, sizeof(tempHeader));
1948 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
1949 tempHeader.stamp.version = VOLUMEHEADERVERSION;
1950 tempHeader.id = isp->volumeId;
1951 tempHeader.parent = isp->RWvolumeId;
1952 /* Check for duplicates (inodes are sorted by type field) */
1953 for (i = 0; i<isp->nSpecialInodes-1; i++) {
1954 ip = &inodes[isp->index+i];
1955 if (ip->u.special.type == (ip+1)->u.special.type) {
1956 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
1960 for (i = 0; i<isp->nSpecialInodes; i++) {
1961 ip = &inodes[isp->index+i];
1962 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
1964 Log("Rubbish header inode\n");
1967 Log("Rubbish header inode; deleted\n");
1969 else if (!stuff[ip->u.special.type-1].obsolete) {
1970 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
1971 if (!check && ip->u.special.type != VI_LINKTABLE)
1972 ip->linkCount--; /* Keep the inode around */
1973 allinodesobsolete = 0;
1977 if (allinodesobsolete) {
1984 VGLinkH_cnt ++; /* one for every header. */
1986 if (!RW && !check && isp->volSummary) {
1987 ClearROInUseBit(isp->volSummary);
1991 for (i = 0; i< MAXINODETYPE; i++) {
1992 if (stuff[i].inodeType == VI_LINKTABLE) {
1993 /* Gross hack: SalvageHeader does a bcmp on the volume header.
1994 * And we may have recreated the link table earlier, so set the
1995 * RW header as well.
1997 if (VALID_INO(VGLinkH->ih_ino)) {
1998 *stuff[i].inode = VGLinkH->ih_ino;
2002 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2006 if (isp->volSummary == NULL) {
2008 sprintf(name, VFORMAT, isp->volumeId);
2010 Log("No header file for volume %u\n", isp->volumeId);
2013 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2014 isp->volumeId, (Testing?"it would have been ":""),
2015 fileSysPathName, name);
2016 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2017 assert(headerFd != -1)
2018 isp->volSummary = (struct VolumeSummary *)
2019 malloc(sizeof(struct VolumeSummary));
2020 isp->volSummary->fileName = ToString(name);
2024 /* hack: these two fields are obsolete... */
2025 isp->volSummary->header.volumeAcl = 0;
2026 isp->volSummary->header.volumeMountTable = 0;
2028 if (bcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2029 /* We often remove the name before calling us, so we make a fake one up */
2030 if (isp->volSummary->fileName) {
2031 strcpy(name, isp->volSummary->fileName);
2033 sprintf(name, VFORMAT, isp->volumeId);
2034 isp->volSummary->fileName = ToString(name);
2037 Log("Header file %s is damaged or no longer valid%s\n",
2038 name, (check ? "" : "; repairing"));
2042 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2043 assert(headerFd != -1)
2047 bcopy(&tempHeader,&isp->volSummary->header,sizeof(struct VolumeHeader));
2049 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2051 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2052 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2053 != sizeof(struct VolumeDiskHeader)) {
2054 Log("Couldn't rewrite volume header file!\n");
2061 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2062 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2066 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2067 int check, int *deleteMe)
2070 VolumeDiskData volumeInfo;
2071 struct versionStamp fileHeader;
2080 #ifndef AFS_NAMEI_ENV
2081 if ( sp->inodeType == VI_LINKTABLE)
2084 if (*(sp->inode) == 0) {
2086 Log("Missing inode in volume header (%s)\n", sp->description);
2089 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2090 sp->description, (Testing ? "it would have recreated it": "recreating"));
2092 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2093 isp->volumeId, INODESPECIAL,
2094 sp->inodeType, isp->RWvolumeId);
2095 if (!VALID_INO(*(sp->inode)))
2096 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2097 sp->description, errno);
2102 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2103 fdP = IH_OPEN(specH);
2104 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2105 /* bail out early and destroy the volume */
2106 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2108 if (deleteMe) *deleteMe = 1;
2113 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2114 sp->description, errno);
2117 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2118 || header.fileHeader.magic != sp->stamp.magic)) {
2120 Log("Part of the header (%s) is corrupted\n", sp->description);
2121 FDH_REALLYCLOSE(fdP);
2125 Log("Part of the header (%s) is corrupted; recreating\n",
2129 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2132 FDH_REALLYCLOSE(fdP);
2136 if (recreate && !Testing) {
2138 Abort("Internal error: recreating volume header (%s) in check mode\n",
2140 code = FDH_TRUNC(fdP, 0);
2142 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2143 sp->description, errno);
2145 /* The following code should be moved into vutil.c */
2146 if (sp->inodeType == VI_VOLINFO) {
2148 bzero(&header.volumeInfo, sizeof (header.volumeInfo));
2149 header.volumeInfo.stamp = sp->stamp;
2150 header.volumeInfo.id = isp->volumeId;
2151 header.volumeInfo.parentId = isp->RWvolumeId;
2152 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2153 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2154 header.volumeInfo.inService = 0;
2155 header.volumeInfo.blessed = 0;
2156 /* The + 1000 is a hack in case there are any files out in venus caches */
2157 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2158 header.volumeInfo.type =
2159 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2160 header.volumeInfo.needsCallback = 0;
2161 gettimeofday(&tp,0);
2162 header.volumeInfo.creationDate = tp.tv_sec;
2163 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2164 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2166 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2167 sizeof(header.volumeInfo));
2168 if (code != sizeof(header.volumeInfo)) {
2170 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2171 sp->description, errno);
2172 Abort("Unable to write entire volume header file (%s)\n",
2177 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2178 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2180 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2181 if (code != sizeof(sp->stamp)) {
2183 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2184 sp->description, errno);
2185 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2190 FDH_REALLYCLOSE(fdP);
2192 if (sp->inodeType == VI_VOLINFO) {
2193 VolInfo = header.volumeInfo;
2196 if (VolInfo.updateDate) {
2197 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2198 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2199 (Testing?"it would have been ":""), update);
2201 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2202 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2211 int SalvageVnodes(register struct InodeSummary *rwIsp,
2212 register struct InodeSummary * thisIsp,
2213 register struct ViceInodeInfo * inodes, int check)
2215 int ilarge, ismall, ioffset, RW, nInodes;
2216 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2217 if (Showmode) return 0;
2218 RW = (rwIsp == thisIsp);
2219 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2220 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2221 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2222 if (check && ismall == -1)
2224 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2225 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2226 return (ilarge==0 && ismall==0 ? 0: -1);
2229 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2230 register struct ViceInodeInfo *ip,
2231 int nInodes, struct VolumeSummary *volSummary, int check)
2233 VolumeId volumeNumber;
2234 char buf[SIZEOF_LARGEDISKVNODE];
2235 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2237 StreamHandle_t *file;
2238 struct VnodeClassInfo *vcp;
2240 int vnodeIndex, nVnodes;
2241 afs_ino_str_t stmp1, stmp2;
2245 volumeNumber = volSummary->header.id;
2246 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2247 fdP = IH_OPEN(handle);
2248 assert(fdP != NULL);
2249 file = FDH_FDOPEN(fdP, "r+");
2250 assert(file != NULL)
2251 vcp = &VnodeClassInfo[class];
2252 size = OS_SIZE(fdP->fd_fd);
2254 nVnodes = (size / vcp->diskSize) - 1;
2256 assert((nVnodes+1) * vcp->diskSize == size)
2257 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2262 for (vnodeIndex = 0;
2263 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2264 nVnodes--, vnodeIndex++) {
2265 if (vnode->type != vNull) {
2266 int vnodeChanged = 0;
2267 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2268 /* Log programs that belong to root (potentially suid root);
2269 don't bother for read-only or backup volumes */
2270 #ifdef notdef /* This is done elsewhere */
2271 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2272 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2273 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2274 vnode->owner, vnode->modeBits);
2276 if (VNDISK_GET_INO(vnode) == 0) {
2278 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2279 bzero(vnode, vcp->diskSize);
2284 if (vcp->magic != vnode->vnodeMagic) {
2285 /* bad magic #, probably partially created vnode */
2286 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2287 bzero(vnode, vcp->diskSize);
2291 /* ****** Should do a bit more salvage here: e.g. make sure
2292 vnode type matches what it should be given the index */
2293 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2294 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2295 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2296 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2303 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2304 /* The following doesn't work, because the version number
2305 is not maintained correctly by the file server */
2306 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2307 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2309 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2316 /* For RW volume, look for vnode with matching inode number;
2317 if no such match, take the first determined by our sort
2319 register struct ViceInodeInfo *lip = ip;
2320 register lnInodes = nInodes;
2321 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2322 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2331 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2332 /* "Matching" inode */
2336 vu = vnode->uniquifier;
2337 iu = ip->u.vnode.vnodeUniquifier;
2338 vd = vnode->dataVersion;
2339 id = ip->u.vnode.inodeDataVersion;
2341 * Because of the possibility of the uniquifier overflows (> 4M)
2342 * we compare them modulo the low 22-bits; we shouldn't worry
2343 * about mismatching since they shouldn't to many old
2344 * uniquifiers of the same vnode...
2346 if (IUnique(vu) != IUnique(iu)) {
2348 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2349 vnodeNumber, IUnique(vu), IUnique(iu));
2352 vnode->uniquifier = iu;
2353 #ifdef AFS_3DISPARES
2354 vnode->dataVersion = (id >= vd ?
2355 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2356 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2358 #if defined(AFS_SGI_EXMAG)
2359 vnode->dataVersion = (id >= vd ?
2360 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2361 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2363 vnode->dataVersion = (id>vd ? id:vd);
2364 #endif /* AFS_SGI_EXMAG */
2365 #endif /* AFS_3DISPARES */
2369 /* don't bother checking for vd > id any more, since
2370 partial file transfers always result in this state,
2371 and you can't do much else anyway (you've already
2372 found the best data you can) */
2373 #ifdef AFS_3DISPARES
2374 if (!vnodeIsDirectory(vnodeNumber) &&
2375 ((vd < id && (id-vd) < 1887437) ||
2376 ((vd > id && (vd-id) > 1887437)))) {
2378 #if defined(AFS_SGI_EXMAG)
2379 if (!vnodeIsDirectory(vnodeNumber) &&
2380 ((vd < id && (id-vd) < 15099494) ||
2381 ((vd > id && (vd-id) > 15099494)))) {
2383 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2384 #endif /* AFS_SGI_EXMAG */
2386 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2387 vnode->dataVersion = id;
2392 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2395 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2396 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2397 PrintInode(stmp2, ip->inodeNumber),
2400 VNDISK_SET_INO(vnode, ip->inodeNumber);
2405 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2407 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2408 PrintInode(stmp2, ip->inodeNumber),
2411 VNDISK_SET_INO(vnode, ip->inodeNumber);
2414 if (ip->byteCount != vnode->length) {
2416 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2417 vnodeNumber, vnode->length, ip->byteCount);
2421 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2422 vnodeNumber, vnode->length, ip->byteCount);
2423 vnode->length = ip->byteCount;
2427 ip->linkCount--; /* Keep the inode around */
2431 else { /* no matching inode */
2432 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2433 /* No matching inode--get rid of the vnode */
2435 if (VNDISK_GET_INO(vnode)) {
2437 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2438 vnodeNumber, vnode->uniquifier,
2439 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2442 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2447 if (VNDISK_GET_INO(vnode)) {
2449 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2450 vnodeNumber, vnode->uniquifier,
2451 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2452 ctime((time_t *)&(vnode->serverModifyTime)));
2455 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime((time_t *)&(vnode->serverModifyTime)));
2457 bzero(vnode, vcp->diskSize);
2460 /* Should not reach here becuase we checked for
2461 * (inodeNumber == 0) above. And where we zero the vnode,
2462 * we also goto vnodeDone.
2466 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2470 } /* VNDISK_GET_INO(vnode) != 0 */
2472 assert(!(vnodeChanged && check));
2473 if (vnodeChanged && !Testing) {
2474 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2475 (char*)vnode, vcp->diskSize)
2477 VolumeChanged = 1; /* For break call back */
2488 struct VnodeEssence *CheckVnodeNumber(vnodeNumber)
2489 VnodeId vnodeNumber;
2492 struct VnodeInfo *vip;
2495 class = vnodeIdToClass(vnodeNumber);
2496 vip = &vnodeInfo[class];
2497 offset = vnodeIdToBitNumber(vnodeNumber);
2498 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2502 void CopyOnWrite(register struct DirSummary *dir)
2504 /* Copy the directory unconditionally if we are going to change it:
2505 * not just if was cloned.
2507 struct VnodeDiskObject vnode;
2508 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2509 Inode oldinode, newinode;
2512 if (dir->copied || Testing)
2514 DFlush(); /* Well justified paranoia... */
2516 code = IH_IREAD(vnodeInfo[vLarge].handle,
2517 vnodeIndexOffset(vcp, dir->vnodeNumber),
2518 (char*)&vnode, sizeof (vnode));
2519 assert(code == sizeof(vnode));
2520 oldinode = VNDISK_GET_INO(&vnode);
2521 /* Increment the version number by a whole lot to avoid problems with
2522 * clients that were promised new version numbers--but the file server
2523 * crashed before the versions were written to disk.
2525 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2526 dir->rwVid, dir->vnodeNumber,
2527 vnode.uniquifier, vnode.dataVersion += 200);
2528 assert(VALID_INO(newinode));
2529 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2531 VNDISK_SET_INO(&vnode, newinode);
2532 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2533 vnodeIndexOffset(vcp, dir->vnodeNumber),
2534 (char*)&vnode, sizeof (vnode));
2535 assert(code == sizeof (vnode));
2537 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2538 fileSysDevice, newinode);
2539 /* Don't delete the original inode right away, because the directory is
2540 * still being scanned.
2545 /* This function should either successfully create a new dir, or give up and leave
2546 * things the way they were. In particular, if it fails to write the new dir properly,
2547 * it should return w/o changing the reference to the old dir.
2549 void CopyAndSalvage(register struct DirSummary *dir)
2551 struct VnodeDiskObject vnode;
2552 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2553 Inode oldinode, newinode;
2555 register afs_int32 code;
2556 afs_int32 parentUnique= 1;
2557 struct VnodeEssence *vnodeEssence;
2561 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2562 code = IH_IREAD(vnodeInfo[vLarge].handle,
2563 vnodeIndexOffset(vcp, dir->vnodeNumber),
2564 (char*)&vnode, sizeof (vnode));
2565 assert(code == sizeof (vnode));
2566 oldinode = VNDISK_GET_INO(&vnode);
2567 /* Increment the version number by a whole lot to avoid problems with
2568 * clients that were promised new version numbers--but the file server
2569 * crashed before the versions were written to disk.
2571 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2572 dir->rwVid, dir->vnodeNumber,
2574 vnode.dataVersion += 200);
2575 assert(VALID_INO(newinode));
2576 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2578 /* Assign . and .. vnode numbers from dir and vnode.parent.
2579 * The uniquifier for . is in the vnode.
2580 * The uniquifier for .. might be set to a bogus value of 1 and
2581 * the salvager will later clean it up.
2583 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2584 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2586 code = DirSalvage(&dir->dirHandle, &newdir,
2587 dir->vnodeNumber, vnode.uniquifier,
2588 (vnode.parent?vnode.parent:dir->vnodeNumber),
2590 if (code == 0) code = DFlush();
2592 /* didn't really build the new directory properly, let's just give up. */
2593 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2595 Log("Directory salvage returned code %d, continuing.\n", code);
2598 Log("Checking the results of the directory salvage...\n");
2599 if (!DirOK(&newdir)) {
2600 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2601 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2606 VNDISK_SET_INO(&vnode, newinode);
2607 vnode.length = Length(&newdir);
2608 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2609 vnodeIndexOffset(vcp, dir->vnodeNumber),
2610 (char*)&vnode, sizeof (vnode));
2611 assert(code == sizeof (vnode));
2613 nt_sync(fileSysDevice);
2615 sync(); /* this is slow, but hopefully rarely called. We don't have
2616 * an open FD on the file itself to fsync.
2619 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2621 dir->dirHandle = newdir;
2624 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2627 struct VnodeEssence *vnodeEssence;
2628 afs_int32 dirOrphaned, todelete;
2630 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2632 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2633 if (vnodeEssence == NULL) {
2635 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2636 dir->vnodeNumber, dir->name?dir->name:"??",
2637 name, vnodeNumber, unique);
2641 assert(Delete(&dir->dirHandle, name) == 0)
2647 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2648 * mount inode for the partition. If this inode were deleted, it would crash
2651 if (vnodeEssence->InodeNumber == 0) {
2652 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2653 dir->vnodeNumber, (dir->name?dir->name:"??"),
2654 name, vnodeNumber, unique,
2655 (Testing?"-- would have deleted":" -- deleted"));
2658 assert(Delete(&dir->dirHandle, name) == 0);
2664 if (!(vnodeNumber & 1) && !Showmode &&
2665 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2666 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2667 dir->vnodeNumber, (dir->name?dir->name:"??"),
2668 name, vnodeNumber, unique,
2669 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2673 assert(Delete(&dir->dirHandle, name) == 0);
2679 /* Check if the Uniquifiers match. If not, change the directory entry
2680 * so its unique matches the vnode unique. Delete if the unique is zero
2681 * or if the directory is orphaned.
2683 if (!IUnique(vnodeEssence->unique) ||
2684 (IUnique(vnodeEssence->unique) != IUnique(unique)) ) {
2685 if ( !IUnique(vnodeEssence->unique) &&
2686 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2687 /* This is an orphaned directory. Don't delete the . or ..
2688 * entry. Otherwise, it will get created in the next
2689 * salvage and deleted again here. So Just skip it.
2694 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2697 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2698 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2699 name, vnodeNumber, unique, vnodeEssence->unique,
2700 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2704 fid.Vnode = vnodeNumber;
2705 fid.Unique = vnodeEssence->unique;
2707 assert(Delete(&dir->dirHandle, name) == 0)
2709 assert(Create(&dir->dirHandle, name, &fid) == 0)
2711 if (todelete) return; /* no need to continue */
2714 if (strcmp(name,".") == 0) {
2715 if (dir->vnodeNumber != vnodeNumber || (IUnique(dir->unique) != IUnique(unique))) {
2717 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2718 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2721 assert(Delete(&dir->dirHandle, ".") == 0)
2722 fid.Vnode = dir->vnodeNumber;
2723 fid.Unique = dir->unique;
2724 assert(Create(&dir->dirHandle, ".", &fid) == 0)
2727 vnodeNumber = fid.Vnode; /* Get the new Essence */
2728 unique = fid.Unique;
2729 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2733 else if (strcmp(name,"..") == 0) {
2736 struct VnodeEssence *dotdot;
2737 pa.Vnode = dir->parent;
2738 dotdot = CheckVnodeNumber(pa.Vnode);
2739 assert (dotdot != NULL); /* XXX Should not be assert */
2740 pa.Unique = dotdot->unique;
2743 pa.Vnode = dir->vnodeNumber;
2744 pa.Unique = dir->unique;
2746 if ((pa.Vnode != vnodeNumber) || (IUnique(pa.Unique) != IUnique(unique))) {
2747 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2748 dir->vnodeNumber, IUnique(dir->unique), vnodeNumber, IUnique(unique));
2751 assert(Delete(&dir->dirHandle, "..") == 0);
2752 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2755 vnodeNumber = pa.Vnode; /* Get the new Essence */
2757 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2759 dir->haveDotDot = 1;
2760 } else if (strncmp(name,".__afs",6) == 0) {
2762 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2763 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2767 assert(Delete(&dir->dirHandle, name) == 0)
2769 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2770 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2774 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2775 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2776 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber, dir->vnodeNumber);
2777 if (ShowMounts && (vnodeEssence->type == vSymlink) && !(vnodeEssence->modeBits & 0111)) {
2783 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2784 vnodeEssence->InodeNumber);
2786 assert(fdP != NULL);
2787 size = FDH_SIZE(fdP);
2790 if (size > 1024) size = 1024;
2791 code = FDH_READ(fdP, buf, size);
2792 assert(code == size);
2793 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2794 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2795 dir->name?dir->name:"??", name, buf);
2796 FDH_REALLYCLOSE(fdP);
2799 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2800 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2801 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2802 if (vnodeIdToClass(vnodeNumber) == vLarge && vnodeEssence->name == (char *)0) {
2804 if (n = (char*)malloc(strlen(name)+1))
2806 vnodeEssence->name = n;
2809 /* The directory entry points to the vnode. Check to see if the
2810 * vnode points back to the directory. If not, then let the
2811 * directory claim it (else it might end up orphaned). Vnodes
2812 * already claimed by another directory are deleted from this
2813 * directory: hardlinks to the same vnode are not allowed
2814 * from different directories.
2816 if (vnodeEssence->parent != dir->vnodeNumber) {
2817 if (!vnodeEssence->claimed && !dirOrphaned) {
2818 /* Vnode does not point back to this directory.
2819 * Orphaned dirs cannot claim a file (it may belong to
2820 * another non-orphaned dir).
2823 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2824 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2825 vnodeNumber, unique, (Testing?"would have been ":""),
2826 vnodeEssence->parent, dir->vnodeNumber);
2828 vnodeEssence->parent = dir->vnodeNumber;
2829 vnodeEssence->changed = 1;
2831 /* Vnode was claimed by another directory */
2834 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2835 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2836 vnodeEssence->parent, vnodeNumber, unique,
2837 (Testing?"would have been ":""));
2839 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2840 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2841 vnodeEssence->parent, vnodeNumber, unique,
2842 (Testing?"would have been ":""));
2847 assert(Delete(&dir->dirHandle, name) == 0);
2852 /* This directory claims the vnode */
2853 vnodeEssence->claimed = 1;
2855 vnodeEssence->count--;
2858 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2861 register struct VnodeInfo *vip = &vnodeInfo[class];
2862 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2863 char buf[SIZEOF_LARGEDISKVNODE];
2864 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2867 StreamHandle_t *file;
2872 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2873 fdP = IH_OPEN(vip->handle);
2875 file = FDH_FDOPEN(fdP, "r+");
2876 assert(file != NULL);
2877 size = OS_SIZE(fdP->fd_fd);
2879 vip->nVnodes = (size / vcp->diskSize) - 1;
2880 if (vip->nVnodes > 0) {
2881 assert((vip->nVnodes+1)*vcp->diskSize == size)
2882 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2883 assert((vip->vnodes = (struct VnodeEssence *)
2884 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL)
2885 if (class == vLarge) {
2886 assert((vip->inodes = (Inode *)
2887 calloc(vip->nVnodes, sizeof (Inode))) != NULL)
2898 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2899 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2900 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2901 nVnodes--, vnodeIndex++) {
2902 if (vnode->type != vNull) {
2903 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2904 vip->nAllocatedVnodes++;
2905 vep->count = vnode->linkCount;
2906 vep->blockCount = nBlocks(vnode->length);
2907 vip->volumeBlockCount += vep->blockCount;
2908 vep->parent = vnode->parent;
2909 vep->unique = vnode->uniquifier;
2910 if (*maxu < vnode->uniquifier)
2911 *maxu = vnode->uniquifier;
2912 vep->modeBits = vnode->modeBits;
2913 vep->InodeNumber = VNDISK_GET_INO(vnode);
2914 vep->type = vnode->type;
2915 vep->author = vnode->author;
2916 vep->owner = vnode->owner;
2917 vep->group = vnode->group;
2918 if (vnode->type == vDirectory) {
2919 assert(class == vLarge)
2920 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2928 static char *GetDirName(vnode, vp, path)
2930 struct VnodeEssence *vp;
2933 struct VnodeEssence *parentvp;
2939 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
2941 strcat(path, vp->name);
2947 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
2948 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
2950 static int IsVnodeOrphaned(vnode)
2953 struct VnodeEssence *vep;
2955 if (vnode == 0) return(1); /* Vnode zero does not exist */
2956 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
2957 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
2958 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
2960 return( IsVnodeOrphaned(vep->parent) );
2963 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
2964 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
2967 static struct DirSummary dir;
2968 static struct DirHandle dirHandle;
2969 struct VnodeEssence *parent;
2970 static char path[MAXPATHLEN];
2973 if (dirVnodeInfo->vnodes[i].salvaged)
2974 return; /* already salvaged */
2977 dirVnodeInfo->vnodes[i].salvaged = 1;
2979 if (dirVnodeInfo->inodes[i] == 0)
2980 return; /* Not allocated to a directory */
2982 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
2983 if (parent && parent->salvaged == 0)
2984 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
2985 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
2986 rootdir, rootdirfound);
2987 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
2988 dir.unique = dirVnodeInfo->vnodes[i].unique;
2991 dir.parent = dirVnodeInfo->vnodes[i].parent;
2992 dir.haveDot = dir.haveDotDot = 0;
2993 dir.ds_linkH = alinkH;
2994 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
2996 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
2999 Log("Directory bad, vnode %d; %s...\n",
3000 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3003 CopyAndSalvage(&dir);
3007 dirHandle = dir.dirHandle;
3009 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3012 /* If enumeration failed for random reasons, we will probably delete
3013 * too much stuff, so we guard against this instead.
3015 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3018 /* Delete the old directory if it was copied in order to salvage.
3019 * CopyOnWrite has written the new inode # to the disk, but we still
3020 * have the old one in our local structure here. Thus, we idec the
3024 if (dir.copied && !Testing) {
3025 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3027 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3030 /* Remember rootdir DirSummary _after_ it has been judged */
3031 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3032 bcopy(&dir, rootdir, sizeof(struct DirSummary));
3039 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3041 /* This routine, for now, will only be called for read-write volumes */
3043 int BlocksInVolume = 0, FilesInVolume = 0;
3044 register VnodeClass class;
3045 struct DirSummary rootdir, oldrootdir;
3046 struct VnodeInfo *dirVnodeInfo;
3047 struct VnodeDiskObject vnode;
3048 VolumeDiskData volHeader;
3050 int orphaned, rootdirfound = 0;
3051 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3052 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3053 struct VnodeEssence *vep;
3058 VnodeId LFVnode, ThisVnode;
3059 Unique LFUnique, ThisUnique;
3062 vid = rwIsp->volSummary->header.id;
3063 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3064 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3065 assert(nBytes == sizeof(volHeader));
3066 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3067 assert (volHeader.destroyMe != DESTROY_ME);
3068 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3070 DistilVnodeEssence(vid, vLarge,
3071 rwIsp->volSummary->header.largeVnodeIndex,
3073 DistilVnodeEssence(vid, vSmall,
3074 rwIsp->volSummary->header.smallVnodeIndex,
3077 dirVnodeInfo = &vnodeInfo[vLarge];
3078 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3079 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3080 &rootdir, &rootdirfound);
3087 /* Parse each vnode looking for orphaned vnodes and
3088 * connect them to the tree as orphaned (if requested).
3090 oldrootdir = rootdir;
3091 for (class=0; class < nVNODECLASSES; class++) {
3092 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3093 vep = &(vnodeInfo[class].vnodes[v]);
3094 ThisVnode = bitNumberToVnodeNumber(v, class);
3095 ThisUnique = vep->unique;
3097 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3098 continue; /* Ignore unused, claimed, and root vnodes */
3100 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3101 * entry in this vnode had incremented the parent link count (In
3102 * JudgeEntry()). We need to go to the parent and decrement that
3103 * link count. But if the parent's unique is zero, then the parent
3104 * link count was not incremented in JudgeEntry().
3106 if (class == vLarge) { /* directory vnode */
3107 pv = vnodeIdToBitNumber(vep->parent);
3108 if (IUnique(vnodeInfo[vLarge].vnodes[pv].unique) != 0)
3109 vnodeInfo[vLarge].vnodes[pv].count++;
3113 continue; /* If no rootdir, can't attach orphaned files */
3115 /* Here we attach orphaned files and directories into the
3116 * root directory, LVVnode, making sure link counts stay correct.
3118 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3119 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3120 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3122 /* Update this orphaned vnode's info. Its parent info and
3123 * link count (do for orphaned directories and files).
3125 vep->parent = LFVnode; /* Parent is the root dir */
3126 vep->unique = LFUnique;
3129 vep->count--; /* Inc link count (root dir will pt to it) */
3131 /* If this orphaned vnode is a directory, change '..'.
3132 * The name of the orphaned dir/file is unknown, so we
3133 * build a unique name. No need to CopyOnWrite the directory
3134 * since it is not connected to tree in BK or RO volume and
3135 * won't be visible there.
3137 if (class == vLarge) {
3141 /* Remove and recreate the ".." entry in this orphaned directory */
3142 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3144 pa.Unique = LFUnique;
3145 assert(Delete(&dh, "..") == 0);
3146 assert(Create(&dh, "..", &pa) == 0);
3148 /* The original parent's link count was decremented above.
3149 * Here we increment the new parent's link count.
3151 pv = vnodeIdToBitNumber(LFVnode);
3152 vnodeInfo[vLarge].vnodes[pv].count--;
3156 /* Go to the root dir and add this entry. The link count of the
3157 * root dir was incremented when ".." was created. Try 10 times.
3159 for (j=0; j<10; j++) {
3160 pa.Vnode = ThisVnode;
3161 pa.Unique = ThisUnique;
3163 sprintf(npath, "%s.%d.%d",
3164 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3165 ThisVnode, ThisUnique);
3167 CopyOnWrite(&rootdir);
3168 code = Create(&rootdir.dirHandle, npath, &pa);
3171 ThisUnique += 50; /* Try creating a different file */
3174 Log("Attaching orphaned %s to volume's root dir as %s\n",
3175 ((class == vLarge)?"directory":"file"), npath);
3177 } /* for each vnode in the class */
3178 } /* for each class of vnode */
3180 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3182 if (!oldrootdir.copied && rootdir.copied) {
3183 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3185 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3188 DFlush(); /* Flush the changes */
3189 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3190 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3191 orphans = ORPH_IGNORE;
3194 /* Write out all changed vnodes. Orphaned files and directories
3195 * will get removed here also (if requested).
3197 for (class = 0; class < nVNODECLASSES; class++){
3198 int nVnodes = vnodeInfo[class].nVnodes;
3199 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3200 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3201 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3202 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3203 for (i = 0; i<nVnodes; i++) {
3204 register struct VnodeEssence *vnp = &vnodes[i];
3205 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3207 /* If the vnode is good but is unclaimed (not listed in
3208 * any directory entries), then it is orphaned.
3211 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3212 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3216 if (vnp->changed || vnp->count) {
3219 nBytes = IH_IREAD(vnodeInfo[class].handle,
3220 vnodeIndexOffset(vcp, vnodeNumber),
3221 (char*)&vnode, sizeof (vnode));
3222 assert(nBytes == sizeof(vnode));
3224 vnode.parent = vnp->parent;
3225 oldCount = vnode.linkCount;
3226 vnode.linkCount = vnode.linkCount - vnp->count;
3229 orphaned = IsVnodeOrphaned(vnodeNumber);
3231 if (!vnp->todelete) {
3232 /* Orphans should have already been attached (if requested) */
3233 assert(orphans != ORPH_ATTACH);
3234 oblocks += vnp->blockCount;
3237 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3238 BlocksInVolume -= vnp->blockCount;
3240 if (VNDISK_GET_INO(&vnode)) {
3241 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3244 bzero(&vnode, sizeof(vnode));
3246 } else if (vnp->count) {
3248 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3249 vnodeNumber, oldCount,
3250 (Testing?"would have changed to":"now"), vnode.linkCount);
3254 vnode.dataVersion++;
3256 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3257 vnodeIndexOffset(vcp, vnodeNumber),
3258 (char*)&vnode, sizeof (vnode));
3259 assert(nBytes == sizeof(vnode));
3265 if (!Showmode && ofiles) {
3266 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3267 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3271 for (class = 0; class < nVNODECLASSES; class++) {
3272 register struct VnodeInfo *vip = &vnodeInfo[class];
3273 for (i=0; i<vip->nVnodes; i++)
3274 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3275 if (vip->vnodes) free(vip->vnodes);
3276 if (vip->inodes) free(vip->inodes);
3279 /* Set correct resource utilization statistics */
3280 volHeader.filecount = FilesInVolume;
3281 volHeader.diskused = BlocksInVolume;
3283 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3284 if (volHeader.uniquifier < (maxunique + 1)) {
3285 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3286 /* Plus 2,000 in case there are workstations out there with
3287 * cached vnodes that have since been deleted
3289 volHeader.uniquifier = (maxunique + 1 + 2000);
3292 /* Turn off the inUse bit; the volume's been salvaged! */
3293 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3294 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3295 volHeader.inService = 1; /* allow service again */
3296 volHeader.needsCallback = (VolumeChanged != 0);
3297 volHeader.dontSalvage = DONT_SALVAGE;
3300 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3301 assert(nBytes == sizeof(volHeader));
3304 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3305 (Testing?"It would have ":""), volHeader.name,
3306 volHeader.id, FilesInVolume, BlocksInVolume);
3308 IH_RELEASE(vnodeInfo[vSmall].handle);
3309 IH_RELEASE(vnodeInfo[vLarge].handle);
3314 void ClearROInUseBit(struct VolumeSummary *summary)
3316 IHandle_t *h = summary->volumeInfoHandle;
3319 VolumeDiskData volHeader;
3321 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3322 assert(nBytes == sizeof(volHeader));
3323 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC)
3324 volHeader.inUse = 0;
3325 volHeader.needsSalvaged = 0;
3326 volHeader.inService = 1;
3327 volHeader.dontSalvage = DONT_SALVAGE;
3329 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3330 assert(nBytes == sizeof(volHeader));
3335 * Possible delete the volume.
3337 * deleteMe - Always do so, only a partial volume.
3339 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3343 if (readOnly(isp) || deleteMe) {
3344 if (isp->volSummary && isp->volSummary->fileName) {
3346 if (!Showmode) 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);
3347 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3349 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3350 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3353 unlink(isp->volSummary->fileName);
3357 Log("%s salvage was unsuccessful: read-write volume %u\n",
3358 message, isp->volumeId);
3359 Abort("Salvage of volume %u aborted\n",
3365 void AskOffline(VolumeId volumeId)
3367 if (FSYNC_askfs(volumeId, (char *)0, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3368 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3369 Abort("Salvage aborted\n");
3373 void AskOnline(VolumeId volumeId, char *partition)
3375 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3376 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3377 volumeId, partition);
3381 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3383 /* Volume parameter is passed in case iopen is upgraded in future to
3384 * require a volume Id to be passed
3387 IHandle_t *srcH, *destH;
3388 FdHandle_t *srcFdP, *destFdP;
3391 IH_INIT(srcH, device, rwvolume, inode1);
3392 srcFdP = IH_OPEN(srcH);
3393 assert(srcFdP != NULL);
3394 IH_INIT(destH, device, rwvolume, inode2);
3395 destFdP = IH_OPEN(destH);
3397 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3398 assert(FDH_WRITE(destFdP, buf, n) == n);
3400 FDH_REALLYCLOSE(srcFdP);
3401 FDH_REALLYCLOSE(destFdP);
3407 void PrintInodeList(void)
3409 register struct ViceInodeInfo *ip;
3410 struct ViceInodeInfo *buf;
3414 assert(fstat(inodeFd, &status) == 0);
3415 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3416 assert(buf != NULL);
3417 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3418 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3419 for(ip = buf; nInodes--; ip++) {
3420 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3421 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3422 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3427 void PrintInodeSummary(void)
3430 struct InodeSummary *isp;
3432 for (i=0; i<nVolumesInInodeFile; i++) {
3433 isp = &inodeSummary[i];
3434 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3435 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3436 isp->nSpecialInodes, isp->maxUniquifier);
3440 void PrintVolumeSummary(void)
3443 struct VolumeSummary *vsp;
3445 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3446 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3454 assert(0); /* Fork is never executed in the NT code path */
3465 if (ShowLog) showlog();
3467 if (main_thread != pthread_self())
3468 pthread_exit((void*)code);
3476 int Wait(char *prog)
3480 pid = wait(&status);
3482 if (WCOREDUMP(status))
3483 Log("\"%s\" core dumped!\n", prog);
3484 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3489 static char *TimeStamp(time_t clock, int precision)
3492 static char timestamp[20];
3493 lt = localtime(&clock);
3495 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3497 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3501 void CheckLogFile(void)
3503 char oldSlvgLog[AFSDIR_PATH_MAX];
3505 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3506 strcat(oldSlvgLog, ".old");
3508 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3509 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3511 if (!logFile) { /* still nothing, use stdout */
3516 #ifndef AFS_NAMEI_ENV
3517 AFS_DEBUG_IOPS_LOG(logFile);
3529 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3532 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3535 while (fgets(line, sizeof(line), logFile))
3541 void Log(a,b,c,d,e,f,g,h,i,j,k)
3542 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3545 gettimeofday(&now, 0);
3546 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3547 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3551 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3552 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3554 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3556 if (ShowLog) showlog();
3562 char * ToString(char *s)
3565 p = (char *) malloc(strlen(s)+1);
3572 /* Remove the FORCESALVAGE file */
3573 void RemoveTheForce(char *path)
3575 if (!Testing && ForceSalvage) {
3576 if (chdir(path) == 0)
3577 unlink("FORCESALVAGE");
3581 #ifndef AFS_AIX32_ENV
3583 * UseTheForceLuke - see if we can use the force
3585 int UseTheForceLuke(char *path)
3589 assert(chdir(path) != -1);
3591 return (stat("FORCESALVAGE", &force) == 0);
3595 * UseTheForceLuke - see if we can use the force
3598 * The VRMIX fsck will not muck with the filesystem it is supposedly
3599 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3600 * muck directly with the root inode, which is within the normal
3602 * ListViceInodes() has a side effect of setting ForceSalvage if
3603 * it detects a need, based on root inode examination.
3605 int UseTheForceLuke(char *path)
3608 return 0; /* sorry OB1 */
3613 /* NT support routines */
3615 static char execpathname[MAX_PATH];
3616 int nt_SalvagePartition(char *partName, int jobn)
3621 if (!*execpathname) {
3622 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3623 if (!n || n == 1023)
3626 job.cj_magic = SALVAGER_MAGIC;
3627 job.cj_number = jobn;
3628 (void) strcpy(job.cj_part, partName);
3629 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3634 int nt_SetupPartitionSalvage(void *datap, int len)
3636 childJob_t *jobp = (childJob_t*)datap;
3637 char logname[AFSDIR_PATH_MAX];
3639 if (len != sizeof(childJob_t))
3641 if (jobp->cj_magic != SALVAGER_MAGIC)
3646 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3648 logFile = fopen(logname, "w");
3649 if (!logFile) logFile = stdout;
3655 #endif /* AFS_NT40_ENV */