2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #define SalvageVersion "2.4"
88 /* Main program file. Define globals. */
91 #include <afsconfig.h>
92 #include <afs/param.h>
100 #include <sys/stat.h>
105 #include <WINNT/afsevent.h>
107 #include <sys/param.h>
108 #include <sys/file.h>
110 #include <sys/time.h>
111 #endif /* ITIMER_REAL */
113 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
114 #define WCOREDUMP(x) (x & 0200)
117 #include <afs/afsint.h>
118 #include <afs/assert.h>
119 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
120 #if defined(AFS_VFSINCL_ENV)
121 #include <sys/vnode.h>
123 #include <sys/fs/ufs_inode.h>
125 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
126 #include <ufs/ufs/dinode.h>
127 #include <ufs/ffs/fs.h>
129 #include <ufs/inode.h>
132 #else /* AFS_VFSINCL_ENV */
134 #include <ufs/inode.h>
135 #else /* AFS_OSF_ENV */
136 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
137 #include <sys/inode.h>
140 #endif /* AFS_VFSINCL_ENV */
141 #endif /* AFS_SGI_ENV */
144 #include <sys/lockf.h>
148 #include <checklist.h>
150 #if defined(AFS_SGI_ENV)
155 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
158 #include <sys/mnttab.h>
159 #include <sys/mntent.h>
164 #endif /* AFS_SGI_ENV */
165 #endif /* AFS_HPUX_ENV */
170 #include <afs/osi_inode.h>
173 #include <afs/afsutil.h>
174 #include <afs/fileutil.h>
175 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
183 #include <afs/afssyscalls.h>
187 #include "partition.h"
189 #include "viceinode.h"
191 #include "volinodes.h" /* header magic number, etc. stuff */
197 extern void *calloc();
199 extern char *vol_DevName();
200 static char *TimeStamp(time_t clock, int precision);
202 #define ORPH_IGNORE 0
203 #define ORPH_REMOVE 1
204 #define ORPH_ATTACH 2
207 int debug; /* -d flag */
208 int Testing=0; /* -n flag */
209 int ListInodeOption; /* -i flag */
210 int ShowRootFiles; /* -r flag */
211 int RebuildDirs; /* -sal flag */
212 int Parallel = 4; /* -para X flag */
213 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
214 int forceR = 0; /* -b flag */
215 int ShowLog = 0; /* -showlog flag */
216 int ShowSuid = 0; /* -showsuid flag */
217 int ShowMounts = 0; /* -showmounts flag */
218 int orphans = ORPH_IGNORE; /* -orphans option */
222 int useSyslog = 0; /* -syslog flag */
223 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
226 #define MAXPARALLEL 32
228 int OKToZap; /* -o flag */
229 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
230 in the volume header */
232 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
234 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
236 Device fileSysDevice; /* The device number of the current
237 partition being salvaged */
241 char *fileSysPath; /* The path of the mounted partition currently
242 being salvaged, i.e. the directory
243 containing the volume headers */
245 char *fileSysPathName; /* NT needs this to make name pretty in log. */
246 IHandle_t *VGLinkH; /* Link handle for current volume group. */
247 int VGLinkH_cnt; /* # of references to lnk handle. */
248 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
250 char *fileSysDeviceName; /* The block device where the file system
251 being salvaged was mounted */
252 char *filesysfulldev;
254 int VolumeChanged; /* Set by any routine which would change the volume in
255 a way which would require callback is to be broken if the
256 volume was put back on line by an active file server */
258 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
260 struct InodeSummary { /* Inode summary file--an entry for each
261 volume in the inode file for a partition */
262 VolId volumeId; /* Volume id */
263 VolId RWvolumeId; /* RW volume associated */
264 int index; /* index into inode file (0, 1, 2 ...) */
265 int nInodes; /* Number of inodes for this volume */
266 int nSpecialInodes; /* Number of special inodes, i.e. volume
267 header, index, etc. These are all
268 marked (viceinode.h) and will all be sorted
269 to the beginning of the information for
270 this volume. Read-only volumes should
271 ONLY have special inodes (all the other
272 inodes look as if they belong to the
273 original RW volume). */
274 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
275 This is only useful for RW volumes and is used
276 to compute a new volume uniquifier in the event
277 that the header needs to be recreated. The inode
278 uniquifier may be a truncated version of vnode
279 uniquifier (AFS_3DISPARES). The real maxUniquifer
280 is from the vnodes and later calcuated from it */
281 struct VolumeSummary *volSummary;
282 /* Either a pointer to the original volume
283 header summary, or constructed summary
286 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
287 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
288 int inodeFd; /* File descriptor for inode file */
291 struct VolumeSummary { /* Volume summary an entry for each
292 volume in a volume directory.
293 Assumption: one volume directory per
295 char *fileName; /* File name on the partition for the volume
297 struct VolumeHeader header;
298 /* volume number, rw volume number, inode
299 numbers of each major component of
301 IHandle_t *volumeInfoHandle;
302 byte wouldNeedCallback; /* set if the file server should issue
303 call backs for all the files in this volume when
304 the volume goes back on line */
308 IHandle_t *handle; /* Inode containing this index */
309 int nVnodes; /* Total number of vnodes in index */
310 int nAllocatedVnodes; /* Total number actually used */
311 int volumeBlockCount; /* Total number of blocks used by volume */
312 Inode *inodes; /* Directory only */
313 struct VnodeEssence {
314 short count; /* Number of references to vnode; MUST BE SIGNED */
315 unsigned claimed:1; /* Set when a parent directory containing an entry
316 referencing this vnode is found. The claim
317 is that the parent in "parent" can point to
318 this vnode, and no other */
319 unsigned changed:1; /* Set if any parameters (other than the count)
320 in the vnode change. It is determined if the
321 link count has changed by noting whether it is
322 0 after scanning all directories */
323 unsigned salvaged:1;/* Set if this directory vnode has already been salvaged. */
324 unsigned todelete:1;/* Set if this vnode is to be deleted (should not be claimed) */
325 afs_uint32 blockCount;
326 /* Number of blocks (1K) used by this vnode,
328 VnodeId parent; /* parent in vnode */
329 Unique unique; /* Must match entry! */
330 char *name; /* Name of directory entry */
331 int modeBits; /* File mode bits */
332 Inode InodeNumber; /* file's inode */
333 int type; /* File type */
334 int author; /* File author */
335 int owner; /* File owner */
336 int group; /* File group */
338 } vnodeInfo[nVNODECLASSES];
341 struct DirHandle dirHandle;
344 unsigned haveDot, haveDotDot;
346 int copied; /* If the copy-on-write stuff has been applied */
354 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
355 int nVolumes; /* Number of volumes (read-write and read-only)
359 /* For NT, we can fork the per partition salvagers to gain the required
360 * safety against Aborts. But there's too many complex data structures at
361 * the per volume salvager layer to easilty copy the data across.
362 * childJobNumber is resset from -1 to the job number if this is a
363 * per partition child of the main salvager. This information is passed
364 * out-of-band in the extra data area setup for the now unused parent/child
367 #define SALVAGER_MAGIC 0x00BBaaDD
368 #define NOT_CHILD -1 /* job numbers start at 0 */
369 /* If new options need to be passed to child, add them here. */
376 /* Child job this process is running. */
377 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD};
379 int nt_SalvagePartition(char *partName, int jobn);
380 int nt_SetupPartitionSalvage(void *datap, int len);
383 struct InodeSummary *svgp_inodeSummaryp;
393 /* Forward declarations */
394 void Log(), Abort(), Exit();
396 int Wait(char *prog);
397 char * ToString(char *s);
398 void AskOffline(VolumeId volumeId);
399 void AskOnline(VolumeId volumeId, char *partition);
400 void CheckLogFile(void);
401 void ClearROInUseBit(struct VolumeSummary *summary);
402 void CopyAndSalvage(register struct DirSummary *dir);
403 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
404 void CopyOnWrite(register struct DirSummary *dir);
405 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
406 register struct InodeSummary * summary);
407 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
408 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
410 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
411 void GetVolumeSummary(VolumeId singleVolumeNumber);
412 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
414 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
417 void ObtainSalvageLock(void);
418 void PrintInodeList(void);
419 void PrintInodeSummary(void);
420 void PrintVolumeSummary(void);
421 int QuickCheck(register struct InodeSummary *isp, int nVols);
422 void RemoveTheForce(char *path);
423 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
424 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
426 void SalvageFileSysParallel(struct DiskPartition *partP);
427 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
428 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
429 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
430 int check, int *deleteMe);
431 int SalvageIndex(Inode ino, VnodeClass class, int RW,
432 register struct ViceInodeInfo *ip,
433 int nInodes, struct VolumeSummary *volSummary, int check);
434 int SalvageVnodes(register struct InodeSummary *rwIsp,
435 register struct InodeSummary * thisIsp,
436 register struct ViceInodeInfo * inodes, int check);
437 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
438 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
440 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
442 #define SalvageVolumeGroup DoSalvageVolumeGroup
444 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
445 register struct ViceInodeInfo *inodes,
446 int RW, int check, int *deleteMe);
448 int UseTheForceLuke(char *path);
450 static int IsVnodeOrphaned(VnodeId vnode);
452 /* Uniquifier stored in the Inode */
453 static Unique IUnique(u)
457 return(u & 0x3fffff);
459 #if defined(AFS_SGI_EXMAG)
460 return(u & SGI_UNIQMASK);
463 #endif /* AFS_SGI_EXMAG */
467 static int BadError(aerror)
468 register int aerror; {
469 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
471 return 0; /* otherwise may be transient, e.g. EMFILE */
477 struct cmd_syndesc *as;
479 register struct cmd_item *ti;
480 char pname[100], *temp;
481 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0, i;
482 struct DiskPartition *partP;
484 #ifdef AFS_SGI_VNODE_GLUE
485 if (afs_init_kernel_config(-1) <0) {
486 printf("Can't determine NUMA configuration, not starting salvager.\n");
492 for (i = 0; i < CMD_MAXPARMS; i++) {
493 if (as->parms[i].items) {
499 printf("Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!\n");
502 #endif /* FAST_RESTART */
503 if (ti = as->parms[0].items) { /* -partition */
505 strncpy(pname, ti->data, 100);
507 if (ti = as->parms[1].items) { /* -volumeid */
509 printf("You must also specify '-partition' option with the '-volumeid' option\n");
513 vid = atoi(ti->data);
515 if (as->parms[2].items) /* -debug */
517 if (as->parms[3].items) /* -nowrite */
519 if (as->parms[4].items) /* -inodes */
521 if (as->parms[5].items) /* -force */
523 if (as->parms[6].items) /* -oktozap */
525 if (as->parms[7].items) /* -rootinodes */
527 if (as->parms[8].items) /* -RebuildDirs */
529 if (as->parms[9].items) /* -ForceReads */
531 if (ti = as->parms[10].items) {/* -Parallel # */
533 if (strncmp(temp,"all",3) == 0) {
537 if (strlen(temp) != 0) {
538 Parallel = atoi(temp);
539 if (Parallel < 1) Parallel = 1;
540 if (Parallel > MAXPARALLEL) {
541 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
542 Parallel = MAXPARALLEL;
546 if (ti = as->parms[11].items) {/* -tmpdir */
550 dirp = opendir(tmpdir);
552 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
557 if (ti = as->parms[12].items) /* -showlog */
559 if (ti = as->parms[13].items) { /* -log */
564 if (ti = as->parms[14].items) { /* -showmounts */
569 if (ti = as->parms[15].items) { /* -orphans */
571 orphans = ORPH_IGNORE;
572 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
573 orphans = ORPH_REMOVE;
574 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
575 orphans = ORPH_ATTACH;
578 #ifndef AFS_NT40_ENV /* ignore options on NT */
579 if ( ti = as->parms[16].items) { /* -syslog */
583 if ( ti = as->parms[17].items) { /* -syslogfacility */
584 useSyslogFacility = atoi(ti->data);
590 if (ti = as->parms[18].items) { /* -DontSalvage */
591 printf("Exiting immediately without salvage. Look into the FileLog");
592 printf(" to find volumes which really need to be salvaged!\n");
595 #endif /* FAST_RESTART */
597 /* Note: if seemvol we initialize this as a standard volume utility: this has the
598 implication that the file server may be running; negotations have to be made with
599 the file server in this case to take the read write volume and associated read-only
600 volumes off line before salvaging */
603 if (afs_winsockInit()<0) {
604 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
605 AFSDIR_SALVAGER_FILE, 0);
606 Log("Failed to initailize winsock, exiting.\n");
611 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
614 if (myjob.cj_number != NOT_CHILD) {
617 (void) strcpy(pname, myjob.cj_part);
622 for (partP = DiskPartitionList; partP; partP = partP->next) {
623 SalvageFileSysParallel(partP);
625 SalvageFileSysParallel(0);
628 partP = VGetPartition(pname, 0);
630 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
635 SalvageFileSys(partP, 0);
637 /* Salvage individual volume */
639 Log("salvage: invalid volume id specified; salvage aborted\n");
642 SalvageFileSys (partP, vid);
650 #include "AFS_component_version_number.c"
654 char *save_args[MAX_ARGS];
656 pthread_t main_thread;
662 struct cmd_syndesc *ts;
664 char commandLine[150];
667 extern char cml_version_number[];
671 * The following signal action for AIX is necessary so that in case of a
672 * crash (i.e. core is generated) we can include the user's data section
673 * in the core dump. Unfortunately, by default, only a partial core is
674 * generated which, in many cases, isn't too useful.
676 struct sigaction nsa;
678 sigemptyset(&nsa.sa_mask);
679 nsa.sa_handler = SIG_DFL;
680 nsa.sa_flags = SA_FULLDUMP;
681 sigaction(SIGABRT, &nsa, NULL);
682 sigaction(SIGSEGV, &nsa, NULL);
685 /* Initialize directory paths */
686 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
688 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
690 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
694 main_thread = pthread_self();
695 if (spawnDatap && spawnDataLen) {
696 /* This is a child per partition salvager. Don't setup log or
697 * try to lock the salvager lock.
699 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
704 for (commandLine[0] = '\0', i=0; i<argc; i++) {
705 if (i > 0) strcat(commandLine, " ");
706 strcat(commandLine, argv[i]);
709 /* All entries to the log will be appended. Useful if there are
710 * multiple salvagers appending to the log.
715 #ifdef AFS_LINUX20_ENV
716 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
718 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
724 if (geteuid() != 0) {
725 printf("Salvager must be run as root.\n");
731 /* bad for normal help flag processing, but can do nada */
733 fprintf(logFile, "%s\n", cml_version_number);
734 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
736 /* Get and hold a lock for the duration of the salvage to make sure
737 * that no other salvage runs at the same time. The routine
738 * VInitVolumePackage (called below) makes sure that a file server or
739 * other volume utilities don't interfere with the salvage.
746 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
747 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
748 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
749 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
750 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
751 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
752 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
753 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
754 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
755 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
756 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
757 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
758 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
759 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
760 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
761 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
762 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
764 /* note - syslog isn't avail on NT, but if we make it conditional, have
765 to deal with screwy offsets for cmd params */
766 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
767 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
770 cmd_AddParm(ts, "-DontSalvage", CMD_FLAG, CMD_OPTIONAL, "Don't salvage. This my be set in BosConfig to let the fileserver restart immediately after a crash. Bad volumes will be taken offline");
771 #endif /* FAST_RESTART */
772 err = cmd_Dispatch(argc, argv);
776 /* Get the salvage lock if not already held. Hold until process exits. */
777 void ObtainSalvageLock(void)
782 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
783 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
785 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
787 "salvager: There appears to be another salvager running! Aborted.\n");
791 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
792 assert(salvageLock >= 0);
793 #ifdef AFS_DARWIN_ENV
794 if (flock(salvageLock, LOCK_EX) == -1) {
796 if (lockf(salvageLock, F_LOCK, 0) == -1) {
799 "salvager: There appears to be another salvager running! Aborted.\n");
806 #ifdef AFS_SGI_XFS_IOPS_ENV
807 /* Check if the given partition is mounted. For XFS, the root inode is not a
808 * constant. So we check the hard way.
810 int IsPartitionMounted(char *part)
813 struct mntent *mntent;
815 assert(mntfp = setmntent(MOUNTED, "r"));
816 while (mntent = getmntent(mntfp)) {
817 if (!strcmp(part, mntent->mnt_dir))
822 return mntent ? 1 : 1;
825 /* Check if the given inode is the root of the filesystem. */
826 #ifndef AFS_SGI_XFS_IOPS_ENV
827 int IsRootInode(status)
830 /* The root inode is not a fixed value in XFS partitions. So we need to see if
831 * the partition is in the list of mounted partitions. This only affects the
832 * SalvageFileSys path, so we check there.
834 return (status->st_ino == ROOTINODE);
839 #ifndef AFS_NAMEI_ENV
840 /* We don't want to salvage big files filesystems, since we can't put volumes on
843 int CheckIfBigFilesFS(mountPoint, devName)
847 struct superblock fs;
850 if (strncmp(devName, "/dev/", 5)) {
851 (void) sprintf(name, "/dev/%s", devName);
854 (void) strcpy(name, devName);
857 if (ReadSuper(&fs, name)<0) {
858 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
861 if (IsBigFilesFileSystem(&fs)) {
862 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
871 #define HDSTR "\\Device\\Harddisk"
872 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
873 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
880 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
882 if (strncmp(res, HDSTR, HDLEN)) {
885 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
886 res, HDSTR, p1->devName);
890 d1 = atoi(&res[HDLEN]);
892 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
894 if (strncmp(res, HDSTR, HDLEN)) {
897 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
898 res, HDSTR, p2->devName);
902 d2 = atoi(&res[HDLEN]);
907 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
910 /* This assumes that two partitions with the same device number divided by
911 * PartsPerDisk are on the same disk.
913 void SalvageFileSysParallel(struct DiskPartition *partP)
916 struct DiskPartition *partP;
917 int pid; /* Pid for this job */
918 int jobnumb; /* Log file job number */
919 struct job *nextjob; /* Next partition on disk to salvage */
921 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
922 struct job *thisjob = 0;
923 static int numjobs = 0;
924 static int jobcount = 0;
930 char logFileName[256];
934 /* We have a partition to salvage. Copy it into thisjob */
935 thisjob = (struct job *) malloc(sizeof(struct job));
937 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
940 memset(thisjob, 0, sizeof(struct job));
941 thisjob->partP = partP;
942 thisjob->jobnumb = jobcount;
945 else if (jobcount == 0) {
946 /* We are asking to wait for all jobs (partp == 0), yet we never
949 Log("No file system partitions named %s* found; not salvaged\n",
950 VICE_PARTITION_PREFIX);
954 if (debug || Parallel == 1) {
956 SalvageFileSys(thisjob->partP, 0);
963 /* Check to see if thisjob is for a disk that we are already
964 * salvaging. If it is, link it in as the next job to do. The
965 * jobs array has 1 entry per disk being salvages. numjobs is
966 * the total number of disks currently being salvaged. In
967 * order to keep thejobs array compact, when a disk is
968 * completed, the hightest element in the jobs array is moved
969 * down to now open slot.
971 for (j=0; j<numjobs; j++) {
972 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
973 /* On same disk, add it to this list and return */
974 thisjob->nextjob = jobs[j]->nextjob;
975 jobs[j]->nextjob = thisjob;
982 /* Loop until we start thisjob or until all existing jobs are finished */
983 while ( thisjob || (!partP && (numjobs > 0)) ) {
984 startjob = -1; /* No new job to start */
986 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
987 /* Either the max jobs are running or we have to wait for all
988 * the jobs to finish. In either case, we wait for at least one
989 * job to finish. When it's done, clean up after it.
991 pid = wait(&wstatus);
993 for (j=0; j<numjobs; j++) { /* Find which job it is */
994 if (pid == jobs[j]->pid) break;
997 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
998 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1001 numjobs--; /* job no longer running */
1002 oldjob = jobs[j]; /* remember */
1003 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1004 free(oldjob); /* free the old job */
1006 /* If there is another partition on the disk to salvage, then
1007 * say we will start it (startjob). If not, then put thisjob there
1008 * and say we will start it.
1010 if (jobs[j]) { /* Another partitions to salvage */
1011 startjob = j; /* Will start it */
1012 } else { /* There is not another partition to salvage */
1014 jobs[j] = thisjob; /* Add thisjob */
1016 startjob = j; /* Will start it */
1018 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1019 startjob = -1; /* Don't start it - already running */
1023 /* We don't have to wait for a job to complete */
1025 jobs[numjobs] = thisjob; /* Add this job */
1027 startjob = numjobs; /* Will start it */
1031 /* Start up a new salvage job on a partition in job slot "startjob" */
1032 if (startjob != -1) {
1034 Log("Starting salvage of file system partition %s\n",
1035 jobs[startjob]->partP->name);
1037 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1038 * commands and pass the child job number via the data path.
1040 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1041 jobs[startjob]->jobnumb);
1042 jobs[startjob]->pid = pid;
1047 jobs[startjob]->pid = pid;
1053 for (fd =0; fd < 16; fd++) close(fd);
1054 open("/", 0); dup2(0, 1); dup2(0, 2);
1055 #ifndef AFS_NT40_ENV
1057 openlog(NULL, LOG_PID, useSyslogFacility);
1061 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1062 logFile = fopen(logFileName, "w");
1064 if (!logFile) logFile = stdout;
1066 SalvageFileSys1(jobs[startjob]->partP, 0);
1071 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1073 /* If waited for all jobs to complete, now collect log files and return */
1074 #ifndef AFS_NT40_ENV
1075 if ( ! useSyslog ) /* if syslogging - no need to collect */
1078 for (i=0; i<jobcount; i++) {
1079 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1080 if (passLog = fopen(logFileName, "r")) {
1081 while(fgets(buf, sizeof(buf), passLog)) {
1082 fputs(buf, logFile);
1086 (void)unlink(logFileName);
1094 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1096 if (!canfork || debug || Fork() == 0) {
1097 SalvageFileSys1(partP, singleVolumeNumber);
1098 if (canfork && !debug) {
1104 Wait("SalvageFileSys");
1107 char *get_DevName(pbuffer, wpath)
1108 char *wpath, *pbuffer;
1110 char pbuf[128], *ptr;
1111 strcpy(pbuf, pbuffer);
1112 ptr = (char *)strrchr(pbuf, '/');
1115 strcpy(wpath, pbuf);
1118 ptr = (char *)strrchr(pbuffer, '/');
1120 strcpy(pbuffer, ptr+1);
1126 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1129 char inodeListPath[50];
1130 static char tmpDevName[100];
1131 static char wpath[100];
1132 struct VolumeSummary *vsp, *esp;
1135 fileSysPartition = partP;
1136 fileSysDevice = fileSysPartition->device;
1137 fileSysPathName = VPartitionPath(fileSysPartition);
1140 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1141 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1142 name = partP->devName;
1144 fileSysPath = fileSysPathName;
1145 strcpy(tmpDevName, partP->devName);
1146 name = get_DevName(tmpDevName, wpath);
1147 fileSysDeviceName = name;
1148 filesysfulldev = wpath;
1151 VLockPartition(partP->name);
1152 if (singleVolumeNumber || ForceSalvage)
1155 ForceSalvage = UseTheForceLuke(fileSysPath);
1157 if (singleVolumeNumber) {
1158 if (!VConnectFS()) {
1159 Abort("Couldn't connect to file server\n");
1161 AskOffline(singleVolumeNumber);
1164 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1166 Log("***Forced salvage of all volumes on this partition***\n");
1171 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1178 assert((dirp = opendir(fileSysPath)) != NULL);
1179 while (dp = readdir(dirp)) {
1180 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1181 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1183 Log("Removing old salvager temp files %s\n", dp->d_name);
1184 strcpy(npath, fileSysPath);
1186 strcat(npath, dp->d_name);
1192 tdir = (tmpdir ? tmpdir : fileSysPath);
1194 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1195 (void) strcpy(inodeListPath, _tempnam(tdir, "salvage.inodes."));
1197 sprintf(inodeListPath, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1199 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1200 unlink(inodeListPath);
1204 /* Using nt_unlink here since we're really using the delete on close
1205 * semantics of unlink. In most places in the salvager, we really do
1206 * mean to unlink the file at that point. Those places have been
1207 * modified to actually do that so that the NT crt can be used there.
1209 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1210 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1212 inodeFd = open(inodeListPath, O_RDONLY);
1213 unlink(inodeListPath);
1216 Abort("Temporary file %s is missing...\n",
1218 if (ListInodeOption) {
1222 /* enumerate volumes in the partition.
1223 figure out sets of read-only + rw volumes.
1224 salvage each set, read-only volumes first, then read-write.
1225 Fix up inodes on last volume in set (whether it is read-write
1228 GetVolumeSummary(singleVolumeNumber);
1230 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1231 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1232 for (j=i; j < nVolumesInInodeFile
1233 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1234 VolumeId vid = inodeSummary[j].volumeId;
1235 struct VolumeSummary *tsp;
1236 /* Scan volume list (from partition root directory) looking for the
1237 current rw volume number in the volume list from the inode scan.
1238 If there is one here that is not in the inode volume list,
1240 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1242 DeleteExtraVolumeHeaderFile(vsp);
1244 /* Now match up the volume summary info from the root directory with the
1245 entry in the volume list obtained from scanning inodes */
1246 inodeSummary[j].volSummary = NULL;
1247 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1248 if (tsp->header.id == vid) {
1249 inodeSummary[j].volSummary = tsp;
1255 /* Salvage the group of volumes (several read-only + 1 read/write)
1256 * starting with the current read-only volume we're looking at.
1258 SalvageVolumeGroup(&inodeSummary[i], j-i);
1261 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1262 for ( ; vsp<esp; vsp++) {
1264 DeleteExtraVolumeHeaderFile(vsp);
1267 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1268 RemoveTheForce(fileSysPath);
1270 if (!Testing && singleVolumeNumber) {
1271 AskOnline(singleVolumeNumber, fileSysPartition->name);
1273 /* Step through the volumeSummary list and set all volumes on-line.
1274 * The volumes were taken off-line in GetVolumeSummary.
1276 for (j=0; j<nVolumes; j++) {
1277 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1282 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1283 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1286 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1289 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1291 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1292 vsp->fileName, (Testing? "would have been ":""));
1294 unlink(vsp->fileName);
1298 CompareInodes(_p1,_p2)
1299 const void *_p1,*_p2;
1301 register const struct ViceInodeInfo *p1 = _p1;
1302 register const struct ViceInodeInfo *p2 = _p2;
1303 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1304 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1305 VolumeId p1rwid, p2rwid;
1306 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1307 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1308 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1309 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1310 if (p1rwid < p2rwid)
1312 if (p1rwid > p2rwid)
1314 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1315 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1316 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1317 return (p1->u.special.type < p2->u.special.type? -1: 1);
1318 if (p1->u.vnode.volumeId == p1rwid)
1320 if (p2->u.vnode.volumeId == p2rwid)
1322 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1324 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1325 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1326 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1328 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1330 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1332 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1334 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1336 /* The following tests are reversed, so that the most desirable
1337 of several similar inodes comes first */
1338 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1339 #ifdef AFS_3DISPARES
1340 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1341 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1344 #ifdef AFS_SGI_EXMAG
1345 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1346 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1351 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1352 #ifdef AFS_3DISPARES
1353 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1354 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1357 #ifdef AFS_SGI_EXMAG
1358 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1359 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1364 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1365 #ifdef AFS_3DISPARES
1366 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1367 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1370 #ifdef AFS_SGI_EXMAG
1371 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1372 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1377 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1378 #ifdef AFS_3DISPARES
1379 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1380 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1383 #ifdef AFS_SGI_EXMAG
1384 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1385 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1393 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1394 register struct InodeSummary * summary)
1396 int volume = ip->u.vnode.volumeId;
1397 int rwvolume = volume;
1398 register n, nSpecial;
1399 register Unique maxunique;
1402 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1404 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1406 rwvolume = ip->u.special.parentId;
1407 /* This isn't quite right, as there could (in error) be different
1408 parent inodes in different special vnodes */
1411 if (maxunique < ip->u.vnode.vnodeUniquifier)
1412 maxunique = ip->u.vnode.vnodeUniquifier;
1416 summary->volumeId = volume;
1417 summary->RWvolumeId = rwvolume;
1418 summary->nInodes =n;
1419 summary->nSpecialInodes = nSpecial;
1420 summary->maxUniquifier = maxunique;
1423 int OnlyOneVolume(inodeinfo, singleVolumeNumber)
1424 struct ViceInodeInfo *inodeinfo;
1425 VolumeId singleVolumeNumber;
1427 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1428 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1429 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1434 * Collect list of inodes in file named by path. If a truly fatal error,
1435 * unlink the file and abort. For lessor errors, return -1. The file will
1436 * be unlinked by the caller.
1438 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1442 struct ViceInodeInfo *ip;
1443 struct InodeSummary summary;
1444 char summaryFileName[50];
1447 char *dev = fileSysPath;
1448 char *wpath = fileSysPath;
1450 char *dev = fileSysDeviceName;
1451 char *wpath = filesysfulldev;
1453 char *part = fileSysPath;
1456 /* This file used to come from vfsck; cobble it up ourselves now... */
1457 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1459 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1464 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1466 if (forceSal && !ForceSalvage) {
1467 Log("***Forced salvage of all volumes on this partition***\n");
1470 inodeFd = open(path, O_RDWR);
1471 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1473 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1475 tdir = (tmpdir ? tmpdir : part);
1477 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1478 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1480 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1482 summaryFile = fopen(summaryFileName, "a+");
1483 if (summaryFile == NULL) {
1486 Abort("Unable to create inode summary file\n");
1488 if (!canfork || debug || Fork() == 0) {
1490 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1492 fclose(summaryFile); close(inodeFd);
1493 unlink(summaryFileName);
1494 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1495 RemoveTheForce(fileSysPath);
1496 Log("%s vice inodes on %s; not salvaged\n",
1497 singleVolumeNumber? "No applicable": "No", dev);
1500 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1502 fclose(summaryFile); close(inodeFd);
1504 unlink(summaryFileName);
1505 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1507 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1508 fclose(summaryFile); close(inodeFd);
1510 unlink(summaryFileName);
1511 Abort("Unable to read inode table; %s not salvaged\n", dev);
1513 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1514 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1515 write(inodeFd, ip, status.st_size) != status.st_size) {
1516 fclose(summaryFile); close(inodeFd);
1518 unlink(summaryFileName);
1519 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1523 CountVolumeInodes(ip, nInodes, &summary);
1524 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1525 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1526 fclose(summaryFile); close(inodeFd);
1529 summary.index += (summary.nInodes);
1530 nInodes -= summary.nInodes;
1531 ip += summary.nInodes;
1533 /* Following fflush is not fclose, because if it was debug mode would not work */
1534 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1535 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1536 fclose(summaryFile); close(inodeFd);
1539 if (canfork && !debug) {
1545 if (Wait("Inode summary") == -1) {
1546 fclose(summaryFile); close(inodeFd);
1548 unlink(summaryFileName);
1549 Exit(1); /* salvage of this partition aborted */
1552 assert(fstat(fileno(summaryFile), &status) != -1);
1553 if ( status.st_size != 0 ) {
1555 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1556 assert(inodeSummary != NULL);
1557 /* For GNU we need to do lseek to get the file pointer moved. */
1558 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1559 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1560 assert(ret == status.st_size);
1562 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1563 fclose(summaryFile);
1565 unlink(summaryFileName);
1569 /* Comparison routine for volume sort.
1570 This is setup so that a read-write volume comes immediately before
1571 any read-only clones of that volume */
1572 CompareVolumes(_p1,_p2)
1573 const void *_p1,*_p2;
1575 register const struct VolumeSummary *p1 = _p1;
1576 register const struct VolumeSummary *p2 = _p2;
1577 if (p1->header.parent != p2->header.parent)
1578 return p1->header.parent < p2->header.parent? -1: 1;
1579 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1581 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1583 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1586 void GetVolumeSummary(VolumeId singleVolumeNumber)
1589 afs_int32 nvols = 0;
1590 struct VolumeSummary *vsp, vs;
1591 struct VolumeDiskHeader diskHeader;
1594 /* Get headers from volume directory */
1595 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1596 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1597 if (!singleVolumeNumber) {
1598 while (dp = readdir(dirp)) {
1599 char *p = dp->d_name;
1600 p = strrchr(dp->d_name, '.');
1601 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1603 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1604 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1605 == sizeof (diskHeader) &&
1606 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1607 DiskToVolumeHeader(&vs.header, &diskHeader);
1614 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1618 if (!nvols) nvols = 1;
1619 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1621 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1622 assert(volumeSummaryp != NULL);
1625 vsp = volumeSummaryp;
1626 while (dp = readdir(dirp)) {
1627 char *p = dp->d_name;
1628 p = strrchr(dp->d_name, '.');
1629 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1632 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1633 || read(fd, &diskHeader, sizeof (diskHeader))
1634 != sizeof (diskHeader)
1635 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1640 if (!singleVolumeNumber) {
1641 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1642 dp->d_name, (Testing? "it would have been ":""));
1648 char nameShouldBe[64];
1649 DiskToVolumeHeader(&vsp->header, &diskHeader);
1650 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1651 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1654 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1655 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1656 if (singleVolumeNumber)
1657 AskOffline(vsp->header.id);
1658 if (strcmp(nameShouldBe, dp->d_name)) {
1659 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 ":""));
1664 vsp->fileName = ToString(dp->d_name);
1674 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1677 /* Find the link table. This should be associated with the RW volume or, if
1678 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1680 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1681 struct ViceInodeInfo *allInodes)
1684 struct ViceInodeInfo *ip;
1686 for (i=0; i<nVols; i++) {
1687 ip = allInodes + isp[i].index;
1688 for (j=0; j<isp[i].nSpecialInodes; j++) {
1689 if (ip[j].u.special.type == VI_LINKTABLE)
1690 return ip[j].inodeNumber;
1696 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1698 struct versionStamp version;
1701 if (!VALID_INO(ino))
1702 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1703 isp->volumeId, INODESPECIAL,
1704 VI_LINKTABLE, isp->RWvolumeId);
1705 if (!VALID_INO(ino))
1706 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1707 isp->RWvolumeId, errno);
1708 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1709 fdP = IH_OPEN(VGLinkH);
1711 Abort("Can't open link table for volume %u (error = %d)\n",
1712 isp->RWvolumeId, errno);
1714 if (FDH_TRUNC(fdP, 0)<0)
1715 Abort("Can't truncate link table for volume %u (error = %d)\n",
1716 isp->RWvolumeId, errno);
1718 version.magic = LINKTABLEMAGIC;
1719 version.version = LINKTABLEVERSION;
1721 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1723 Abort("Can't truncate link table for volume %u (error = %d)\n",
1724 isp->RWvolumeId, errno);
1726 FDH_REALLYCLOSE(fdP);
1728 /* If the volume summary exits (i.e., the V*.vol header file exists),
1729 * then set this inode there as well.
1731 if (isp->volSummary)
1732 isp->volSummary->header.linkTable = ino;
1738 void *nt_SVG(void *arg)
1740 SVGParms_t *parms = (SVGParms_t*)arg;
1741 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1745 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1748 pthread_attr_t tattr;
1752 /* Initialize per volume global variables, even if later code does so */
1756 memset(&VolInfo, 0, sizeof(VolInfo));
1758 parms.svgp_inodeSummaryp = isp;
1759 parms.svgp_count = nVols;
1760 code = pthread_attr_init(&tattr);
1762 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1766 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1768 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1772 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1774 Log("Failed to create thread to salvage volume group %u\n",
1778 (void) pthread_join(tid, NULL);
1780 #endif /* AFS_NT40_ENV */
1782 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1784 struct ViceInodeInfo *inodes,*allInodes,*ip;
1785 int i, totalInodes, size, salvageTo;
1789 int dec_VGLinkH = 0;
1791 FdHandle_t *fdP = NULL;
1794 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1795 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1796 if (!ForceSalvage && QuickCheck(isp, nVols))
1799 if (ShowMounts && !haveRWvolume)
1801 if (canfork && !debug && Fork() != 0) {
1802 (void) Wait("Salvage volume group");
1805 for (i = 0, totalInodes = 0; i<nVols; i++)
1806 totalInodes += isp[i].nInodes;
1807 size = totalInodes * sizeof (struct ViceInodeInfo);
1808 inodes = (struct ViceInodeInfo *) malloc(size);
1809 allInodes = inodes - isp->index; /* this would the base of all the inodes
1810 for the partition, if all the inodes
1811 had been read into memory */
1812 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1)
1813 assert(read(inodeFd,inodes,size) == size)
1815 /* Don't try to salvage a read write volume if there isn't one on this
1817 salvageTo = haveRWvolume? 0:1;
1819 #ifdef AFS_NAMEI_ENV
1820 ino = FindLinkHandle(isp, nVols, allInodes);
1821 if (VALID_INO(ino)) {
1822 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1823 fdP = IH_OPEN(VGLinkH);
1825 if (!VALID_INO(ino) || fdP == NULL) {
1826 Log("%s link table for volume %u.\n",
1827 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1829 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1832 CreateLinkTable(isp, ino);
1836 FDH_REALLYCLOSE(fdP);
1838 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1841 /* Salvage in reverse order--read/write volume last; this way any
1842 Inodes not referenced by the time we salvage the read/write volume
1843 can be picked up by the read/write volume */
1844 /* ACTUALLY, that's not done right now--the inodes just vanish */
1845 for (i = nVols-1; i>=salvageTo; i--) {
1847 struct InodeSummary *lisp = &isp[i];
1848 #ifdef AFS_NAMEI_ENV
1849 /* If only the RO is present on this partition, the link table
1850 * shows up as a RW volume special file. Need to make sure the
1851 * salvager doesn't try to salvage the non-existent RW.
1853 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1854 /* If this only special inode is the link table, continue */
1855 if (inodes->u.special.type == VI_LINKTABLE) {
1861 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1862 lisp->volumeId, (Testing?"(READONLY mode)":""));
1863 /* Check inodes twice. The second time do things seriously. This
1864 way the whole RO volume can be deleted, below, if anything goes wrong */
1865 for (check = 1; check>=0; check--) {
1867 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1868 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1869 if (rw && deleteMe) {
1870 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1871 volume won't be called */
1877 if (rw && check == 1)
1879 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1880 MaybeZapVolume(lisp,"Vnode index", 0, check);
1886 /* Fix actual inode counts */
1888 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1889 static int TraceBadLinkCounts = 0;
1890 #ifdef AFS_NAMEI_ENV
1891 if (VGLinkH->ih_ino == ip->inodeNumber) {
1892 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1893 VGLinkH_p1 = ip->u.param[0];
1894 continue; /* Deal with this last. */
1897 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1898 TraceBadLinkCounts--; /* Limit reports, per volume */
1899 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1900 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1901 ip->byteCount, ip->u.param[0], ip->u.param[1],
1902 ip->u.param[2], ip->u.param[3]);
1904 while (ip->linkCount > 0) {
1905 /* below used to assert, not break */
1907 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1908 Log ("idec failed. inode %s errno %d\n",
1909 PrintInode(NULL, ip->inodeNumber), errno);
1915 while (ip->linkCount < 0) {
1916 /* these used to be asserts */
1918 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1919 Log ("iinc failed. inode %s errno %d\n",
1920 PrintInode(NULL, ip->inodeNumber) ,errno);
1927 #ifdef AFS_NAMEI_ENV
1928 while (dec_VGLinkH > 0) {
1929 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1930 Log("idec failed on link table, errno = %d\n", errno);
1934 while (dec_VGLinkH < 0) {
1935 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1936 Log("iinc failed on link table, errno = %d\n", errno);
1943 /* Directory consistency checks on the rw volume */
1945 SalvageVolume(isp, VGLinkH);
1946 IH_RELEASE(VGLinkH);
1948 if (canfork && !debug) {
1954 int QuickCheck(register struct InodeSummary *isp, int nVols)
1956 /* Check headers BEFORE forking */
1960 for (i = 0; i<nVols; i++) {
1961 struct VolumeSummary *vs = isp[i].volSummary;
1962 VolumeDiskData volHeader;
1964 /* Don't salvage just because phantom rw volume is there... */
1965 /* (If a read-only volume exists, read/write inodes must also exist) */
1966 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1970 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1971 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1972 == sizeof(volHeader)
1973 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1974 && volHeader.dontSalvage == DONT_SALVAGE
1975 && volHeader.needsSalvaged == 0
1976 && volHeader.destroyMe == 0) {
1977 if (volHeader.inUse == 1) {
1978 volHeader.inUse = 0;
1979 volHeader.inService = 1;
1981 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1982 != sizeof(volHeader)) {
1999 /* SalvageVolumeHeaderFile
2001 * Salvage the top level V*.vol header file. Make sure the special files
2002 * exist and that there are no duplicates.
2004 * Calls SalvageHeader for each possible type of volume special file.
2007 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2008 register struct ViceInodeInfo *inodes,
2009 int RW, int check, int *deleteMe)
2013 register struct ViceInodeInfo *ip;
2014 int allinodesobsolete = 1;
2015 struct VolumeDiskHeader diskHeader;
2019 memset(&tempHeader, 0, sizeof(tempHeader));
2020 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2021 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2022 tempHeader.id = isp->volumeId;
2023 tempHeader.parent = isp->RWvolumeId;
2024 /* Check for duplicates (inodes are sorted by type field) */
2025 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2026 ip = &inodes[isp->index+i];
2027 if (ip->u.special.type == (ip+1)->u.special.type) {
2028 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2032 for (i = 0; i<isp->nSpecialInodes; i++) {
2033 ip = &inodes[isp->index+i];
2034 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2036 Log("Rubbish header inode\n");
2039 Log("Rubbish header inode; deleted\n");
2041 else if (!stuff[ip->u.special.type-1].obsolete) {
2042 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2043 if (!check && ip->u.special.type != VI_LINKTABLE)
2044 ip->linkCount--; /* Keep the inode around */
2045 allinodesobsolete = 0;
2049 if (allinodesobsolete) {
2056 VGLinkH_cnt ++; /* one for every header. */
2058 if (!RW && !check && isp->volSummary) {
2059 ClearROInUseBit(isp->volSummary);
2063 for (i = 0; i< MAXINODETYPE; i++) {
2064 if (stuff[i].inodeType == VI_LINKTABLE) {
2065 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2066 * And we may have recreated the link table earlier, so set the
2067 * RW header as well.
2069 if (VALID_INO(VGLinkH->ih_ino)) {
2070 *stuff[i].inode = VGLinkH->ih_ino;
2074 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2078 if (isp->volSummary == NULL) {
2080 sprintf(name, VFORMAT, isp->volumeId);
2082 Log("No header file for volume %u\n", isp->volumeId);
2085 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2086 isp->volumeId, (Testing?"it would have been ":""),
2087 fileSysPathName, name);
2088 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2089 assert(headerFd != -1)
2090 isp->volSummary = (struct VolumeSummary *)
2091 malloc(sizeof(struct VolumeSummary));
2092 isp->volSummary->fileName = ToString(name);
2096 /* hack: these two fields are obsolete... */
2097 isp->volSummary->header.volumeAcl = 0;
2098 isp->volSummary->header.volumeMountTable = 0;
2100 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2101 /* We often remove the name before calling us, so we make a fake one up */
2102 if (isp->volSummary->fileName) {
2103 strcpy(name, isp->volSummary->fileName);
2105 sprintf(name, VFORMAT, isp->volumeId);
2106 isp->volSummary->fileName = ToString(name);
2109 Log("Header file %s is damaged or no longer valid%s\n",
2110 name, (check ? "" : "; repairing"));
2114 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2115 assert(headerFd != -1)
2119 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2121 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2123 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2124 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2125 != sizeof(struct VolumeDiskHeader)) {
2126 Log("Couldn't rewrite volume header file!\n");
2133 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2134 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2138 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2139 int check, int *deleteMe)
2142 VolumeDiskData volumeInfo;
2143 struct versionStamp fileHeader;
2152 #ifndef AFS_NAMEI_ENV
2153 if ( sp->inodeType == VI_LINKTABLE)
2156 if (*(sp->inode) == 0) {
2158 Log("Missing inode in volume header (%s)\n", sp->description);
2161 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2162 sp->description, (Testing ? "it would have recreated it": "recreating"));
2164 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2165 isp->volumeId, INODESPECIAL,
2166 sp->inodeType, isp->RWvolumeId);
2167 if (!VALID_INO(*(sp->inode)))
2168 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2169 sp->description, errno);
2174 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2175 fdP = IH_OPEN(specH);
2176 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2177 /* bail out early and destroy the volume */
2178 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2180 if (deleteMe) *deleteMe = 1;
2185 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2186 sp->description, errno);
2189 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2190 || header.fileHeader.magic != sp->stamp.magic)) {
2192 Log("Part of the header (%s) is corrupted\n", sp->description);
2193 FDH_REALLYCLOSE(fdP);
2197 Log("Part of the header (%s) is corrupted; recreating\n",
2201 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2204 FDH_REALLYCLOSE(fdP);
2208 if (recreate && !Testing) {
2210 Abort("Internal error: recreating volume header (%s) in check mode\n",
2212 code = FDH_TRUNC(fdP, 0);
2214 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2215 sp->description, errno);
2217 /* The following code should be moved into vutil.c */
2218 if (sp->inodeType == VI_VOLINFO) {
2220 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2221 header.volumeInfo.stamp = sp->stamp;
2222 header.volumeInfo.id = isp->volumeId;
2223 header.volumeInfo.parentId = isp->RWvolumeId;
2224 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2225 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2226 header.volumeInfo.inService = 0;
2227 header.volumeInfo.blessed = 0;
2228 /* The + 1000 is a hack in case there are any files out in venus caches */
2229 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2230 header.volumeInfo.type =
2231 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2232 header.volumeInfo.needsCallback = 0;
2233 gettimeofday(&tp,0);
2234 header.volumeInfo.creationDate = tp.tv_sec;
2235 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2236 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2238 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2239 sizeof(header.volumeInfo));
2240 if (code != sizeof(header.volumeInfo)) {
2242 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2243 sp->description, errno);
2244 Abort("Unable to write entire volume header file (%s)\n",
2249 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2250 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2252 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2253 if (code != sizeof(sp->stamp)) {
2255 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2256 sp->description, errno);
2257 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2262 FDH_REALLYCLOSE(fdP);
2264 if (sp->inodeType == VI_VOLINFO) {
2265 VolInfo = header.volumeInfo;
2268 if (VolInfo.updateDate) {
2269 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2270 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2271 (Testing?"it would have been ":""), update);
2273 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2274 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2283 int SalvageVnodes(register struct InodeSummary *rwIsp,
2284 register struct InodeSummary * thisIsp,
2285 register struct ViceInodeInfo * inodes, int check)
2287 int ilarge, ismall, ioffset, RW, nInodes;
2288 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2289 if (Showmode) return 0;
2290 RW = (rwIsp == thisIsp);
2291 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2292 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2293 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2294 if (check && ismall == -1)
2296 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2297 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2298 return (ilarge==0 && ismall==0 ? 0: -1);
2301 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2302 register struct ViceInodeInfo *ip,
2303 int nInodes, struct VolumeSummary *volSummary, int check)
2305 VolumeId volumeNumber;
2306 char buf[SIZEOF_LARGEDISKVNODE];
2307 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2309 StreamHandle_t *file;
2310 struct VnodeClassInfo *vcp;
2312 int vnodeIndex, nVnodes;
2313 afs_ino_str_t stmp1, stmp2;
2317 volumeNumber = volSummary->header.id;
2318 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2319 fdP = IH_OPEN(handle);
2320 assert(fdP != NULL);
2321 file = FDH_FDOPEN(fdP, "r+");
2322 assert(file != NULL)
2323 vcp = &VnodeClassInfo[class];
2324 size = OS_SIZE(fdP->fd_fd);
2326 nVnodes = (size / vcp->diskSize) - 1;
2328 assert((nVnodes+1) * vcp->diskSize == size)
2329 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2334 for (vnodeIndex = 0;
2335 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2336 nVnodes--, vnodeIndex++) {
2337 if (vnode->type != vNull) {
2338 int vnodeChanged = 0;
2339 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2340 /* Log programs that belong to root (potentially suid root);
2341 don't bother for read-only or backup volumes */
2342 #ifdef notdef /* This is done elsewhere */
2343 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2344 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2345 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2346 vnode->owner, vnode->modeBits);
2348 if (VNDISK_GET_INO(vnode) == 0) {
2350 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2351 memset(vnode, 0, vcp->diskSize);
2356 if (vcp->magic != vnode->vnodeMagic) {
2357 /* bad magic #, probably partially created vnode */
2358 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2359 memset(vnode, 0, vcp->diskSize);
2363 /* ****** Should do a bit more salvage here: e.g. make sure
2364 vnode type matches what it should be given the index */
2365 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2366 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2367 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2368 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2375 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2376 /* The following doesn't work, because the version number
2377 is not maintained correctly by the file server */
2378 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2379 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2381 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2388 /* For RW volume, look for vnode with matching inode number;
2389 if no such match, take the first determined by our sort
2391 register struct ViceInodeInfo *lip = ip;
2392 register lnInodes = nInodes;
2393 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2394 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2403 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2404 /* "Matching" inode */
2408 vu = vnode->uniquifier;
2409 iu = ip->u.vnode.vnodeUniquifier;
2410 vd = vnode->dataVersion;
2411 id = ip->u.vnode.inodeDataVersion;
2413 * Because of the possibility of the uniquifier overflows (> 4M)
2414 * we compare them modulo the low 22-bits; we shouldn't worry
2415 * about mismatching since they shouldn't to many old
2416 * uniquifiers of the same vnode...
2418 if (IUnique(vu) != IUnique(iu)) {
2420 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2421 vnodeNumber, IUnique(vu), IUnique(iu));
2424 vnode->uniquifier = iu;
2425 #ifdef AFS_3DISPARES
2426 vnode->dataVersion = (id >= vd ?
2427 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2428 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2430 #if defined(AFS_SGI_EXMAG)
2431 vnode->dataVersion = (id >= vd ?
2432 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2433 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2435 vnode->dataVersion = (id>vd ? id:vd);
2436 #endif /* AFS_SGI_EXMAG */
2437 #endif /* AFS_3DISPARES */
2441 /* don't bother checking for vd > id any more, since
2442 partial file transfers always result in this state,
2443 and you can't do much else anyway (you've already
2444 found the best data you can) */
2445 #ifdef AFS_3DISPARES
2446 if (!vnodeIsDirectory(vnodeNumber) &&
2447 ((vd < id && (id-vd) < 1887437) ||
2448 ((vd > id && (vd-id) > 1887437)))) {
2450 #if defined(AFS_SGI_EXMAG)
2451 if (!vnodeIsDirectory(vnodeNumber) &&
2452 ((vd < id && (id-vd) < 15099494) ||
2453 ((vd > id && (vd-id) > 15099494)))) {
2455 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2456 #endif /* AFS_SGI_EXMAG */
2458 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2459 vnode->dataVersion = id;
2464 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2467 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2469 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2470 PrintInode(stmp2, ip->inodeNumber),
2473 VNDISK_SET_INO(vnode, ip->inodeNumber);
2478 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2480 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2481 PrintInode(stmp2, ip->inodeNumber),
2484 VNDISK_SET_INO(vnode, ip->inodeNumber);
2487 if (ip->byteCount != vnode->length) {
2489 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2490 vnodeNumber, vnode->length, ip->byteCount);
2494 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2495 vnodeNumber, vnode->length, ip->byteCount);
2496 vnode->length = ip->byteCount;
2500 ip->linkCount--; /* Keep the inode around */
2504 else { /* no matching inode */
2505 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2506 /* No matching inode--get rid of the vnode */
2508 if (VNDISK_GET_INO(vnode)) {
2510 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2511 vnodeNumber, vnode->uniquifier,
2512 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2515 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2520 if (VNDISK_GET_INO(vnode)) {
2522 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2523 vnodeNumber, vnode->uniquifier,
2524 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2525 ctime((time_t *)&(vnode->serverModifyTime)));
2528 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)));
2530 memset(vnode, 0, vcp->diskSize);
2533 /* Should not reach here becuase we checked for
2534 * (inodeNumber == 0) above. And where we zero the vnode,
2535 * we also goto vnodeDone.
2539 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2543 } /* VNDISK_GET_INO(vnode) != 0 */
2545 assert(!(vnodeChanged && check));
2546 if (vnodeChanged && !Testing) {
2547 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2548 (char*)vnode, vcp->diskSize)
2550 VolumeChanged = 1; /* For break call back */
2561 struct VnodeEssence *CheckVnodeNumber(vnodeNumber)
2562 VnodeId vnodeNumber;
2565 struct VnodeInfo *vip;
2568 class = vnodeIdToClass(vnodeNumber);
2569 vip = &vnodeInfo[class];
2570 offset = vnodeIdToBitNumber(vnodeNumber);
2571 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2575 void CopyOnWrite(register struct DirSummary *dir)
2577 /* Copy the directory unconditionally if we are going to change it:
2578 * not just if was cloned.
2580 struct VnodeDiskObject vnode;
2581 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2582 Inode oldinode, newinode;
2585 if (dir->copied || Testing)
2587 DFlush(); /* Well justified paranoia... */
2589 code = IH_IREAD(vnodeInfo[vLarge].handle,
2590 vnodeIndexOffset(vcp, dir->vnodeNumber),
2591 (char*)&vnode, sizeof (vnode));
2592 assert(code == sizeof(vnode));
2593 oldinode = VNDISK_GET_INO(&vnode);
2594 /* Increment the version number by a whole lot to avoid problems with
2595 * clients that were promised new version numbers--but the file server
2596 * crashed before the versions were written to disk.
2598 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2599 dir->rwVid, dir->vnodeNumber,
2600 vnode.uniquifier, vnode.dataVersion += 200);
2601 assert(VALID_INO(newinode));
2602 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2604 VNDISK_SET_INO(&vnode, newinode);
2605 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2606 vnodeIndexOffset(vcp, dir->vnodeNumber),
2607 (char*)&vnode, sizeof (vnode));
2608 assert(code == sizeof (vnode));
2610 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2611 fileSysDevice, newinode);
2612 /* Don't delete the original inode right away, because the directory is
2613 * still being scanned.
2618 /* This function should either successfully create a new dir, or give up and leave
2619 * things the way they were. In particular, if it fails to write the new dir properly,
2620 * it should return w/o changing the reference to the old dir.
2622 void CopyAndSalvage(register struct DirSummary *dir)
2624 struct VnodeDiskObject vnode;
2625 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2626 Inode oldinode, newinode;
2628 register afs_int32 code;
2629 afs_int32 parentUnique= 1;
2630 struct VnodeEssence *vnodeEssence;
2634 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2635 code = IH_IREAD(vnodeInfo[vLarge].handle,
2636 vnodeIndexOffset(vcp, dir->vnodeNumber),
2637 (char*)&vnode, sizeof (vnode));
2638 assert(code == sizeof (vnode));
2639 oldinode = VNDISK_GET_INO(&vnode);
2640 /* Increment the version number by a whole lot to avoid problems with
2641 * clients that were promised new version numbers--but the file server
2642 * crashed before the versions were written to disk.
2644 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2645 dir->rwVid, dir->vnodeNumber,
2647 vnode.dataVersion += 200);
2648 assert(VALID_INO(newinode));
2649 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2651 /* Assign . and .. vnode numbers from dir and vnode.parent.
2652 * The uniquifier for . is in the vnode.
2653 * The uniquifier for .. might be set to a bogus value of 1 and
2654 * the salvager will later clean it up.
2656 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2657 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2659 code = DirSalvage(&dir->dirHandle, &newdir,
2660 dir->vnodeNumber, vnode.uniquifier,
2661 (vnode.parent?vnode.parent:dir->vnodeNumber),
2663 if (code == 0) code = DFlush();
2665 /* didn't really build the new directory properly, let's just give up. */
2666 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2668 Log("Directory salvage returned code %d, continuing.\n", code);
2671 Log("Checking the results of the directory salvage...\n");
2672 if (!DirOK(&newdir)) {
2673 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2674 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2679 VNDISK_SET_INO(&vnode, newinode);
2680 vnode.length = Length(&newdir);
2681 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2682 vnodeIndexOffset(vcp, dir->vnodeNumber),
2683 (char*)&vnode, sizeof (vnode));
2684 assert(code == sizeof (vnode));
2686 nt_sync(fileSysDevice);
2688 sync(); /* this is slow, but hopefully rarely called. We don't have
2689 * an open FD on the file itself to fsync.
2692 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2694 dir->dirHandle = newdir;
2697 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2700 struct VnodeEssence *vnodeEssence;
2701 afs_int32 dirOrphaned, todelete;
2703 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2705 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2706 if (vnodeEssence == NULL) {
2708 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2709 dir->vnodeNumber, dir->name?dir->name:"??",
2710 name, vnodeNumber, unique);
2714 assert(Delete(&dir->dirHandle, name) == 0)
2720 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2721 * mount inode for the partition. If this inode were deleted, it would crash
2724 if (vnodeEssence->InodeNumber == 0) {
2725 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2726 dir->vnodeNumber, (dir->name?dir->name:"??"),
2727 name, vnodeNumber, unique,
2728 (Testing?"-- would have deleted":" -- deleted"));
2731 assert(Delete(&dir->dirHandle, name) == 0);
2737 if (!(vnodeNumber & 1) && !Showmode &&
2738 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2739 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2740 dir->vnodeNumber, (dir->name?dir->name:"??"),
2741 name, vnodeNumber, unique,
2742 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2746 assert(Delete(&dir->dirHandle, name) == 0);
2752 /* Check if the Uniquifiers match. If not, change the directory entry
2753 * so its unique matches the vnode unique. Delete if the unique is zero
2754 * or if the directory is orphaned.
2756 if (!vnodeEssence->unique ||
2757 (vnodeEssence->unique) != unique) {
2758 if (!vnodeEssence->unique &&
2759 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2760 /* This is an orphaned directory. Don't delete the . or ..
2761 * entry. Otherwise, it will get created in the next
2762 * salvage and deleted again here. So Just skip it.
2767 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2770 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2771 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2772 name, vnodeNumber, unique, vnodeEssence->unique,
2773 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2777 fid.Vnode = vnodeNumber;
2778 fid.Unique = vnodeEssence->unique;
2780 assert(Delete(&dir->dirHandle, name) == 0)
2782 assert(Create(&dir->dirHandle, name, &fid) == 0)
2784 if (todelete) return; /* no need to continue */
2787 if (strcmp(name,".") == 0) {
2788 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2790 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2791 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2794 assert(Delete(&dir->dirHandle, ".") == 0)
2795 fid.Vnode = dir->vnodeNumber;
2796 fid.Unique = dir->unique;
2797 assert(Create(&dir->dirHandle, ".", &fid) == 0)
2800 vnodeNumber = fid.Vnode; /* Get the new Essence */
2801 unique = fid.Unique;
2802 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2806 else if (strcmp(name,"..") == 0) {
2809 struct VnodeEssence *dotdot;
2810 pa.Vnode = dir->parent;
2811 dotdot = CheckVnodeNumber(pa.Vnode);
2812 assert (dotdot != NULL); /* XXX Should not be assert */
2813 pa.Unique = dotdot->unique;
2816 pa.Vnode = dir->vnodeNumber;
2817 pa.Unique = dir->unique;
2819 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2820 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2821 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2824 assert(Delete(&dir->dirHandle, "..") == 0);
2825 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2828 vnodeNumber = pa.Vnode; /* Get the new Essence */
2830 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2832 dir->haveDotDot = 1;
2833 } else if (strncmp(name,".__afs",6) == 0) {
2835 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2836 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2840 assert(Delete(&dir->dirHandle, name) == 0)
2842 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2843 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2847 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2848 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2849 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber, dir->vnodeNumber);
2850 if (ShowMounts && (vnodeEssence->type == vSymlink) && !(vnodeEssence->modeBits & 0111)) {
2856 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2857 vnodeEssence->InodeNumber);
2859 assert(fdP != NULL);
2860 size = FDH_SIZE(fdP);
2862 memset(buf, 0, 1024);
2863 if (size > 1024) size = 1024;
2864 code = FDH_READ(fdP, buf, size);
2865 assert(code == size);
2866 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2867 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2868 dir->name?dir->name:"??", name, buf);
2869 FDH_REALLYCLOSE(fdP);
2872 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2873 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2874 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2875 if (vnodeIdToClass(vnodeNumber) == vLarge && vnodeEssence->name == NULL) {
2877 if (n = (char*)malloc(strlen(name)+1))
2879 vnodeEssence->name = n;
2882 /* The directory entry points to the vnode. Check to see if the
2883 * vnode points back to the directory. If not, then let the
2884 * directory claim it (else it might end up orphaned). Vnodes
2885 * already claimed by another directory are deleted from this
2886 * directory: hardlinks to the same vnode are not allowed
2887 * from different directories.
2889 if (vnodeEssence->parent != dir->vnodeNumber) {
2890 if (!vnodeEssence->claimed && !dirOrphaned) {
2891 /* Vnode does not point back to this directory.
2892 * Orphaned dirs cannot claim a file (it may belong to
2893 * another non-orphaned dir).
2896 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2897 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2898 vnodeNumber, unique, (Testing?"would have been ":""),
2899 vnodeEssence->parent, dir->vnodeNumber);
2901 vnodeEssence->parent = dir->vnodeNumber;
2902 vnodeEssence->changed = 1;
2904 /* Vnode was claimed by another directory */
2907 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2908 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2909 vnodeEssence->parent, vnodeNumber, unique,
2910 (Testing?"would have been ":""));
2912 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2913 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2914 vnodeEssence->parent, vnodeNumber, unique,
2915 (Testing?"would have been ":""));
2920 assert(Delete(&dir->dirHandle, name) == 0);
2925 /* This directory claims the vnode */
2926 vnodeEssence->claimed = 1;
2928 vnodeEssence->count--;
2931 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2934 register struct VnodeInfo *vip = &vnodeInfo[class];
2935 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2936 char buf[SIZEOF_LARGEDISKVNODE];
2937 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2939 StreamHandle_t *file;
2944 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2945 fdP = IH_OPEN(vip->handle);
2947 file = FDH_FDOPEN(fdP, "r+");
2948 assert(file != NULL);
2949 size = OS_SIZE(fdP->fd_fd);
2951 vip->nVnodes = (size / vcp->diskSize) - 1;
2952 if (vip->nVnodes > 0) {
2953 assert((vip->nVnodes+1)*vcp->diskSize == size)
2954 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2955 assert((vip->vnodes = (struct VnodeEssence *)
2956 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL)
2957 if (class == vLarge) {
2958 assert((vip->inodes = (Inode *)
2959 calloc(vip->nVnodes, sizeof (Inode))) != NULL)
2970 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2971 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2972 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2973 nVnodes--, vnodeIndex++) {
2974 if (vnode->type != vNull) {
2975 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2976 vip->nAllocatedVnodes++;
2977 vep->count = vnode->linkCount;
2978 vep->blockCount = nBlocks(vnode->length);
2979 vip->volumeBlockCount += vep->blockCount;
2980 vep->parent = vnode->parent;
2981 vep->unique = vnode->uniquifier;
2982 if (*maxu < vnode->uniquifier)
2983 *maxu = vnode->uniquifier;
2984 vep->modeBits = vnode->modeBits;
2985 vep->InodeNumber = VNDISK_GET_INO(vnode);
2986 vep->type = vnode->type;
2987 vep->author = vnode->author;
2988 vep->owner = vnode->owner;
2989 vep->group = vnode->group;
2990 if (vnode->type == vDirectory) {
2991 assert(class == vLarge)
2992 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3000 static char *GetDirName(vnode, vp, path)
3002 struct VnodeEssence *vp;
3005 struct VnodeEssence *parentvp;
3011 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3013 strcat(path, vp->name);
3019 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3020 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3022 static int IsVnodeOrphaned(vnode)
3025 struct VnodeEssence *vep;
3027 if (vnode == 0) return(1); /* Vnode zero does not exist */
3028 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3029 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3030 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3032 return( IsVnodeOrphaned(vep->parent) );
3035 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3036 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3039 static struct DirSummary dir;
3040 static struct DirHandle dirHandle;
3041 struct VnodeEssence *parent;
3042 static char path[MAXPATHLEN];
3045 if (dirVnodeInfo->vnodes[i].salvaged)
3046 return; /* already salvaged */
3049 dirVnodeInfo->vnodes[i].salvaged = 1;
3051 if (dirVnodeInfo->inodes[i] == 0)
3052 return; /* Not allocated to a directory */
3054 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3055 if (parent && parent->salvaged == 0)
3056 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3057 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3058 rootdir, rootdirfound);
3059 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3060 dir.unique = dirVnodeInfo->vnodes[i].unique;
3063 dir.parent = dirVnodeInfo->vnodes[i].parent;
3064 dir.haveDot = dir.haveDotDot = 0;
3065 dir.ds_linkH = alinkH;
3066 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3068 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3071 Log("Directory bad, vnode %d; %s...\n",
3072 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3075 CopyAndSalvage(&dir);
3079 dirHandle = dir.dirHandle;
3081 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3084 /* If enumeration failed for random reasons, we will probably delete
3085 * too much stuff, so we guard against this instead.
3087 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3090 /* Delete the old directory if it was copied in order to salvage.
3091 * CopyOnWrite has written the new inode # to the disk, but we still
3092 * have the old one in our local structure here. Thus, we idec the
3096 if (dir.copied && !Testing) {
3097 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3099 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3102 /* Remember rootdir DirSummary _after_ it has been judged */
3103 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3104 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3111 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3113 /* This routine, for now, will only be called for read-write volumes */
3115 int BlocksInVolume = 0, FilesInVolume = 0;
3116 register VnodeClass class;
3117 struct DirSummary rootdir, oldrootdir;
3118 struct VnodeInfo *dirVnodeInfo;
3119 struct VnodeDiskObject vnode;
3120 VolumeDiskData volHeader;
3122 int orphaned, rootdirfound = 0;
3123 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3124 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3125 struct VnodeEssence *vep;
3130 VnodeId LFVnode, ThisVnode;
3131 Unique LFUnique, ThisUnique;
3134 vid = rwIsp->volSummary->header.id;
3135 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3136 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3137 assert(nBytes == sizeof(volHeader));
3138 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3139 assert (volHeader.destroyMe != DESTROY_ME);
3140 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3142 DistilVnodeEssence(vid, vLarge,
3143 rwIsp->volSummary->header.largeVnodeIndex,
3145 DistilVnodeEssence(vid, vSmall,
3146 rwIsp->volSummary->header.smallVnodeIndex,
3149 dirVnodeInfo = &vnodeInfo[vLarge];
3150 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3151 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3152 &rootdir, &rootdirfound);
3159 /* Parse each vnode looking for orphaned vnodes and
3160 * connect them to the tree as orphaned (if requested).
3162 oldrootdir = rootdir;
3163 for (class=0; class < nVNODECLASSES; class++) {
3164 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3165 vep = &(vnodeInfo[class].vnodes[v]);
3166 ThisVnode = bitNumberToVnodeNumber(v, class);
3167 ThisUnique = vep->unique;
3169 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3170 continue; /* Ignore unused, claimed, and root vnodes */
3172 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3173 * entry in this vnode had incremented the parent link count (In
3174 * JudgeEntry()). We need to go to the parent and decrement that
3175 * link count. But if the parent's unique is zero, then the parent
3176 * link count was not incremented in JudgeEntry().
3178 if (class == vLarge) { /* directory vnode */
3179 pv = vnodeIdToBitNumber(vep->parent);
3180 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3181 vnodeInfo[vLarge].vnodes[pv].count++;
3185 continue; /* If no rootdir, can't attach orphaned files */
3187 /* Here we attach orphaned files and directories into the
3188 * root directory, LVVnode, making sure link counts stay correct.
3190 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3191 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3192 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3194 /* Update this orphaned vnode's info. Its parent info and
3195 * link count (do for orphaned directories and files).
3197 vep->parent = LFVnode; /* Parent is the root dir */
3198 vep->unique = LFUnique;
3201 vep->count--; /* Inc link count (root dir will pt to it) */
3203 /* If this orphaned vnode is a directory, change '..'.
3204 * The name of the orphaned dir/file is unknown, so we
3205 * build a unique name. No need to CopyOnWrite the directory
3206 * since it is not connected to tree in BK or RO volume and
3207 * won't be visible there.
3209 if (class == vLarge) {
3213 /* Remove and recreate the ".." entry in this orphaned directory */
3214 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3216 pa.Unique = LFUnique;
3217 assert(Delete(&dh, "..") == 0);
3218 assert(Create(&dh, "..", &pa) == 0);
3220 /* The original parent's link count was decremented above.
3221 * Here we increment the new parent's link count.
3223 pv = vnodeIdToBitNumber(LFVnode);
3224 vnodeInfo[vLarge].vnodes[pv].count--;
3228 /* Go to the root dir and add this entry. The link count of the
3229 * root dir was incremented when ".." was created. Try 10 times.
3231 for (j=0; j<10; j++) {
3232 pa.Vnode = ThisVnode;
3233 pa.Unique = ThisUnique;
3235 sprintf(npath, "%s.%d.%d",
3236 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3237 ThisVnode, ThisUnique);
3239 CopyOnWrite(&rootdir);
3240 code = Create(&rootdir.dirHandle, npath, &pa);
3243 ThisUnique += 50; /* Try creating a different file */
3246 Log("Attaching orphaned %s to volume's root dir as %s\n",
3247 ((class == vLarge)?"directory":"file"), npath);
3249 } /* for each vnode in the class */
3250 } /* for each class of vnode */
3252 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3254 if (!oldrootdir.copied && rootdir.copied) {
3255 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3257 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3260 DFlush(); /* Flush the changes */
3261 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3262 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3263 orphans = ORPH_IGNORE;
3266 /* Write out all changed vnodes. Orphaned files and directories
3267 * will get removed here also (if requested).
3269 for (class = 0; class < nVNODECLASSES; class++){
3270 int nVnodes = vnodeInfo[class].nVnodes;
3271 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3272 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3273 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3274 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3275 for (i = 0; i<nVnodes; i++) {
3276 register struct VnodeEssence *vnp = &vnodes[i];
3277 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3279 /* If the vnode is good but is unclaimed (not listed in
3280 * any directory entries), then it is orphaned.
3283 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3284 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3288 if (vnp->changed || vnp->count) {
3291 nBytes = IH_IREAD(vnodeInfo[class].handle,
3292 vnodeIndexOffset(vcp, vnodeNumber),
3293 (char*)&vnode, sizeof (vnode));
3294 assert(nBytes == sizeof(vnode));
3296 vnode.parent = vnp->parent;
3297 oldCount = vnode.linkCount;
3298 vnode.linkCount = vnode.linkCount - vnp->count;
3301 orphaned = IsVnodeOrphaned(vnodeNumber);
3303 if (!vnp->todelete) {
3304 /* Orphans should have already been attached (if requested) */
3305 assert(orphans != ORPH_ATTACH);
3306 oblocks += vnp->blockCount;
3309 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3310 BlocksInVolume -= vnp->blockCount;
3312 if (VNDISK_GET_INO(&vnode)) {
3313 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3316 memset(&vnode, 0, sizeof(vnode));
3318 } else if (vnp->count) {
3320 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3321 vnodeNumber, oldCount,
3322 (Testing?"would have changed to":"now"), vnode.linkCount);
3326 vnode.dataVersion++;
3328 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3329 vnodeIndexOffset(vcp, vnodeNumber),
3330 (char*)&vnode, sizeof (vnode));
3331 assert(nBytes == sizeof(vnode));
3337 if (!Showmode && ofiles) {
3338 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3339 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3343 for (class = 0; class < nVNODECLASSES; class++) {
3344 register struct VnodeInfo *vip = &vnodeInfo[class];
3345 for (i=0; i<vip->nVnodes; i++)
3346 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3347 if (vip->vnodes) free(vip->vnodes);
3348 if (vip->inodes) free(vip->inodes);
3351 /* Set correct resource utilization statistics */
3352 volHeader.filecount = FilesInVolume;
3353 volHeader.diskused = BlocksInVolume;
3355 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3356 if (volHeader.uniquifier < (maxunique + 1)) {
3357 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3358 /* Plus 2,000 in case there are workstations out there with
3359 * cached vnodes that have since been deleted
3361 volHeader.uniquifier = (maxunique + 1 + 2000);
3364 /* Turn off the inUse bit; the volume's been salvaged! */
3365 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3366 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3367 volHeader.inService = 1; /* allow service again */
3368 volHeader.needsCallback = (VolumeChanged != 0);
3369 volHeader.dontSalvage = DONT_SALVAGE;
3372 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3373 assert(nBytes == sizeof(volHeader));
3376 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3377 (Testing?"It would have ":""), volHeader.name,
3378 volHeader.id, FilesInVolume, BlocksInVolume);
3380 IH_RELEASE(vnodeInfo[vSmall].handle);
3381 IH_RELEASE(vnodeInfo[vLarge].handle);
3386 void ClearROInUseBit(struct VolumeSummary *summary)
3388 IHandle_t *h = summary->volumeInfoHandle;
3391 VolumeDiskData volHeader;
3393 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3394 assert(nBytes == sizeof(volHeader));
3395 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC)
3396 volHeader.inUse = 0;
3397 volHeader.needsSalvaged = 0;
3398 volHeader.inService = 1;
3399 volHeader.dontSalvage = DONT_SALVAGE;
3401 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3402 assert(nBytes == sizeof(volHeader));
3407 * Possible delete the volume.
3409 * deleteMe - Always do so, only a partial volume.
3411 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3415 if (readOnly(isp) || deleteMe) {
3416 if (isp->volSummary && isp->volSummary->fileName) {
3418 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);
3419 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3421 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3422 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3425 unlink(isp->volSummary->fileName);
3429 Log("%s salvage was unsuccessful: read-write volume %u\n",
3430 message, isp->volumeId);
3431 Abort("Salvage of volume %u aborted\n",
3437 void AskOffline(VolumeId volumeId)
3439 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3440 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3441 Abort("Salvage aborted\n");
3445 void AskOnline(VolumeId volumeId, char *partition)
3447 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3448 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3449 volumeId, partition);
3453 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3455 /* Volume parameter is passed in case iopen is upgraded in future to
3456 * require a volume Id to be passed
3459 IHandle_t *srcH, *destH;
3460 FdHandle_t *srcFdP, *destFdP;
3463 IH_INIT(srcH, device, rwvolume, inode1);
3464 srcFdP = IH_OPEN(srcH);
3465 assert(srcFdP != NULL);
3466 IH_INIT(destH, device, rwvolume, inode2);
3467 destFdP = IH_OPEN(destH);
3469 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3470 assert(FDH_WRITE(destFdP, buf, n) == n);
3472 FDH_REALLYCLOSE(srcFdP);
3473 FDH_REALLYCLOSE(destFdP);
3479 void PrintInodeList(void)
3481 register struct ViceInodeInfo *ip;
3482 struct ViceInodeInfo *buf;
3486 assert(fstat(inodeFd, &status) == 0);
3487 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3488 assert(buf != NULL);
3489 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3490 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3491 for(ip = buf; nInodes--; ip++) {
3492 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3493 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3494 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3499 void PrintInodeSummary(void)
3502 struct InodeSummary *isp;
3504 for (i=0; i<nVolumesInInodeFile; i++) {
3505 isp = &inodeSummary[i];
3506 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3507 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3508 isp->nSpecialInodes, isp->maxUniquifier);
3512 void PrintVolumeSummary(void)
3515 struct VolumeSummary *vsp;
3517 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3518 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3526 assert(0); /* Fork is never executed in the NT code path */
3537 if (ShowLog) showlog();
3539 if (main_thread != pthread_self())
3540 pthread_exit((void*)code);
3548 int Wait(char *prog)
3552 pid = wait(&status);
3554 if (WCOREDUMP(status))
3555 Log("\"%s\" core dumped!\n", prog);
3556 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3561 static char *TimeStamp(time_t clock, int precision)
3564 static char timestamp[20];
3565 lt = localtime(&clock);
3567 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3569 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3573 void CheckLogFile(void)
3575 char oldSlvgLog[AFSDIR_PATH_MAX];
3577 #ifndef AFS_NT40_ENV
3584 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3585 strcat(oldSlvgLog, ".old");
3587 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3588 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3590 if (!logFile) { /* still nothing, use stdout */
3595 #ifndef AFS_NAMEI_ENV
3596 AFS_DEBUG_IOPS_LOG(logFile);
3605 #ifndef AFS_NT40_ENV
3607 printf("Can't show log since using syslog.\n");
3616 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3619 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3622 while (fgets(line, sizeof(line), logFile))
3628 void Log(a,b,c,d,e,f,g,h,i,j,k)
3629 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3633 #ifndef AFS_NT40_ENV
3636 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3640 gettimeofday(&now, 0);
3641 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3642 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3647 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3648 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3650 #ifndef AFS_NT40_ENV
3653 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3657 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3659 if (ShowLog) showlog();
3666 char * ToString(char *s)
3669 p = (char *) malloc(strlen(s)+1);
3676 /* Remove the FORCESALVAGE file */
3677 void RemoveTheForce(char *path)
3679 if (!Testing && ForceSalvage) {
3680 if (chdir(path) == 0)
3681 unlink("FORCESALVAGE");
3685 #ifndef AFS_AIX32_ENV
3687 * UseTheForceLuke - see if we can use the force
3689 int UseTheForceLuke(char *path)
3693 assert(chdir(path) != -1);
3695 return (stat("FORCESALVAGE", &force) == 0);
3699 * UseTheForceLuke - see if we can use the force
3702 * The VRMIX fsck will not muck with the filesystem it is supposedly
3703 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3704 * muck directly with the root inode, which is within the normal
3706 * ListViceInodes() has a side effect of setting ForceSalvage if
3707 * it detects a need, based on root inode examination.
3709 int UseTheForceLuke(char *path)
3712 return 0; /* sorry OB1 */
3717 /* NT support routines */
3719 static char execpathname[MAX_PATH];
3720 int nt_SalvagePartition(char *partName, int jobn)
3725 if (!*execpathname) {
3726 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3727 if (!n || n == 1023)
3730 job.cj_magic = SALVAGER_MAGIC;
3731 job.cj_number = jobn;
3732 (void) strcpy(job.cj_part, partName);
3733 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3738 int nt_SetupPartitionSalvage(void *datap, int len)
3740 childJob_t *jobp = (childJob_t*)datap;
3741 char logname[AFSDIR_PATH_MAX];
3743 if (len != sizeof(childJob_t))
3745 if (jobp->cj_magic != SALVAGER_MAGIC)
3750 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3752 logFile = fopen(logname, "w");
3753 if (!logFile) logFile = stdout;
3759 #endif /* AFS_NT40_ENV */