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)
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 /* We don't want to salvage big files filesystems, since we can't put volumes on
842 int CheckIfBigFilesFS(mountPoint, devName)
846 struct superblock fs;
849 if (strncmp(devName, "/dev/", 5)) {
850 (void) sprintf(name, "/dev/%s", devName);
853 (void) strcpy(name, devName);
856 if (ReadSuper(&fs, name)<0) {
857 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
860 if (IsBigFilesFileSystem(&fs)) {
861 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
869 #define HDSTR "\\Device\\Harddisk"
870 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
871 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
878 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
880 if (strncmp(res, HDSTR, HDLEN)) {
883 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
884 res, HDSTR, p1->devName);
888 d1 = atoi(&res[HDLEN]);
890 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
892 if (strncmp(res, HDSTR, HDLEN)) {
895 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
896 res, HDSTR, p2->devName);
900 d2 = atoi(&res[HDLEN]);
905 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
908 /* This assumes that two partitions with the same device number divided by
909 * PartsPerDisk are on the same disk.
911 void SalvageFileSysParallel(struct DiskPartition *partP)
914 struct DiskPartition *partP;
915 int pid; /* Pid for this job */
916 int jobnumb; /* Log file job number */
917 struct job *nextjob; /* Next partition on disk to salvage */
919 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
920 struct job *thisjob = 0;
921 static int numjobs = 0;
922 static int jobcount = 0;
928 char logFileName[256];
932 /* We have a partition to salvage. Copy it into thisjob */
933 thisjob = (struct job *) malloc(sizeof(struct job));
935 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
938 memset(thisjob, 0, sizeof(struct job));
939 thisjob->partP = partP;
940 thisjob->jobnumb = jobcount;
943 else if (jobcount == 0) {
944 /* We are asking to wait for all jobs (partp == 0), yet we never
947 Log("No file system partitions named %s* found; not salvaged\n",
948 VICE_PARTITION_PREFIX);
952 if (debug || Parallel == 1) {
954 SalvageFileSys(thisjob->partP, 0);
961 /* Check to see if thisjob is for a disk that we are already
962 * salvaging. If it is, link it in as the next job to do. The
963 * jobs array has 1 entry per disk being salvages. numjobs is
964 * the total number of disks currently being salvaged. In
965 * order to keep thejobs array compact, when a disk is
966 * completed, the hightest element in the jobs array is moved
967 * down to now open slot.
969 for (j=0; j<numjobs; j++) {
970 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
971 /* On same disk, add it to this list and return */
972 thisjob->nextjob = jobs[j]->nextjob;
973 jobs[j]->nextjob = thisjob;
980 /* Loop until we start thisjob or until all existing jobs are finished */
981 while ( thisjob || (!partP && (numjobs > 0)) ) {
982 startjob = -1; /* No new job to start */
984 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
985 /* Either the max jobs are running or we have to wait for all
986 * the jobs to finish. In either case, we wait for at least one
987 * job to finish. When it's done, clean up after it.
989 pid = wait(&wstatus);
991 for (j=0; j<numjobs; j++) { /* Find which job it is */
992 if (pid == jobs[j]->pid) break;
995 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
996 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
999 numjobs--; /* job no longer running */
1000 oldjob = jobs[j]; /* remember */
1001 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1002 free(oldjob); /* free the old job */
1004 /* If there is another partition on the disk to salvage, then
1005 * say we will start it (startjob). If not, then put thisjob there
1006 * and say we will start it.
1008 if (jobs[j]) { /* Another partitions to salvage */
1009 startjob = j; /* Will start it */
1010 } else { /* There is not another partition to salvage */
1012 jobs[j] = thisjob; /* Add thisjob */
1014 startjob = j; /* Will start it */
1016 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1017 startjob = -1; /* Don't start it - already running */
1021 /* We don't have to wait for a job to complete */
1023 jobs[numjobs] = thisjob; /* Add this job */
1025 startjob = numjobs; /* Will start it */
1029 /* Start up a new salvage job on a partition in job slot "startjob" */
1030 if (startjob != -1) {
1032 Log("Starting salvage of file system partition %s\n",
1033 jobs[startjob]->partP->name);
1035 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1036 * commands and pass the child job number via the data path.
1038 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1039 jobs[startjob]->jobnumb);
1040 jobs[startjob]->pid = pid;
1045 jobs[startjob]->pid = pid;
1051 for (fd =0; fd < 16; fd++) close(fd);
1052 open("/", 0); dup2(0, 1); dup2(0, 2);
1053 #ifndef AFS_NT40_ENV
1055 openlog(NULL, LOG_PID, useSyslogFacility);
1059 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1060 logFile = fopen(logFileName, "w");
1062 if (!logFile) logFile = stdout;
1064 SalvageFileSys1(jobs[startjob]->partP, 0);
1069 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1071 /* If waited for all jobs to complete, now collect log files and return */
1072 #ifndef AFS_NT40_ENV
1073 if ( ! useSyslog ) /* if syslogging - no need to collect */
1076 for (i=0; i<jobcount; i++) {
1077 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1078 if (passLog = fopen(logFileName, "r")) {
1079 while(fgets(buf, sizeof(buf), passLog)) {
1080 fputs(buf, logFile);
1084 (void)unlink(logFileName);
1092 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1094 if (!canfork || debug || Fork() == 0) {
1095 SalvageFileSys1(partP, singleVolumeNumber);
1096 if (canfork && !debug) {
1102 Wait("SalvageFileSys");
1105 char *get_DevName(pbuffer, wpath)
1106 char *wpath, *pbuffer;
1108 char pbuf[128], *ptr;
1109 strcpy(pbuf, pbuffer);
1110 ptr = (char *)strrchr(pbuf, '/');
1113 strcpy(wpath, pbuf);
1116 ptr = (char *)strrchr(pbuffer, '/');
1118 strcpy(pbuffer, ptr+1);
1124 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1127 char inodeListPath[50];
1128 static char tmpDevName[100];
1129 static char wpath[100];
1130 struct VolumeSummary *vsp, *esp;
1133 fileSysPartition = partP;
1134 fileSysDevice = fileSysPartition->device;
1135 fileSysPathName = VPartitionPath(fileSysPartition);
1138 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1139 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1140 name = partP->devName;
1142 fileSysPath = fileSysPathName;
1143 strcpy(tmpDevName, partP->devName);
1144 name = get_DevName(tmpDevName, wpath);
1145 fileSysDeviceName = name;
1146 filesysfulldev = wpath;
1149 VLockPartition(partP->name);
1150 if (singleVolumeNumber || ForceSalvage)
1153 ForceSalvage = UseTheForceLuke(fileSysPath);
1155 if (singleVolumeNumber) {
1156 if (!VConnectFS()) {
1157 Abort("Couldn't connect to file server\n");
1159 AskOffline(singleVolumeNumber);
1162 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1164 Log("***Forced salvage of all volumes on this partition***\n");
1169 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1176 assert((dirp = opendir(fileSysPath)) != NULL);
1177 while (dp = readdir(dirp)) {
1178 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1179 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1181 Log("Removing old salvager temp files %s\n", dp->d_name);
1182 strcpy(npath, fileSysPath);
1184 strcat(npath, dp->d_name);
1190 tdir = (tmpdir ? tmpdir : fileSysPath);
1192 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1193 (void) strcpy(inodeListPath, _tempnam(tdir, "salvage.inodes."));
1195 sprintf(inodeListPath, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1197 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1198 unlink(inodeListPath);
1202 /* Using nt_unlink here since we're really using the delete on close
1203 * semantics of unlink. In most places in the salvager, we really do
1204 * mean to unlink the file at that point. Those places have been
1205 * modified to actually do that so that the NT crt can be used there.
1207 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1208 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1210 inodeFd = open(inodeListPath, O_RDONLY);
1211 unlink(inodeListPath);
1214 Abort("Temporary file %s is missing...\n",
1216 if (ListInodeOption) {
1220 /* enumerate volumes in the partition.
1221 figure out sets of read-only + rw volumes.
1222 salvage each set, read-only volumes first, then read-write.
1223 Fix up inodes on last volume in set (whether it is read-write
1226 GetVolumeSummary(singleVolumeNumber);
1228 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1229 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1230 for (j=i; j < nVolumesInInodeFile
1231 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1232 VolumeId vid = inodeSummary[j].volumeId;
1233 struct VolumeSummary *tsp;
1234 /* Scan volume list (from partition root directory) looking for the
1235 current rw volume number in the volume list from the inode scan.
1236 If there is one here that is not in the inode volume list,
1238 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1240 DeleteExtraVolumeHeaderFile(vsp);
1242 /* Now match up the volume summary info from the root directory with the
1243 entry in the volume list obtained from scanning inodes */
1244 inodeSummary[j].volSummary = NULL;
1245 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1246 if (tsp->header.id == vid) {
1247 inodeSummary[j].volSummary = tsp;
1253 /* Salvage the group of volumes (several read-only + 1 read/write)
1254 * starting with the current read-only volume we're looking at.
1256 SalvageVolumeGroup(&inodeSummary[i], j-i);
1259 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1260 for ( ; vsp<esp; vsp++) {
1262 DeleteExtraVolumeHeaderFile(vsp);
1265 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1266 RemoveTheForce(fileSysPath);
1268 if (!Testing && singleVolumeNumber) {
1269 AskOnline(singleVolumeNumber, fileSysPartition->name);
1271 /* Step through the volumeSummary list and set all volumes on-line.
1272 * The volumes were taken off-line in GetVolumeSummary.
1274 for (j=0; j<nVolumes; j++) {
1275 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1280 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1281 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1284 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1287 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1289 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1290 vsp->fileName, (Testing? "would have been ":""));
1292 unlink(vsp->fileName);
1296 CompareInodes(_p1,_p2)
1297 const void *_p1,*_p2;
1299 register const struct ViceInodeInfo *p1 = _p1;
1300 register const struct ViceInodeInfo *p2 = _p2;
1301 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1302 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1303 VolumeId p1rwid, p2rwid;
1304 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1305 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1306 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1307 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1308 if (p1rwid < p2rwid)
1310 if (p1rwid > p2rwid)
1312 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1313 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1314 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1315 return (p1->u.special.type < p2->u.special.type? -1: 1);
1316 if (p1->u.vnode.volumeId == p1rwid)
1318 if (p2->u.vnode.volumeId == p2rwid)
1320 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1322 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1323 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1324 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1326 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1328 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1330 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1332 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1334 /* The following tests are reversed, so that the most desirable
1335 of several similar inodes comes first */
1336 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1337 #ifdef AFS_3DISPARES
1338 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1339 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1342 #ifdef AFS_SGI_EXMAG
1343 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1344 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1349 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1350 #ifdef AFS_3DISPARES
1351 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1352 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1355 #ifdef AFS_SGI_EXMAG
1356 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1357 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1362 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1363 #ifdef AFS_3DISPARES
1364 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1365 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1368 #ifdef AFS_SGI_EXMAG
1369 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1370 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1375 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1376 #ifdef AFS_3DISPARES
1377 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1378 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1381 #ifdef AFS_SGI_EXMAG
1382 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1383 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1391 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1392 register struct InodeSummary * summary)
1394 int volume = ip->u.vnode.volumeId;
1395 int rwvolume = volume;
1396 register n, nSpecial;
1397 register Unique maxunique;
1400 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1402 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1404 rwvolume = ip->u.special.parentId;
1405 /* This isn't quite right, as there could (in error) be different
1406 parent inodes in different special vnodes */
1409 if (maxunique < ip->u.vnode.vnodeUniquifier)
1410 maxunique = ip->u.vnode.vnodeUniquifier;
1414 summary->volumeId = volume;
1415 summary->RWvolumeId = rwvolume;
1416 summary->nInodes =n;
1417 summary->nSpecialInodes = nSpecial;
1418 summary->maxUniquifier = maxunique;
1421 int OnlyOneVolume(inodeinfo, singleVolumeNumber)
1422 struct ViceInodeInfo *inodeinfo;
1423 VolumeId singleVolumeNumber;
1425 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1426 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1427 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1432 * Collect list of inodes in file named by path. If a truly fatal error,
1433 * unlink the file and abort. For lessor errors, return -1. The file will
1434 * be unlinked by the caller.
1436 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1440 struct ViceInodeInfo *ip;
1441 struct InodeSummary summary;
1442 char summaryFileName[50];
1445 char *dev = fileSysPath;
1446 char *wpath = fileSysPath;
1448 char *dev = fileSysDeviceName;
1449 char *wpath = filesysfulldev;
1451 char *part = fileSysPath;
1454 /* This file used to come from vfsck; cobble it up ourselves now... */
1455 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1457 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1462 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1464 if (forceSal && !ForceSalvage) {
1465 Log("***Forced salvage of all volumes on this partition***\n");
1468 inodeFd = open(path, O_RDWR);
1469 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1471 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1473 tdir = (tmpdir ? tmpdir : part);
1475 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1476 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1478 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1480 summaryFile = fopen(summaryFileName, "a+");
1481 if (summaryFile == NULL) {
1484 Abort("Unable to create inode summary file\n");
1486 if (!canfork || debug || Fork() == 0) {
1488 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1490 fclose(summaryFile); close(inodeFd);
1491 unlink(summaryFileName);
1492 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1493 RemoveTheForce(fileSysPath);
1494 Log("%s vice inodes on %s; not salvaged\n",
1495 singleVolumeNumber? "No applicable": "No", dev);
1498 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1500 fclose(summaryFile); close(inodeFd);
1502 unlink(summaryFileName);
1503 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1505 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1506 fclose(summaryFile); close(inodeFd);
1508 unlink(summaryFileName);
1509 Abort("Unable to read inode table; %s not salvaged\n", dev);
1511 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1512 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1513 write(inodeFd, ip, status.st_size) != status.st_size) {
1514 fclose(summaryFile); close(inodeFd);
1516 unlink(summaryFileName);
1517 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1521 CountVolumeInodes(ip, nInodes, &summary);
1522 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1523 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1524 fclose(summaryFile); close(inodeFd);
1527 summary.index += (summary.nInodes);
1528 nInodes -= summary.nInodes;
1529 ip += summary.nInodes;
1531 /* Following fflush is not fclose, because if it was debug mode would not work */
1532 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1533 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1534 fclose(summaryFile); close(inodeFd);
1537 if (canfork && !debug) {
1543 if (Wait("Inode summary") == -1) {
1544 fclose(summaryFile); close(inodeFd);
1546 unlink(summaryFileName);
1547 Exit(1); /* salvage of this partition aborted */
1550 assert(fstat(fileno(summaryFile), &status) != -1);
1551 if ( status.st_size != 0 ) {
1553 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1554 assert(inodeSummary != NULL);
1555 /* For GNU we need to do lseek to get the file pointer moved. */
1556 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1557 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1558 assert(ret == status.st_size);
1560 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1561 fclose(summaryFile);
1563 unlink(summaryFileName);
1567 /* Comparison routine for volume sort.
1568 This is setup so that a read-write volume comes immediately before
1569 any read-only clones of that volume */
1570 CompareVolumes(_p1,_p2)
1571 const void *_p1,*_p2;
1573 register const struct VolumeSummary *p1 = _p1;
1574 register const struct VolumeSummary *p2 = _p2;
1575 if (p1->header.parent != p2->header.parent)
1576 return p1->header.parent < p2->header.parent? -1: 1;
1577 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1579 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1581 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1584 void GetVolumeSummary(VolumeId singleVolumeNumber)
1587 afs_int32 nvols = 0;
1588 struct VolumeSummary *vsp, vs;
1589 struct VolumeDiskHeader diskHeader;
1592 /* Get headers from volume directory */
1593 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1594 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1595 if (!singleVolumeNumber) {
1596 while (dp = readdir(dirp)) {
1597 char *p = dp->d_name;
1598 p = strrchr(dp->d_name, '.');
1599 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1601 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1602 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1603 == sizeof (diskHeader) &&
1604 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1605 DiskToVolumeHeader(&vs.header, &diskHeader);
1612 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1616 if (!nvols) nvols = 1;
1617 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1619 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1620 assert(volumeSummaryp != NULL);
1623 vsp = volumeSummaryp;
1624 while (dp = readdir(dirp)) {
1625 char *p = dp->d_name;
1626 p = strrchr(dp->d_name, '.');
1627 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1630 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1631 || read(fd, &diskHeader, sizeof (diskHeader))
1632 != sizeof (diskHeader)
1633 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1638 if (!singleVolumeNumber) {
1639 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1640 dp->d_name, (Testing? "it would have been ":""));
1646 char nameShouldBe[64];
1647 DiskToVolumeHeader(&vsp->header, &diskHeader);
1648 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1649 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1652 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1653 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1654 if (singleVolumeNumber)
1655 AskOffline(vsp->header.id);
1656 if (strcmp(nameShouldBe, dp->d_name)) {
1657 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 ":""));
1662 vsp->fileName = ToString(dp->d_name);
1672 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1675 /* Find the link table. This should be associated with the RW volume or, if
1676 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1678 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1679 struct ViceInodeInfo *allInodes)
1682 struct ViceInodeInfo *ip;
1684 for (i=0; i<nVols; i++) {
1685 ip = allInodes + isp[i].index;
1686 for (j=0; j<isp[i].nSpecialInodes; j++) {
1687 if (ip[j].u.special.type == VI_LINKTABLE)
1688 return ip[j].inodeNumber;
1694 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1696 struct versionStamp version;
1699 if (!VALID_INO(ino))
1700 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1701 isp->volumeId, INODESPECIAL,
1702 VI_LINKTABLE, isp->RWvolumeId);
1703 if (!VALID_INO(ino))
1704 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1705 isp->RWvolumeId, errno);
1706 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1707 fdP = IH_OPEN(VGLinkH);
1709 Abort("Can't open link table for volume %u (error = %d)\n",
1710 isp->RWvolumeId, errno);
1712 if (FDH_TRUNC(fdP, 0)<0)
1713 Abort("Can't truncate link table for volume %u (error = %d)\n",
1714 isp->RWvolumeId, errno);
1716 version.magic = LINKTABLEMAGIC;
1717 version.version = LINKTABLEVERSION;
1719 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1721 Abort("Can't truncate link table for volume %u (error = %d)\n",
1722 isp->RWvolumeId, errno);
1724 FDH_REALLYCLOSE(fdP);
1726 /* If the volume summary exits (i.e., the V*.vol header file exists),
1727 * then set this inode there as well.
1729 if (isp->volSummary)
1730 isp->volSummary->header.linkTable = ino;
1736 void *nt_SVG(void *arg)
1738 SVGParms_t *parms = (SVGParms_t*)arg;
1739 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1743 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1746 pthread_attr_t tattr;
1750 /* Initialize per volume global variables, even if later code does so */
1754 memset(&VolInfo, 0, sizeof(VolInfo));
1756 parms.svgp_inodeSummaryp = isp;
1757 parms.svgp_count = nVols;
1758 code = pthread_attr_init(&tattr);
1760 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1764 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1766 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1770 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1772 Log("Failed to create thread to salvage volume group %u\n",
1776 (void) pthread_join(tid, NULL);
1778 #endif /* AFS_NT40_ENV */
1780 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1782 struct ViceInodeInfo *inodes,*allInodes,*ip;
1783 int i, totalInodes, size, salvageTo;
1787 int dec_VGLinkH = 0;
1789 FdHandle_t *fdP = NULL;
1792 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1793 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1794 if (!ForceSalvage && QuickCheck(isp, nVols))
1797 if (ShowMounts && !haveRWvolume)
1799 if (canfork && !debug && Fork() != 0) {
1800 (void) Wait("Salvage volume group");
1803 for (i = 0, totalInodes = 0; i<nVols; i++)
1804 totalInodes += isp[i].nInodes;
1805 size = totalInodes * sizeof (struct ViceInodeInfo);
1806 inodes = (struct ViceInodeInfo *) malloc(size);
1807 allInodes = inodes - isp->index; /* this would the base of all the inodes
1808 for the partition, if all the inodes
1809 had been read into memory */
1810 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1)
1811 assert(read(inodeFd,inodes,size) == size)
1813 /* Don't try to salvage a read write volume if there isn't one on this
1815 salvageTo = haveRWvolume? 0:1;
1817 #ifdef AFS_NAMEI_ENV
1818 ino = FindLinkHandle(isp, nVols, allInodes);
1819 if (VALID_INO(ino)) {
1820 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1821 fdP = IH_OPEN(VGLinkH);
1823 if (!VALID_INO(ino) || fdP == NULL) {
1824 Log("%s link table for volume %u.\n",
1825 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1827 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1830 CreateLinkTable(isp, ino);
1834 FDH_REALLYCLOSE(fdP);
1836 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1839 /* Salvage in reverse order--read/write volume last; this way any
1840 Inodes not referenced by the time we salvage the read/write volume
1841 can be picked up by the read/write volume */
1842 /* ACTUALLY, that's not done right now--the inodes just vanish */
1843 for (i = nVols-1; i>=salvageTo; i--) {
1845 struct InodeSummary *lisp = &isp[i];
1846 #ifdef AFS_NAMEI_ENV
1847 /* If only the RO is present on this partition, the link table
1848 * shows up as a RW volume special file. Need to make sure the
1849 * salvager doesn't try to salvage the non-existent RW.
1851 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1852 /* If this only special inode is the link table, continue */
1853 if (inodes->u.special.type == VI_LINKTABLE) {
1859 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1860 lisp->volumeId, (Testing?"(READONLY mode)":""));
1861 /* Check inodes twice. The second time do things seriously. This
1862 way the whole RO volume can be deleted, below, if anything goes wrong */
1863 for (check = 1; check>=0; check--) {
1865 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1866 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1867 if (rw && deleteMe) {
1868 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1869 volume won't be called */
1875 if (rw && check == 1)
1877 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1878 MaybeZapVolume(lisp,"Vnode index", 0, check);
1884 /* Fix actual inode counts */
1886 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1887 static int TraceBadLinkCounts = 0;
1888 #ifdef AFS_NAMEI_ENV
1889 if (VGLinkH->ih_ino == ip->inodeNumber) {
1890 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1891 VGLinkH_p1 = ip->u.param[0];
1892 continue; /* Deal with this last. */
1895 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1896 TraceBadLinkCounts--; /* Limit reports, per volume */
1897 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1898 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1899 ip->byteCount, ip->u.param[0], ip->u.param[1],
1900 ip->u.param[2], ip->u.param[3]);
1902 while (ip->linkCount > 0) {
1903 /* below used to assert, not break */
1905 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1906 Log ("idec failed. inode %s errno %d\n",
1907 PrintInode(NULL, ip->inodeNumber), errno);
1913 while (ip->linkCount < 0) {
1914 /* these used to be asserts */
1916 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1917 Log ("iinc failed. inode %s errno %d\n",
1918 PrintInode(NULL, ip->inodeNumber) ,errno);
1925 #ifdef AFS_NAMEI_ENV
1926 while (dec_VGLinkH > 0) {
1927 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1928 Log("idec failed on link table, errno = %d\n", errno);
1932 while (dec_VGLinkH < 0) {
1933 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1934 Log("iinc failed on link table, errno = %d\n", errno);
1941 /* Directory consistency checks on the rw volume */
1943 SalvageVolume(isp, VGLinkH);
1944 IH_RELEASE(VGLinkH);
1946 if (canfork && !debug) {
1952 int QuickCheck(register struct InodeSummary *isp, int nVols)
1954 /* Check headers BEFORE forking */
1958 for (i = 0; i<nVols; i++) {
1959 struct VolumeSummary *vs = isp[i].volSummary;
1960 VolumeDiskData volHeader;
1962 /* Don't salvage just because phantom rw volume is there... */
1963 /* (If a read-only volume exists, read/write inodes must also exist) */
1964 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1968 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1969 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1970 == sizeof(volHeader)
1971 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1972 && volHeader.dontSalvage == DONT_SALVAGE
1973 && volHeader.needsSalvaged == 0
1974 && volHeader.destroyMe == 0) {
1975 if (volHeader.inUse == 1) {
1976 volHeader.inUse = 0;
1977 volHeader.inService = 1;
1979 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1980 != sizeof(volHeader)) {
1997 /* SalvageVolumeHeaderFile
1999 * Salvage the top level V*.vol header file. Make sure the special files
2000 * exist and that there are no duplicates.
2002 * Calls SalvageHeader for each possible type of volume special file.
2005 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2006 register struct ViceInodeInfo *inodes,
2007 int RW, int check, int *deleteMe)
2011 register struct ViceInodeInfo *ip;
2012 int allinodesobsolete = 1;
2013 struct VolumeDiskHeader diskHeader;
2017 memset(&tempHeader, 0, sizeof(tempHeader));
2018 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2019 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2020 tempHeader.id = isp->volumeId;
2021 tempHeader.parent = isp->RWvolumeId;
2022 /* Check for duplicates (inodes are sorted by type field) */
2023 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2024 ip = &inodes[isp->index+i];
2025 if (ip->u.special.type == (ip+1)->u.special.type) {
2026 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2030 for (i = 0; i<isp->nSpecialInodes; i++) {
2031 ip = &inodes[isp->index+i];
2032 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2034 Log("Rubbish header inode\n");
2037 Log("Rubbish header inode; deleted\n");
2039 else if (!stuff[ip->u.special.type-1].obsolete) {
2040 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2041 if (!check && ip->u.special.type != VI_LINKTABLE)
2042 ip->linkCount--; /* Keep the inode around */
2043 allinodesobsolete = 0;
2047 if (allinodesobsolete) {
2054 VGLinkH_cnt ++; /* one for every header. */
2056 if (!RW && !check && isp->volSummary) {
2057 ClearROInUseBit(isp->volSummary);
2061 for (i = 0; i< MAXINODETYPE; i++) {
2062 if (stuff[i].inodeType == VI_LINKTABLE) {
2063 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2064 * And we may have recreated the link table earlier, so set the
2065 * RW header as well.
2067 if (VALID_INO(VGLinkH->ih_ino)) {
2068 *stuff[i].inode = VGLinkH->ih_ino;
2072 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2076 if (isp->volSummary == NULL) {
2078 sprintf(name, VFORMAT, isp->volumeId);
2080 Log("No header file for volume %u\n", isp->volumeId);
2083 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2084 isp->volumeId, (Testing?"it would have been ":""),
2085 fileSysPathName, name);
2086 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2087 assert(headerFd != -1)
2088 isp->volSummary = (struct VolumeSummary *)
2089 malloc(sizeof(struct VolumeSummary));
2090 isp->volSummary->fileName = ToString(name);
2094 /* hack: these two fields are obsolete... */
2095 isp->volSummary->header.volumeAcl = 0;
2096 isp->volSummary->header.volumeMountTable = 0;
2098 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2099 /* We often remove the name before calling us, so we make a fake one up */
2100 if (isp->volSummary->fileName) {
2101 strcpy(name, isp->volSummary->fileName);
2103 sprintf(name, VFORMAT, isp->volumeId);
2104 isp->volSummary->fileName = ToString(name);
2107 Log("Header file %s is damaged or no longer valid%s\n",
2108 name, (check ? "" : "; repairing"));
2112 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2113 assert(headerFd != -1)
2117 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2119 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2121 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2122 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2123 != sizeof(struct VolumeDiskHeader)) {
2124 Log("Couldn't rewrite volume header file!\n");
2131 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2132 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2136 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2137 int check, int *deleteMe)
2140 VolumeDiskData volumeInfo;
2141 struct versionStamp fileHeader;
2150 #ifndef AFS_NAMEI_ENV
2151 if ( sp->inodeType == VI_LINKTABLE)
2154 if (*(sp->inode) == 0) {
2156 Log("Missing inode in volume header (%s)\n", sp->description);
2159 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2160 sp->description, (Testing ? "it would have recreated it": "recreating"));
2162 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2163 isp->volumeId, INODESPECIAL,
2164 sp->inodeType, isp->RWvolumeId);
2165 if (!VALID_INO(*(sp->inode)))
2166 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2167 sp->description, errno);
2172 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2173 fdP = IH_OPEN(specH);
2174 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2175 /* bail out early and destroy the volume */
2176 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2178 if (deleteMe) *deleteMe = 1;
2183 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2184 sp->description, errno);
2187 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2188 || header.fileHeader.magic != sp->stamp.magic)) {
2190 Log("Part of the header (%s) is corrupted\n", sp->description);
2191 FDH_REALLYCLOSE(fdP);
2195 Log("Part of the header (%s) is corrupted; recreating\n",
2199 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2202 FDH_REALLYCLOSE(fdP);
2206 if (recreate && !Testing) {
2208 Abort("Internal error: recreating volume header (%s) in check mode\n",
2210 code = FDH_TRUNC(fdP, 0);
2212 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2213 sp->description, errno);
2215 /* The following code should be moved into vutil.c */
2216 if (sp->inodeType == VI_VOLINFO) {
2218 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2219 header.volumeInfo.stamp = sp->stamp;
2220 header.volumeInfo.id = isp->volumeId;
2221 header.volumeInfo.parentId = isp->RWvolumeId;
2222 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2223 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2224 header.volumeInfo.inService = 0;
2225 header.volumeInfo.blessed = 0;
2226 /* The + 1000 is a hack in case there are any files out in venus caches */
2227 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2228 header.volumeInfo.type =
2229 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2230 header.volumeInfo.needsCallback = 0;
2231 gettimeofday(&tp,0);
2232 header.volumeInfo.creationDate = tp.tv_sec;
2233 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2234 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2236 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2237 sizeof(header.volumeInfo));
2238 if (code != sizeof(header.volumeInfo)) {
2240 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2241 sp->description, errno);
2242 Abort("Unable to write entire volume header file (%s)\n",
2247 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2248 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2250 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2251 if (code != sizeof(sp->stamp)) {
2253 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2254 sp->description, errno);
2255 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2260 FDH_REALLYCLOSE(fdP);
2262 if (sp->inodeType == VI_VOLINFO) {
2263 VolInfo = header.volumeInfo;
2266 if (VolInfo.updateDate) {
2267 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2268 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2269 (Testing?"it would have been ":""), update);
2271 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2272 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2281 int SalvageVnodes(register struct InodeSummary *rwIsp,
2282 register struct InodeSummary * thisIsp,
2283 register struct ViceInodeInfo * inodes, int check)
2285 int ilarge, ismall, ioffset, RW, nInodes;
2286 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2287 if (Showmode) return 0;
2288 RW = (rwIsp == thisIsp);
2289 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2290 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2291 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2292 if (check && ismall == -1)
2294 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2295 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2296 return (ilarge==0 && ismall==0 ? 0: -1);
2299 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2300 register struct ViceInodeInfo *ip,
2301 int nInodes, struct VolumeSummary *volSummary, int check)
2303 VolumeId volumeNumber;
2304 char buf[SIZEOF_LARGEDISKVNODE];
2305 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2307 StreamHandle_t *file;
2308 struct VnodeClassInfo *vcp;
2310 int vnodeIndex, nVnodes;
2311 afs_ino_str_t stmp1, stmp2;
2315 volumeNumber = volSummary->header.id;
2316 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2317 fdP = IH_OPEN(handle);
2318 assert(fdP != NULL);
2319 file = FDH_FDOPEN(fdP, "r+");
2320 assert(file != NULL)
2321 vcp = &VnodeClassInfo[class];
2322 size = OS_SIZE(fdP->fd_fd);
2324 nVnodes = (size / vcp->diskSize) - 1;
2326 assert((nVnodes+1) * vcp->diskSize == size)
2327 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2332 for (vnodeIndex = 0;
2333 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2334 nVnodes--, vnodeIndex++) {
2335 if (vnode->type != vNull) {
2336 int vnodeChanged = 0;
2337 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2338 /* Log programs that belong to root (potentially suid root);
2339 don't bother for read-only or backup volumes */
2340 #ifdef notdef /* This is done elsewhere */
2341 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2342 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2343 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2344 vnode->owner, vnode->modeBits);
2346 if (VNDISK_GET_INO(vnode) == 0) {
2348 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2349 memset(vnode, 0, vcp->diskSize);
2354 if (vcp->magic != vnode->vnodeMagic) {
2355 /* bad magic #, probably partially created vnode */
2356 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2357 memset(vnode, 0, vcp->diskSize);
2361 /* ****** Should do a bit more salvage here: e.g. make sure
2362 vnode type matches what it should be given the index */
2363 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2364 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2365 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2366 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2373 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2374 /* The following doesn't work, because the version number
2375 is not maintained correctly by the file server */
2376 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2377 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2379 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2386 /* For RW volume, look for vnode with matching inode number;
2387 if no such match, take the first determined by our sort
2389 register struct ViceInodeInfo *lip = ip;
2390 register lnInodes = nInodes;
2391 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2392 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2401 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2402 /* "Matching" inode */
2406 vu = vnode->uniquifier;
2407 iu = ip->u.vnode.vnodeUniquifier;
2408 vd = vnode->dataVersion;
2409 id = ip->u.vnode.inodeDataVersion;
2411 * Because of the possibility of the uniquifier overflows (> 4M)
2412 * we compare them modulo the low 22-bits; we shouldn't worry
2413 * about mismatching since they shouldn't to many old
2414 * uniquifiers of the same vnode...
2416 if (IUnique(vu) != IUnique(iu)) {
2418 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2419 vnodeNumber, IUnique(vu), IUnique(iu));
2422 vnode->uniquifier = iu;
2423 #ifdef AFS_3DISPARES
2424 vnode->dataVersion = (id >= vd ?
2425 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2426 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2428 #if defined(AFS_SGI_EXMAG)
2429 vnode->dataVersion = (id >= vd ?
2430 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2431 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2433 vnode->dataVersion = (id>vd ? id:vd);
2434 #endif /* AFS_SGI_EXMAG */
2435 #endif /* AFS_3DISPARES */
2439 /* don't bother checking for vd > id any more, since
2440 partial file transfers always result in this state,
2441 and you can't do much else anyway (you've already
2442 found the best data you can) */
2443 #ifdef AFS_3DISPARES
2444 if (!vnodeIsDirectory(vnodeNumber) &&
2445 ((vd < id && (id-vd) < 1887437) ||
2446 ((vd > id && (vd-id) > 1887437)))) {
2448 #if defined(AFS_SGI_EXMAG)
2449 if (!vnodeIsDirectory(vnodeNumber) &&
2450 ((vd < id && (id-vd) < 15099494) ||
2451 ((vd > id && (vd-id) > 15099494)))) {
2453 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2454 #endif /* AFS_SGI_EXMAG */
2456 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2457 vnode->dataVersion = id;
2462 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2465 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2466 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2467 PrintInode(stmp2, ip->inodeNumber),
2470 VNDISK_SET_INO(vnode, ip->inodeNumber);
2475 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2477 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2478 PrintInode(stmp2, ip->inodeNumber),
2481 VNDISK_SET_INO(vnode, ip->inodeNumber);
2484 if (ip->byteCount != vnode->length) {
2486 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2487 vnodeNumber, vnode->length, ip->byteCount);
2491 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2492 vnodeNumber, vnode->length, ip->byteCount);
2493 vnode->length = ip->byteCount;
2497 ip->linkCount--; /* Keep the inode around */
2501 else { /* no matching inode */
2502 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2503 /* No matching inode--get rid of the vnode */
2505 if (VNDISK_GET_INO(vnode)) {
2507 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2508 vnodeNumber, vnode->uniquifier,
2509 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2512 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2517 if (VNDISK_GET_INO(vnode)) {
2519 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2520 vnodeNumber, vnode->uniquifier,
2521 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2522 ctime((time_t *)&(vnode->serverModifyTime)));
2525 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)));
2527 memset(vnode, 0, vcp->diskSize);
2530 /* Should not reach here becuase we checked for
2531 * (inodeNumber == 0) above. And where we zero the vnode,
2532 * we also goto vnodeDone.
2536 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2540 } /* VNDISK_GET_INO(vnode) != 0 */
2542 assert(!(vnodeChanged && check));
2543 if (vnodeChanged && !Testing) {
2544 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2545 (char*)vnode, vcp->diskSize)
2547 VolumeChanged = 1; /* For break call back */
2558 struct VnodeEssence *CheckVnodeNumber(vnodeNumber)
2559 VnodeId vnodeNumber;
2562 struct VnodeInfo *vip;
2565 class = vnodeIdToClass(vnodeNumber);
2566 vip = &vnodeInfo[class];
2567 offset = vnodeIdToBitNumber(vnodeNumber);
2568 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2572 void CopyOnWrite(register struct DirSummary *dir)
2574 /* Copy the directory unconditionally if we are going to change it:
2575 * not just if was cloned.
2577 struct VnodeDiskObject vnode;
2578 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2579 Inode oldinode, newinode;
2582 if (dir->copied || Testing)
2584 DFlush(); /* Well justified paranoia... */
2586 code = IH_IREAD(vnodeInfo[vLarge].handle,
2587 vnodeIndexOffset(vcp, dir->vnodeNumber),
2588 (char*)&vnode, sizeof (vnode));
2589 assert(code == sizeof(vnode));
2590 oldinode = VNDISK_GET_INO(&vnode);
2591 /* Increment the version number by a whole lot to avoid problems with
2592 * clients that were promised new version numbers--but the file server
2593 * crashed before the versions were written to disk.
2595 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2596 dir->rwVid, dir->vnodeNumber,
2597 vnode.uniquifier, vnode.dataVersion += 200);
2598 assert(VALID_INO(newinode));
2599 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2601 VNDISK_SET_INO(&vnode, newinode);
2602 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2603 vnodeIndexOffset(vcp, dir->vnodeNumber),
2604 (char*)&vnode, sizeof (vnode));
2605 assert(code == sizeof (vnode));
2607 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2608 fileSysDevice, newinode);
2609 /* Don't delete the original inode right away, because the directory is
2610 * still being scanned.
2615 /* This function should either successfully create a new dir, or give up and leave
2616 * things the way they were. In particular, if it fails to write the new dir properly,
2617 * it should return w/o changing the reference to the old dir.
2619 void CopyAndSalvage(register struct DirSummary *dir)
2621 struct VnodeDiskObject vnode;
2622 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2623 Inode oldinode, newinode;
2625 register afs_int32 code;
2626 afs_int32 parentUnique= 1;
2627 struct VnodeEssence *vnodeEssence;
2631 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2632 code = IH_IREAD(vnodeInfo[vLarge].handle,
2633 vnodeIndexOffset(vcp, dir->vnodeNumber),
2634 (char*)&vnode, sizeof (vnode));
2635 assert(code == sizeof (vnode));
2636 oldinode = VNDISK_GET_INO(&vnode);
2637 /* Increment the version number by a whole lot to avoid problems with
2638 * clients that were promised new version numbers--but the file server
2639 * crashed before the versions were written to disk.
2641 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2642 dir->rwVid, dir->vnodeNumber,
2644 vnode.dataVersion += 200);
2645 assert(VALID_INO(newinode));
2646 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2648 /* Assign . and .. vnode numbers from dir and vnode.parent.
2649 * The uniquifier for . is in the vnode.
2650 * The uniquifier for .. might be set to a bogus value of 1 and
2651 * the salvager will later clean it up.
2653 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2654 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2656 code = DirSalvage(&dir->dirHandle, &newdir,
2657 dir->vnodeNumber, vnode.uniquifier,
2658 (vnode.parent?vnode.parent:dir->vnodeNumber),
2660 if (code == 0) code = DFlush();
2662 /* didn't really build the new directory properly, let's just give up. */
2663 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2665 Log("Directory salvage returned code %d, continuing.\n", code);
2668 Log("Checking the results of the directory salvage...\n");
2669 if (!DirOK(&newdir)) {
2670 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2671 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2676 VNDISK_SET_INO(&vnode, newinode);
2677 vnode.length = Length(&newdir);
2678 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2679 vnodeIndexOffset(vcp, dir->vnodeNumber),
2680 (char*)&vnode, sizeof (vnode));
2681 assert(code == sizeof (vnode));
2683 nt_sync(fileSysDevice);
2685 sync(); /* this is slow, but hopefully rarely called. We don't have
2686 * an open FD on the file itself to fsync.
2689 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2691 dir->dirHandle = newdir;
2694 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2697 struct VnodeEssence *vnodeEssence;
2698 afs_int32 dirOrphaned, todelete;
2700 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2702 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2703 if (vnodeEssence == NULL) {
2705 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2706 dir->vnodeNumber, dir->name?dir->name:"??",
2707 name, vnodeNumber, unique);
2711 assert(Delete(&dir->dirHandle, name) == 0)
2717 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2718 * mount inode for the partition. If this inode were deleted, it would crash
2721 if (vnodeEssence->InodeNumber == 0) {
2722 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2723 dir->vnodeNumber, (dir->name?dir->name:"??"),
2724 name, vnodeNumber, unique,
2725 (Testing?"-- would have deleted":" -- deleted"));
2728 assert(Delete(&dir->dirHandle, name) == 0);
2734 if (!(vnodeNumber & 1) && !Showmode &&
2735 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2736 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2737 dir->vnodeNumber, (dir->name?dir->name:"??"),
2738 name, vnodeNumber, unique,
2739 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2743 assert(Delete(&dir->dirHandle, name) == 0);
2749 /* Check if the Uniquifiers match. If not, change the directory entry
2750 * so its unique matches the vnode unique. Delete if the unique is zero
2751 * or if the directory is orphaned.
2753 if (!IUnique(vnodeEssence->unique) ||
2754 (IUnique(vnodeEssence->unique) != IUnique(unique)) ) {
2755 if ( !IUnique(vnodeEssence->unique) &&
2756 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2757 /* This is an orphaned directory. Don't delete the . or ..
2758 * entry. Otherwise, it will get created in the next
2759 * salvage and deleted again here. So Just skip it.
2764 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2767 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2768 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2769 name, vnodeNumber, unique, vnodeEssence->unique,
2770 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2774 fid.Vnode = vnodeNumber;
2775 fid.Unique = vnodeEssence->unique;
2777 assert(Delete(&dir->dirHandle, name) == 0)
2779 assert(Create(&dir->dirHandle, name, &fid) == 0)
2781 if (todelete) return; /* no need to continue */
2784 if (strcmp(name,".") == 0) {
2785 if (dir->vnodeNumber != vnodeNumber || (IUnique(dir->unique) != IUnique(unique))) {
2787 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2788 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2791 assert(Delete(&dir->dirHandle, ".") == 0)
2792 fid.Vnode = dir->vnodeNumber;
2793 fid.Unique = dir->unique;
2794 assert(Create(&dir->dirHandle, ".", &fid) == 0)
2797 vnodeNumber = fid.Vnode; /* Get the new Essence */
2798 unique = fid.Unique;
2799 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2803 else if (strcmp(name,"..") == 0) {
2806 struct VnodeEssence *dotdot;
2807 pa.Vnode = dir->parent;
2808 dotdot = CheckVnodeNumber(pa.Vnode);
2809 assert (dotdot != NULL); /* XXX Should not be assert */
2810 pa.Unique = dotdot->unique;
2813 pa.Vnode = dir->vnodeNumber;
2814 pa.Unique = dir->unique;
2816 if ((pa.Vnode != vnodeNumber) || (IUnique(pa.Unique) != IUnique(unique))) {
2817 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2818 dir->vnodeNumber, IUnique(dir->unique), vnodeNumber, IUnique(unique));
2821 assert(Delete(&dir->dirHandle, "..") == 0);
2822 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2825 vnodeNumber = pa.Vnode; /* Get the new Essence */
2827 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2829 dir->haveDotDot = 1;
2830 } else if (strncmp(name,".__afs",6) == 0) {
2832 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2833 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2837 assert(Delete(&dir->dirHandle, name) == 0)
2839 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2840 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2844 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2845 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2846 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber, dir->vnodeNumber);
2847 if (ShowMounts && (vnodeEssence->type == vSymlink) && !(vnodeEssence->modeBits & 0111)) {
2853 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2854 vnodeEssence->InodeNumber);
2856 assert(fdP != NULL);
2857 size = FDH_SIZE(fdP);
2859 memset(buf, 0, 1024);
2860 if (size > 1024) size = 1024;
2861 code = FDH_READ(fdP, buf, size);
2862 assert(code == size);
2863 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2864 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2865 dir->name?dir->name:"??", name, buf);
2866 FDH_REALLYCLOSE(fdP);
2869 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2870 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2871 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2872 if (vnodeIdToClass(vnodeNumber) == vLarge && vnodeEssence->name == (char *)0) {
2874 if (n = (char*)malloc(strlen(name)+1))
2876 vnodeEssence->name = n;
2879 /* The directory entry points to the vnode. Check to see if the
2880 * vnode points back to the directory. If not, then let the
2881 * directory claim it (else it might end up orphaned). Vnodes
2882 * already claimed by another directory are deleted from this
2883 * directory: hardlinks to the same vnode are not allowed
2884 * from different directories.
2886 if (vnodeEssence->parent != dir->vnodeNumber) {
2887 if (!vnodeEssence->claimed && !dirOrphaned) {
2888 /* Vnode does not point back to this directory.
2889 * Orphaned dirs cannot claim a file (it may belong to
2890 * another non-orphaned dir).
2893 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2894 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2895 vnodeNumber, unique, (Testing?"would have been ":""),
2896 vnodeEssence->parent, dir->vnodeNumber);
2898 vnodeEssence->parent = dir->vnodeNumber;
2899 vnodeEssence->changed = 1;
2901 /* Vnode was claimed by another directory */
2904 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2905 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2906 vnodeEssence->parent, vnodeNumber, unique,
2907 (Testing?"would have been ":""));
2909 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2910 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2911 vnodeEssence->parent, vnodeNumber, unique,
2912 (Testing?"would have been ":""));
2917 assert(Delete(&dir->dirHandle, name) == 0);
2922 /* This directory claims the vnode */
2923 vnodeEssence->claimed = 1;
2925 vnodeEssence->count--;
2928 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2931 register struct VnodeInfo *vip = &vnodeInfo[class];
2932 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2933 char buf[SIZEOF_LARGEDISKVNODE];
2934 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2936 StreamHandle_t *file;
2941 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2942 fdP = IH_OPEN(vip->handle);
2944 file = FDH_FDOPEN(fdP, "r+");
2945 assert(file != NULL);
2946 size = OS_SIZE(fdP->fd_fd);
2948 vip->nVnodes = (size / vcp->diskSize) - 1;
2949 if (vip->nVnodes > 0) {
2950 assert((vip->nVnodes+1)*vcp->diskSize == size)
2951 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2952 assert((vip->vnodes = (struct VnodeEssence *)
2953 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL)
2954 if (class == vLarge) {
2955 assert((vip->inodes = (Inode *)
2956 calloc(vip->nVnodes, sizeof (Inode))) != NULL)
2967 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2968 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2969 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2970 nVnodes--, vnodeIndex++) {
2971 if (vnode->type != vNull) {
2972 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2973 vip->nAllocatedVnodes++;
2974 vep->count = vnode->linkCount;
2975 vep->blockCount = nBlocks(vnode->length);
2976 vip->volumeBlockCount += vep->blockCount;
2977 vep->parent = vnode->parent;
2978 vep->unique = vnode->uniquifier;
2979 if (*maxu < vnode->uniquifier)
2980 *maxu = vnode->uniquifier;
2981 vep->modeBits = vnode->modeBits;
2982 vep->InodeNumber = VNDISK_GET_INO(vnode);
2983 vep->type = vnode->type;
2984 vep->author = vnode->author;
2985 vep->owner = vnode->owner;
2986 vep->group = vnode->group;
2987 if (vnode->type == vDirectory) {
2988 assert(class == vLarge)
2989 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2997 static char *GetDirName(vnode, vp, path)
2999 struct VnodeEssence *vp;
3002 struct VnodeEssence *parentvp;
3008 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3010 strcat(path, vp->name);
3016 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3017 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3019 static int IsVnodeOrphaned(vnode)
3022 struct VnodeEssence *vep;
3024 if (vnode == 0) return(1); /* Vnode zero does not exist */
3025 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3026 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3027 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3029 return( IsVnodeOrphaned(vep->parent) );
3032 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3033 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3036 static struct DirSummary dir;
3037 static struct DirHandle dirHandle;
3038 struct VnodeEssence *parent;
3039 static char path[MAXPATHLEN];
3042 if (dirVnodeInfo->vnodes[i].salvaged)
3043 return; /* already salvaged */
3046 dirVnodeInfo->vnodes[i].salvaged = 1;
3048 if (dirVnodeInfo->inodes[i] == 0)
3049 return; /* Not allocated to a directory */
3051 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3052 if (parent && parent->salvaged == 0)
3053 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3054 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3055 rootdir, rootdirfound);
3056 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3057 dir.unique = dirVnodeInfo->vnodes[i].unique;
3060 dir.parent = dirVnodeInfo->vnodes[i].parent;
3061 dir.haveDot = dir.haveDotDot = 0;
3062 dir.ds_linkH = alinkH;
3063 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3065 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3068 Log("Directory bad, vnode %d; %s...\n",
3069 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3072 CopyAndSalvage(&dir);
3076 dirHandle = dir.dirHandle;
3078 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3081 /* If enumeration failed for random reasons, we will probably delete
3082 * too much stuff, so we guard against this instead.
3084 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3087 /* Delete the old directory if it was copied in order to salvage.
3088 * CopyOnWrite has written the new inode # to the disk, but we still
3089 * have the old one in our local structure here. Thus, we idec the
3093 if (dir.copied && !Testing) {
3094 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3096 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3099 /* Remember rootdir DirSummary _after_ it has been judged */
3100 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3101 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3108 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3110 /* This routine, for now, will only be called for read-write volumes */
3112 int BlocksInVolume = 0, FilesInVolume = 0;
3113 register VnodeClass class;
3114 struct DirSummary rootdir, oldrootdir;
3115 struct VnodeInfo *dirVnodeInfo;
3116 struct VnodeDiskObject vnode;
3117 VolumeDiskData volHeader;
3119 int orphaned, rootdirfound = 0;
3120 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3121 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3122 struct VnodeEssence *vep;
3127 VnodeId LFVnode, ThisVnode;
3128 Unique LFUnique, ThisUnique;
3131 vid = rwIsp->volSummary->header.id;
3132 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3133 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3134 assert(nBytes == sizeof(volHeader));
3135 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3136 assert (volHeader.destroyMe != DESTROY_ME);
3137 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3139 DistilVnodeEssence(vid, vLarge,
3140 rwIsp->volSummary->header.largeVnodeIndex,
3142 DistilVnodeEssence(vid, vSmall,
3143 rwIsp->volSummary->header.smallVnodeIndex,
3146 dirVnodeInfo = &vnodeInfo[vLarge];
3147 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3148 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3149 &rootdir, &rootdirfound);
3156 /* Parse each vnode looking for orphaned vnodes and
3157 * connect them to the tree as orphaned (if requested).
3159 oldrootdir = rootdir;
3160 for (class=0; class < nVNODECLASSES; class++) {
3161 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3162 vep = &(vnodeInfo[class].vnodes[v]);
3163 ThisVnode = bitNumberToVnodeNumber(v, class);
3164 ThisUnique = vep->unique;
3166 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3167 continue; /* Ignore unused, claimed, and root vnodes */
3169 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3170 * entry in this vnode had incremented the parent link count (In
3171 * JudgeEntry()). We need to go to the parent and decrement that
3172 * link count. But if the parent's unique is zero, then the parent
3173 * link count was not incremented in JudgeEntry().
3175 if (class == vLarge) { /* directory vnode */
3176 pv = vnodeIdToBitNumber(vep->parent);
3177 if (IUnique(vnodeInfo[vLarge].vnodes[pv].unique) != 0)
3178 vnodeInfo[vLarge].vnodes[pv].count++;
3182 continue; /* If no rootdir, can't attach orphaned files */
3184 /* Here we attach orphaned files and directories into the
3185 * root directory, LVVnode, making sure link counts stay correct.
3187 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3188 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3189 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3191 /* Update this orphaned vnode's info. Its parent info and
3192 * link count (do for orphaned directories and files).
3194 vep->parent = LFVnode; /* Parent is the root dir */
3195 vep->unique = LFUnique;
3198 vep->count--; /* Inc link count (root dir will pt to it) */
3200 /* If this orphaned vnode is a directory, change '..'.
3201 * The name of the orphaned dir/file is unknown, so we
3202 * build a unique name. No need to CopyOnWrite the directory
3203 * since it is not connected to tree in BK or RO volume and
3204 * won't be visible there.
3206 if (class == vLarge) {
3210 /* Remove and recreate the ".." entry in this orphaned directory */
3211 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3213 pa.Unique = LFUnique;
3214 assert(Delete(&dh, "..") == 0);
3215 assert(Create(&dh, "..", &pa) == 0);
3217 /* The original parent's link count was decremented above.
3218 * Here we increment the new parent's link count.
3220 pv = vnodeIdToBitNumber(LFVnode);
3221 vnodeInfo[vLarge].vnodes[pv].count--;
3225 /* Go to the root dir and add this entry. The link count of the
3226 * root dir was incremented when ".." was created. Try 10 times.
3228 for (j=0; j<10; j++) {
3229 pa.Vnode = ThisVnode;
3230 pa.Unique = ThisUnique;
3232 sprintf(npath, "%s.%d.%d",
3233 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3234 ThisVnode, ThisUnique);
3236 CopyOnWrite(&rootdir);
3237 code = Create(&rootdir.dirHandle, npath, &pa);
3240 ThisUnique += 50; /* Try creating a different file */
3243 Log("Attaching orphaned %s to volume's root dir as %s\n",
3244 ((class == vLarge)?"directory":"file"), npath);
3246 } /* for each vnode in the class */
3247 } /* for each class of vnode */
3249 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3251 if (!oldrootdir.copied && rootdir.copied) {
3252 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3254 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3257 DFlush(); /* Flush the changes */
3258 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3259 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3260 orphans = ORPH_IGNORE;
3263 /* Write out all changed vnodes. Orphaned files and directories
3264 * will get removed here also (if requested).
3266 for (class = 0; class < nVNODECLASSES; class++){
3267 int nVnodes = vnodeInfo[class].nVnodes;
3268 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3269 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3270 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3271 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3272 for (i = 0; i<nVnodes; i++) {
3273 register struct VnodeEssence *vnp = &vnodes[i];
3274 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3276 /* If the vnode is good but is unclaimed (not listed in
3277 * any directory entries), then it is orphaned.
3280 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3281 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3285 if (vnp->changed || vnp->count) {
3288 nBytes = IH_IREAD(vnodeInfo[class].handle,
3289 vnodeIndexOffset(vcp, vnodeNumber),
3290 (char*)&vnode, sizeof (vnode));
3291 assert(nBytes == sizeof(vnode));
3293 vnode.parent = vnp->parent;
3294 oldCount = vnode.linkCount;
3295 vnode.linkCount = vnode.linkCount - vnp->count;
3298 orphaned = IsVnodeOrphaned(vnodeNumber);
3300 if (!vnp->todelete) {
3301 /* Orphans should have already been attached (if requested) */
3302 assert(orphans != ORPH_ATTACH);
3303 oblocks += vnp->blockCount;
3306 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3307 BlocksInVolume -= vnp->blockCount;
3309 if (VNDISK_GET_INO(&vnode)) {
3310 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3313 memset(&vnode, 0, sizeof(vnode));
3315 } else if (vnp->count) {
3317 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3318 vnodeNumber, oldCount,
3319 (Testing?"would have changed to":"now"), vnode.linkCount);
3323 vnode.dataVersion++;
3325 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3326 vnodeIndexOffset(vcp, vnodeNumber),
3327 (char*)&vnode, sizeof (vnode));
3328 assert(nBytes == sizeof(vnode));
3334 if (!Showmode && ofiles) {
3335 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3336 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3340 for (class = 0; class < nVNODECLASSES; class++) {
3341 register struct VnodeInfo *vip = &vnodeInfo[class];
3342 for (i=0; i<vip->nVnodes; i++)
3343 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3344 if (vip->vnodes) free(vip->vnodes);
3345 if (vip->inodes) free(vip->inodes);
3348 /* Set correct resource utilization statistics */
3349 volHeader.filecount = FilesInVolume;
3350 volHeader.diskused = BlocksInVolume;
3352 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3353 if (volHeader.uniquifier < (maxunique + 1)) {
3354 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3355 /* Plus 2,000 in case there are workstations out there with
3356 * cached vnodes that have since been deleted
3358 volHeader.uniquifier = (maxunique + 1 + 2000);
3361 /* Turn off the inUse bit; the volume's been salvaged! */
3362 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3363 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3364 volHeader.inService = 1; /* allow service again */
3365 volHeader.needsCallback = (VolumeChanged != 0);
3366 volHeader.dontSalvage = DONT_SALVAGE;
3369 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3370 assert(nBytes == sizeof(volHeader));
3373 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3374 (Testing?"It would have ":""), volHeader.name,
3375 volHeader.id, FilesInVolume, BlocksInVolume);
3377 IH_RELEASE(vnodeInfo[vSmall].handle);
3378 IH_RELEASE(vnodeInfo[vLarge].handle);
3383 void ClearROInUseBit(struct VolumeSummary *summary)
3385 IHandle_t *h = summary->volumeInfoHandle;
3388 VolumeDiskData volHeader;
3390 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3391 assert(nBytes == sizeof(volHeader));
3392 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC)
3393 volHeader.inUse = 0;
3394 volHeader.needsSalvaged = 0;
3395 volHeader.inService = 1;
3396 volHeader.dontSalvage = DONT_SALVAGE;
3398 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3399 assert(nBytes == sizeof(volHeader));
3404 * Possible delete the volume.
3406 * deleteMe - Always do so, only a partial volume.
3408 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3412 if (readOnly(isp) || deleteMe) {
3413 if (isp->volSummary && isp->volSummary->fileName) {
3415 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);
3416 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3418 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3419 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3422 unlink(isp->volSummary->fileName);
3426 Log("%s salvage was unsuccessful: read-write volume %u\n",
3427 message, isp->volumeId);
3428 Abort("Salvage of volume %u aborted\n",
3434 void AskOffline(VolumeId volumeId)
3436 if (FSYNC_askfs(volumeId, (char *)0, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3437 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3438 Abort("Salvage aborted\n");
3442 void AskOnline(VolumeId volumeId, char *partition)
3444 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3445 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3446 volumeId, partition);
3450 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3452 /* Volume parameter is passed in case iopen is upgraded in future to
3453 * require a volume Id to be passed
3456 IHandle_t *srcH, *destH;
3457 FdHandle_t *srcFdP, *destFdP;
3460 IH_INIT(srcH, device, rwvolume, inode1);
3461 srcFdP = IH_OPEN(srcH);
3462 assert(srcFdP != NULL);
3463 IH_INIT(destH, device, rwvolume, inode2);
3464 destFdP = IH_OPEN(destH);
3466 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3467 assert(FDH_WRITE(destFdP, buf, n) == n);
3469 FDH_REALLYCLOSE(srcFdP);
3470 FDH_REALLYCLOSE(destFdP);
3476 void PrintInodeList(void)
3478 register struct ViceInodeInfo *ip;
3479 struct ViceInodeInfo *buf;
3483 assert(fstat(inodeFd, &status) == 0);
3484 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3485 assert(buf != NULL);
3486 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3487 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3488 for(ip = buf; nInodes--; ip++) {
3489 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3490 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3491 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3496 void PrintInodeSummary(void)
3499 struct InodeSummary *isp;
3501 for (i=0; i<nVolumesInInodeFile; i++) {
3502 isp = &inodeSummary[i];
3503 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3504 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3505 isp->nSpecialInodes, isp->maxUniquifier);
3509 void PrintVolumeSummary(void)
3512 struct VolumeSummary *vsp;
3514 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3515 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3523 assert(0); /* Fork is never executed in the NT code path */
3534 if (ShowLog) showlog();
3536 if (main_thread != pthread_self())
3537 pthread_exit((void*)code);
3545 int Wait(char *prog)
3549 pid = wait(&status);
3551 if (WCOREDUMP(status))
3552 Log("\"%s\" core dumped!\n", prog);
3553 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3558 static char *TimeStamp(time_t clock, int precision)
3561 static char timestamp[20];
3562 lt = localtime(&clock);
3564 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3566 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3570 void CheckLogFile(void)
3572 char oldSlvgLog[AFSDIR_PATH_MAX];
3574 #ifndef AFS_NT40_ENV
3581 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3582 strcat(oldSlvgLog, ".old");
3584 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3585 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3587 if (!logFile) { /* still nothing, use stdout */
3592 #ifndef AFS_NAMEI_ENV
3593 AFS_DEBUG_IOPS_LOG(logFile);
3602 #ifndef AFS_NT40_ENV
3604 printf("Can't show log since using syslog.\n");
3613 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3616 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3619 while (fgets(line, sizeof(line), logFile))
3625 void Log(a,b,c,d,e,f,g,h,i,j,k)
3626 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3630 #ifndef AFS_NT40_ENV
3633 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3637 gettimeofday(&now, 0);
3638 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3639 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3644 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3645 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3647 #ifndef AFS_NT40_ENV
3650 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3654 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3656 if (ShowLog) showlog();
3663 char * ToString(char *s)
3666 p = (char *) malloc(strlen(s)+1);
3673 /* Remove the FORCESALVAGE file */
3674 void RemoveTheForce(char *path)
3676 if (!Testing && ForceSalvage) {
3677 if (chdir(path) == 0)
3678 unlink("FORCESALVAGE");
3682 #ifndef AFS_AIX32_ENV
3684 * UseTheForceLuke - see if we can use the force
3686 int UseTheForceLuke(char *path)
3690 assert(chdir(path) != -1);
3692 return (stat("FORCESALVAGE", &force) == 0);
3696 * UseTheForceLuke - see if we can use the force
3699 * The VRMIX fsck will not muck with the filesystem it is supposedly
3700 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3701 * muck directly with the root inode, which is within the normal
3703 * ListViceInodes() has a side effect of setting ForceSalvage if
3704 * it detects a need, based on root inode examination.
3706 int UseTheForceLuke(char *path)
3709 return 0; /* sorry OB1 */
3714 /* NT support routines */
3716 static char execpathname[MAX_PATH];
3717 int nt_SalvagePartition(char *partName, int jobn)
3722 if (!*execpathname) {
3723 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3724 if (!n || n == 1023)
3727 job.cj_magic = SALVAGER_MAGIC;
3728 job.cj_number = jobn;
3729 (void) strcpy(job.cj_part, partName);
3730 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3735 int nt_SetupPartitionSalvage(void *datap, int len)
3737 childJob_t *jobp = (childJob_t*)datap;
3738 char logname[AFSDIR_PATH_MAX];
3740 if (len != sizeof(childJob_t))
3742 if (jobp->cj_magic != SALVAGER_MAGIC)
3747 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3749 logFile = fopen(logname, "w");
3750 if (!logFile) logFile = stdout;
3756 #endif /* AFS_NT40_ENV */