2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #define SalvageVersion "2.4"
88 /* Main program file. Define globals. */
91 #include <afsconfig.h>
92 #include <afs/param.h>
100 #include <sys/stat.h>
105 #include <WINNT/afsevent.h>
107 #include <sys/param.h>
108 #include <sys/file.h>
110 #include <sys/time.h>
111 #endif /* ITIMER_REAL */
113 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
114 #define WCOREDUMP(x) (x & 0200)
117 #include <afs/afsint.h>
118 #include <afs/assert.h>
119 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
120 #if defined(AFS_VFSINCL_ENV)
121 #include <sys/vnode.h>
123 #include <sys/fs/ufs_inode.h>
125 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
126 #include <ufs/ufs/dinode.h>
127 #include <ufs/ffs/fs.h>
129 #include <ufs/inode.h>
132 #else /* AFS_VFSINCL_ENV */
134 #include <ufs/inode.h>
135 #else /* AFS_OSF_ENV */
136 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
137 #include <sys/inode.h>
140 #endif /* AFS_VFSINCL_ENV */
141 #endif /* AFS_SGI_ENV */
144 #include <sys/lockf.h>
148 #include <checklist.h>
150 #if defined(AFS_SGI_ENV)
155 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
158 #include <sys/mnttab.h>
159 #include <sys/mntent.h>
164 #endif /* AFS_SGI_ENV */
165 #endif /* AFS_HPUX_ENV */
170 #include <afs/osi_inode.h>
173 #include <afs/afsutil.h>
174 #include <afs/fileutil.h>
175 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
183 #include <afs/afssyscalls.h>
187 #include "partition.h"
189 #include "viceinode.h"
191 #include "volinodes.h" /* header magic number, etc. stuff */
197 extern void *calloc();
199 extern char *vol_DevName();
200 static char *TimeStamp(time_t clock, int precision);
202 #define ORPH_IGNORE 0
203 #define ORPH_REMOVE 1
204 #define ORPH_ATTACH 2
207 int debug; /* -d flag */
208 int Testing=0; /* -n flag */
209 int ListInodeOption; /* -i flag */
210 int ShowRootFiles; /* -r flag */
211 int RebuildDirs; /* -sal flag */
212 int Parallel = 4; /* -para X flag */
213 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
214 int forceR = 0; /* -b flag */
215 int ShowLog = 0; /* -showlog flag */
216 int ShowSuid = 0; /* -showsuid flag */
217 int ShowMounts = 0; /* -showmounts flag */
218 int orphans = ORPH_IGNORE; /* -orphans option */
222 int useSyslog = 0; /* -syslog flag */
223 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
226 #define MAXPARALLEL 32
228 int OKToZap; /* -o flag */
229 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
230 in the volume header */
232 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
234 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
236 Device fileSysDevice; /* The device number of the current
237 partition being salvaged */
241 char *fileSysPath; /* The path of the mounted partition currently
242 being salvaged, i.e. the directory
243 containing the volume headers */
245 char *fileSysPathName; /* NT needs this to make name pretty in log. */
246 IHandle_t *VGLinkH; /* Link handle for current volume group. */
247 int VGLinkH_cnt; /* # of references to lnk handle. */
248 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
250 char *fileSysDeviceName; /* The block device where the file system
251 being salvaged was mounted */
252 char *filesysfulldev;
254 int VolumeChanged; /* Set by any routine which would change the volume in
255 a way which would require callback is to be broken if the
256 volume was put back on line by an active file server */
258 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
260 struct InodeSummary { /* Inode summary file--an entry for each
261 volume in the inode file for a partition */
262 VolId volumeId; /* Volume id */
263 VolId RWvolumeId; /* RW volume associated */
264 int index; /* index into inode file (0, 1, 2 ...) */
265 int nInodes; /* Number of inodes for this volume */
266 int nSpecialInodes; /* Number of special inodes, i.e. volume
267 header, index, etc. These are all
268 marked (viceinode.h) and will all be sorted
269 to the beginning of the information for
270 this volume. Read-only volumes should
271 ONLY have special inodes (all the other
272 inodes look as if they belong to the
273 original RW volume). */
274 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
275 This is only useful for RW volumes and is used
276 to compute a new volume uniquifier in the event
277 that the header needs to be recreated. The inode
278 uniquifier may be a truncated version of vnode
279 uniquifier (AFS_3DISPARES). The real maxUniquifer
280 is from the vnodes and later calcuated from it */
281 struct VolumeSummary *volSummary;
282 /* Either a pointer to the original volume
283 header summary, or constructed summary
286 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
287 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
288 int inodeFd; /* File descriptor for inode file */
291 struct VolumeSummary { /* Volume summary an entry for each
292 volume in a volume directory.
293 Assumption: one volume directory per
295 char *fileName; /* File name on the partition for the volume
297 struct VolumeHeader header;
298 /* volume number, rw volume number, inode
299 numbers of each major component of
301 IHandle_t *volumeInfoHandle;
302 byte wouldNeedCallback; /* set if the file server should issue
303 call backs for all the files in this volume when
304 the volume goes back on line */
308 IHandle_t *handle; /* Inode containing this index */
309 int nVnodes; /* Total number of vnodes in index */
310 int nAllocatedVnodes; /* Total number actually used */
311 int volumeBlockCount; /* Total number of blocks used by volume */
312 Inode *inodes; /* Directory only */
313 struct VnodeEssence {
314 short count; /* Number of references to vnode; MUST BE SIGNED */
315 unsigned claimed:1; /* Set when a parent directory containing an entry
316 referencing this vnode is found. The claim
317 is that the parent in "parent" can point to
318 this vnode, and no other */
319 unsigned changed:1; /* Set if any parameters (other than the count)
320 in the vnode change. It is determined if the
321 link count has changed by noting whether it is
322 0 after scanning all directories */
323 unsigned salvaged:1;/* Set if this directory vnode has already been salvaged. */
324 unsigned todelete:1;/* Set if this vnode is to be deleted (should not be claimed) */
325 afs_uint32 blockCount;
326 /* Number of blocks (1K) used by this vnode,
328 VnodeId parent; /* parent in vnode */
329 Unique unique; /* Must match entry! */
330 char *name; /* Name of directory entry */
331 int modeBits; /* File mode bits */
332 Inode InodeNumber; /* file's inode */
333 int type; /* File type */
334 int author; /* File author */
335 int owner; /* File owner */
336 int group; /* File group */
338 } vnodeInfo[nVNODECLASSES];
341 struct DirHandle dirHandle;
344 unsigned haveDot, haveDotDot;
346 int copied; /* If the copy-on-write stuff has been applied */
354 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
355 int nVolumes; /* Number of volumes (read-write and read-only)
359 /* For NT, we can fork the per partition salvagers to gain the required
360 * safety against Aborts. But there's too many complex data structures at
361 * the per volume salvager layer to easilty copy the data across.
362 * childJobNumber is resset from -1 to the job number if this is a
363 * per partition child of the main salvager. This information is passed
364 * out-of-band in the extra data area setup for the now unused parent/child
367 #define SALVAGER_MAGIC 0x00BBaaDD
368 #define NOT_CHILD -1 /* job numbers start at 0 */
369 /* If new options need to be passed to child, add them here. */
376 /* Child job this process is running. */
377 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD};
379 int nt_SalvagePartition(char *partName, int jobn);
380 int nt_SetupPartitionSalvage(void *datap, int len);
383 struct InodeSummary *svgp_inodeSummaryp;
393 /* Forward declarations */
394 void Log(), Abort(), Exit();
396 int Wait(char *prog);
397 char * ToString(char *s);
398 void AskOffline(VolumeId volumeId);
399 void AskOnline(VolumeId volumeId, char *partition);
400 void CheckLogFile(void);
401 void ClearROInUseBit(struct VolumeSummary *summary);
402 void CopyAndSalvage(register struct DirSummary *dir);
403 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
404 void CopyOnWrite(register struct DirSummary *dir);
405 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
406 register struct InodeSummary * summary);
407 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
408 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
410 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
411 void GetVolumeSummary(VolumeId singleVolumeNumber);
412 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
414 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
417 void ObtainSalvageLock(void);
418 void PrintInodeList(void);
419 void PrintInodeSummary(void);
420 void PrintVolumeSummary(void);
421 int QuickCheck(register struct InodeSummary *isp, int nVols);
422 void RemoveTheForce(char *path);
423 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
424 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
426 void SalvageFileSysParallel(struct DiskPartition *partP);
427 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
428 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
429 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
430 int check, int *deleteMe);
431 int SalvageIndex(Inode ino, VnodeClass class, int RW,
432 register struct ViceInodeInfo *ip,
433 int nInodes, struct VolumeSummary *volSummary, int check);
434 int SalvageVnodes(register struct InodeSummary *rwIsp,
435 register struct InodeSummary * thisIsp,
436 register struct ViceInodeInfo * inodes, int check);
437 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
438 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
440 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
442 #define SalvageVolumeGroup DoSalvageVolumeGroup
444 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
445 register struct ViceInodeInfo *inodes,
446 int RW, int check, int *deleteMe);
448 int UseTheForceLuke(char *path);
450 static int IsVnodeOrphaned(VnodeId vnode);
452 /* Uniquifier stored in the Inode */
453 static Unique IUnique(Unique u)
456 return(u & 0x3fffff);
458 #if defined(AFS_SGI_EXMAG)
459 return(u & SGI_UNIQMASK);
462 #endif /* AFS_SGI_EXMAG */
466 static int BadError(register int aerror)
468 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
470 return 0; /* otherwise may be transient, e.g. EMFILE */
475 static int handleit(struct cmd_syndesc *as)
477 register struct cmd_item *ti;
478 char pname[100], *temp;
479 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0, i;
480 struct DiskPartition *partP;
482 #ifdef AFS_SGI_VNODE_GLUE
483 if (afs_init_kernel_config(-1) <0) {
484 printf("Can't determine NUMA configuration, not starting salvager.\n");
490 for (i = 0; i < CMD_MAXPARMS; i++) {
491 if (as->parms[i].items) {
497 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
506 #endif /* FAST_RESTART */
507 if (ti = as->parms[0].items) { /* -partition */
509 strncpy(pname, ti->data, 100);
511 if (ti = as->parms[1].items) { /* -volumeid */
513 printf("You must also specify '-partition' option with the '-volumeid' option\n");
517 vid = atoi(ti->data);
519 if (as->parms[2].items) /* -debug */
521 if (as->parms[3].items) /* -nowrite */
523 if (as->parms[4].items) /* -inodes */
525 if (as->parms[5].items) /* -force */
527 if (as->parms[6].items) /* -oktozap */
529 if (as->parms[7].items) /* -rootinodes */
531 if (as->parms[8].items) /* -RebuildDirs */
533 if (as->parms[9].items) /* -ForceReads */
535 if (ti = as->parms[10].items) {/* -Parallel # */
537 if (strncmp(temp,"all",3) == 0) {
541 if (strlen(temp) != 0) {
542 Parallel = atoi(temp);
543 if (Parallel < 1) Parallel = 1;
544 if (Parallel > MAXPARALLEL) {
545 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
546 Parallel = MAXPARALLEL;
550 if (ti = as->parms[11].items) {/* -tmpdir */
554 dirp = opendir(tmpdir);
556 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
561 if (ti = as->parms[12].items) /* -showlog */
563 if (ti = as->parms[13].items) { /* -log */
568 if (ti = as->parms[14].items) { /* -showmounts */
573 if (ti = as->parms[15].items) { /* -orphans */
575 orphans = ORPH_IGNORE;
576 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
577 orphans = ORPH_REMOVE;
578 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
579 orphans = ORPH_ATTACH;
582 #ifndef AFS_NT40_ENV /* ignore options on NT */
583 if ( ti = as->parms[16].items) { /* -syslog */
587 if ( ti = as->parms[17].items) { /* -syslogfacility */
588 useSyslogFacility = atoi(ti->data);
594 if (ti = as->parms[18].items) { /* -DontSalvage */
595 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
603 #endif /* FAST_RESTART */
605 /* Note: if seemvol we initialize this as a standard volume utility: this has the
606 implication that the file server may be running; negotations have to be made with
607 the file server in this case to take the read write volume and associated read-only
608 volumes off line before salvaging */
611 if (afs_winsockInit()<0) {
612 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
613 AFSDIR_SALVAGER_FILE, 0);
614 Log("Failed to initailize winsock, exiting.\n");
619 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
622 if (myjob.cj_number != NOT_CHILD) {
625 (void) strcpy(pname, myjob.cj_part);
630 for (partP = DiskPartitionList; partP; partP = partP->next) {
631 SalvageFileSysParallel(partP);
633 SalvageFileSysParallel(0);
636 partP = VGetPartition(pname, 0);
638 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
643 SalvageFileSys(partP, 0);
645 /* Salvage individual volume */
647 Log("salvage: invalid volume id specified; salvage aborted\n");
650 SalvageFileSys (partP, vid);
658 #include "AFS_component_version_number.c"
662 char *save_args[MAX_ARGS];
664 pthread_t main_thread;
667 int main(int argc, char **argv)
669 struct cmd_syndesc *ts;
671 char commandLine[150];
674 extern char cml_version_number[];
678 * The following signal action for AIX is necessary so that in case of a
679 * crash (i.e. core is generated) we can include the user's data section
680 * in the core dump. Unfortunately, by default, only a partial core is
681 * generated which, in many cases, isn't too useful.
683 struct sigaction nsa;
685 sigemptyset(&nsa.sa_mask);
686 nsa.sa_handler = SIG_DFL;
687 nsa.sa_flags = SA_FULLDUMP;
688 sigaction(SIGABRT, &nsa, NULL);
689 sigaction(SIGSEGV, &nsa, NULL);
692 /* Initialize directory paths */
693 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
695 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
697 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
701 main_thread = pthread_self();
702 if (spawnDatap && spawnDataLen) {
703 /* This is a child per partition salvager. Don't setup log or
704 * try to lock the salvager lock.
706 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
711 for (commandLine[0] = '\0', i=0; i<argc; i++) {
712 if (i > 0) strcat(commandLine, " ");
713 strcat(commandLine, argv[i]);
716 /* All entries to the log will be appended. Useful if there are
717 * multiple salvagers appending to the log.
722 #ifdef AFS_LINUX20_ENV
723 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
725 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
731 if (geteuid() != 0) {
732 printf("Salvager must be run as root.\n");
738 /* bad for normal help flag processing, but can do nada */
740 fprintf(logFile, "%s\n", cml_version_number);
741 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
743 /* Get and hold a lock for the duration of the salvage to make sure
744 * that no other salvage runs at the same time. The routine
745 * VInitVolumePackage (called below) makes sure that a file server or
746 * other volume utilities don't interfere with the salvage.
753 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
754 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
755 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
756 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
757 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
758 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
759 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
760 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
761 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
762 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
763 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
764 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
765 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
766 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
767 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
768 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
769 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
771 /* note - syslog isn't avail on NT, but if we make it conditional, have
772 to deal with screwy offsets for cmd params */
773 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
774 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
777 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");
778 #endif /* FAST_RESTART */
779 err = cmd_Dispatch(argc, argv);
783 /* Get the salvage lock if not already held. Hold until process exits. */
784 void ObtainSalvageLock(void)
789 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
790 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
792 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
794 "salvager: There appears to be another salvager running! Aborted.\n");
798 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
799 assert(salvageLock >= 0);
800 #ifdef AFS_DARWIN_ENV
801 if (flock(salvageLock, LOCK_EX) == -1) {
803 if (lockf(salvageLock, F_LOCK, 0) == -1) {
806 "salvager: There appears to be another salvager running! Aborted.\n");
813 #ifdef AFS_SGI_XFS_IOPS_ENV
814 /* Check if the given partition is mounted. For XFS, the root inode is not a
815 * constant. So we check the hard way.
817 int IsPartitionMounted(char *part)
820 struct mntent *mntent;
822 assert(mntfp = setmntent(MOUNTED, "r"));
823 while (mntent = getmntent(mntfp)) {
824 if (!strcmp(part, mntent->mnt_dir))
829 return mntent ? 1 : 1;
832 /* Check if the given inode is the root of the filesystem. */
833 #ifndef AFS_SGI_XFS_IOPS_ENV
834 int IsRootInode(struct stat *status)
837 * The root inode is not a fixed value in XFS partitions. So we need to
838 * see if the partition is in the list of mounted partitions. This only
839 * affects the SalvageFileSys path, so we check there.
841 return (status->st_ino == ROOTINODE);
846 #ifndef AFS_NAMEI_ENV
847 /* We don't want to salvage big files filesystems, since we can't put volumes on
850 int CheckIfBigFilesFS(char *mountPoint, char *devName)
852 struct superblock fs;
855 if (strncmp(devName, "/dev/", 5)) {
856 (void) sprintf(name, "/dev/%s", devName);
859 (void) strcpy(name, devName);
862 if (ReadSuper(&fs, name)<0) {
863 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
866 if (IsBigFilesFileSystem(&fs)) {
867 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
876 #define HDSTR "\\Device\\Harddisk"
877 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
878 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
885 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
887 if (strncmp(res, HDSTR, HDLEN)) {
890 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
891 res, HDSTR, p1->devName);
895 d1 = atoi(&res[HDLEN]);
897 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
899 if (strncmp(res, HDSTR, HDLEN)) {
902 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
903 res, HDSTR, p2->devName);
907 d2 = atoi(&res[HDLEN]);
912 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
915 /* This assumes that two partitions with the same device number divided by
916 * PartsPerDisk are on the same disk.
918 void SalvageFileSysParallel(struct DiskPartition *partP)
921 struct DiskPartition *partP;
922 int pid; /* Pid for this job */
923 int jobnumb; /* Log file job number */
924 struct job *nextjob; /* Next partition on disk to salvage */
926 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
927 struct job *thisjob = 0;
928 static int numjobs = 0;
929 static int jobcount = 0;
935 char logFileName[256];
939 /* We have a partition to salvage. Copy it into thisjob */
940 thisjob = (struct job *) malloc(sizeof(struct job));
942 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
945 memset(thisjob, 0, sizeof(struct job));
946 thisjob->partP = partP;
947 thisjob->jobnumb = jobcount;
950 else if (jobcount == 0) {
951 /* We are asking to wait for all jobs (partp == 0), yet we never
954 Log("No file system partitions named %s* found; not salvaged\n",
955 VICE_PARTITION_PREFIX);
959 if (debug || Parallel == 1) {
961 SalvageFileSys(thisjob->partP, 0);
968 /* Check to see if thisjob is for a disk that we are already
969 * salvaging. If it is, link it in as the next job to do. The
970 * jobs array has 1 entry per disk being salvages. numjobs is
971 * the total number of disks currently being salvaged. In
972 * order to keep thejobs array compact, when a disk is
973 * completed, the hightest element in the jobs array is moved
974 * down to now open slot.
976 for (j=0; j<numjobs; j++) {
977 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
978 /* On same disk, add it to this list and return */
979 thisjob->nextjob = jobs[j]->nextjob;
980 jobs[j]->nextjob = thisjob;
987 /* Loop until we start thisjob or until all existing jobs are finished */
988 while ( thisjob || (!partP && (numjobs > 0)) ) {
989 startjob = -1; /* No new job to start */
991 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
992 /* Either the max jobs are running or we have to wait for all
993 * the jobs to finish. In either case, we wait for at least one
994 * job to finish. When it's done, clean up after it.
996 pid = wait(&wstatus);
998 for (j=0; j<numjobs; j++) { /* Find which job it is */
999 if (pid == jobs[j]->pid) break;
1001 assert(j < numjobs);
1002 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1003 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1006 numjobs--; /* job no longer running */
1007 oldjob = jobs[j]; /* remember */
1008 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1009 free(oldjob); /* free the old job */
1011 /* If there is another partition on the disk to salvage, then
1012 * say we will start it (startjob). If not, then put thisjob there
1013 * and say we will start it.
1015 if (jobs[j]) { /* Another partitions to salvage */
1016 startjob = j; /* Will start it */
1017 } else { /* There is not another partition to salvage */
1019 jobs[j] = thisjob; /* Add thisjob */
1021 startjob = j; /* Will start it */
1023 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1024 startjob = -1; /* Don't start it - already running */
1028 /* We don't have to wait for a job to complete */
1030 jobs[numjobs] = thisjob; /* Add this job */
1032 startjob = numjobs; /* Will start it */
1036 /* Start up a new salvage job on a partition in job slot "startjob" */
1037 if (startjob != -1) {
1039 Log("Starting salvage of file system partition %s\n",
1040 jobs[startjob]->partP->name);
1042 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1043 * commands and pass the child job number via the data path.
1045 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1046 jobs[startjob]->jobnumb);
1047 jobs[startjob]->pid = pid;
1052 jobs[startjob]->pid = pid;
1058 for (fd =0; fd < 16; fd++) close(fd);
1059 open("/", 0); dup2(0, 1); dup2(0, 2);
1060 #ifndef AFS_NT40_ENV
1062 openlog(NULL, LOG_PID, useSyslogFacility);
1066 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1067 logFile = fopen(logFileName, "w");
1069 if (!logFile) logFile = stdout;
1071 SalvageFileSys1(jobs[startjob]->partP, 0);
1076 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1078 /* If waited for all jobs to complete, now collect log files and return */
1079 #ifndef AFS_NT40_ENV
1080 if ( ! useSyslog ) /* if syslogging - no need to collect */
1083 for (i=0; i<jobcount; i++) {
1084 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1085 if (passLog = fopen(logFileName, "r")) {
1086 while(fgets(buf, sizeof(buf), passLog)) {
1087 fputs(buf, logFile);
1091 (void)unlink(logFileName);
1099 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1101 if (!canfork || debug || Fork() == 0) {
1102 SalvageFileSys1(partP, singleVolumeNumber);
1103 if (canfork && !debug) {
1109 Wait("SalvageFileSys");
1112 char *get_DevName(char *pbuffer, char *wpath)
1114 char pbuf[128], *ptr;
1115 strcpy(pbuf, pbuffer);
1116 ptr = (char *)strrchr(pbuf, '/');
1119 strcpy(wpath, pbuf);
1122 ptr = (char *)strrchr(pbuffer, '/');
1124 strcpy(pbuffer, ptr+1);
1130 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1133 char inodeListPath[256];
1134 static char tmpDevName[100];
1135 static char wpath[100];
1136 struct VolumeSummary *vsp, *esp;
1139 fileSysPartition = partP;
1140 fileSysDevice = fileSysPartition->device;
1141 fileSysPathName = VPartitionPath(fileSysPartition);
1144 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1145 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1146 name = partP->devName;
1148 fileSysPath = fileSysPathName;
1149 strcpy(tmpDevName, partP->devName);
1150 name = get_DevName(tmpDevName, wpath);
1151 fileSysDeviceName = name;
1152 filesysfulldev = wpath;
1155 VLockPartition(partP->name);
1156 if (singleVolumeNumber || ForceSalvage)
1159 ForceSalvage = UseTheForceLuke(fileSysPath);
1161 if (singleVolumeNumber) {
1162 if (!VConnectFS()) {
1163 Abort("Couldn't connect to file server\n");
1165 AskOffline(singleVolumeNumber);
1168 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1170 Log("***Forced salvage of all volumes on this partition***\n");
1175 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1182 assert((dirp = opendir(fileSysPath)) != NULL);
1183 while (dp = readdir(dirp)) {
1184 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1185 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1187 Log("Removing old salvager temp files %s\n", dp->d_name);
1188 strcpy(npath, fileSysPath);
1190 strcat(npath, dp->d_name);
1196 tdir = (tmpdir ? tmpdir : fileSysPath);
1198 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1199 (void) strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
1201 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1203 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1204 unlink(inodeListPath);
1208 /* Using nt_unlink here since we're really using the delete on close
1209 * semantics of unlink. In most places in the salvager, we really do
1210 * mean to unlink the file at that point. Those places have been
1211 * modified to actually do that so that the NT crt can be used there.
1213 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1214 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1216 inodeFd = open(inodeListPath, O_RDONLY);
1217 unlink(inodeListPath);
1220 Abort("Temporary file %s is missing...\n",
1222 if (ListInodeOption) {
1226 /* enumerate volumes in the partition.
1227 figure out sets of read-only + rw volumes.
1228 salvage each set, read-only volumes first, then read-write.
1229 Fix up inodes on last volume in set (whether it is read-write
1232 GetVolumeSummary(singleVolumeNumber);
1234 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1235 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1236 for (j=i; j < nVolumesInInodeFile
1237 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1238 VolumeId vid = inodeSummary[j].volumeId;
1239 struct VolumeSummary *tsp;
1240 /* Scan volume list (from partition root directory) looking for the
1241 current rw volume number in the volume list from the inode scan.
1242 If there is one here that is not in the inode volume list,
1244 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1246 DeleteExtraVolumeHeaderFile(vsp);
1248 /* Now match up the volume summary info from the root directory with the
1249 entry in the volume list obtained from scanning inodes */
1250 inodeSummary[j].volSummary = NULL;
1251 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1252 if (tsp->header.id == vid) {
1253 inodeSummary[j].volSummary = tsp;
1259 /* Salvage the group of volumes (several read-only + 1 read/write)
1260 * starting with the current read-only volume we're looking at.
1262 SalvageVolumeGroup(&inodeSummary[i], j-i);
1265 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1266 for ( ; vsp<esp; vsp++) {
1268 DeleteExtraVolumeHeaderFile(vsp);
1271 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1272 RemoveTheForce(fileSysPath);
1274 if (!Testing && singleVolumeNumber) {
1275 AskOnline(singleVolumeNumber, fileSysPartition->name);
1277 /* Step through the volumeSummary list and set all volumes on-line.
1278 * The volumes were taken off-line in GetVolumeSummary.
1280 for (j=0; j<nVolumes; j++) {
1281 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1286 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1287 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1290 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1293 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1295 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1296 vsp->fileName, (Testing? "would have been ":""));
1298 unlink(vsp->fileName);
1302 CompareInodes(const void *_p1, const void *_p2)
1304 register const struct ViceInodeInfo *p1 = _p1;
1305 register const struct ViceInodeInfo *p2 = _p2;
1306 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1307 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1308 VolumeId p1rwid, p2rwid;
1309 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1310 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1311 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1312 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1313 if (p1rwid < p2rwid)
1315 if (p1rwid > p2rwid)
1317 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1318 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1319 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1320 return (p1->u.special.type < p2->u.special.type? -1: 1);
1321 if (p1->u.vnode.volumeId == p1rwid)
1323 if (p2->u.vnode.volumeId == p2rwid)
1325 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1327 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1328 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1329 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1331 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1333 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1335 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1337 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1339 /* The following tests are reversed, so that the most desirable
1340 of several similar inodes comes first */
1341 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1342 #ifdef AFS_3DISPARES
1343 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1344 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1347 #ifdef AFS_SGI_EXMAG
1348 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1349 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1354 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1355 #ifdef AFS_3DISPARES
1356 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1357 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1360 #ifdef AFS_SGI_EXMAG
1361 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1362 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1367 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1368 #ifdef AFS_3DISPARES
1369 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1370 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1373 #ifdef AFS_SGI_EXMAG
1374 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1375 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1380 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1381 #ifdef AFS_3DISPARES
1382 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1383 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1386 #ifdef AFS_SGI_EXMAG
1387 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1388 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1396 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1397 register struct InodeSummary * summary)
1399 int volume = ip->u.vnode.volumeId;
1400 int rwvolume = volume;
1401 register n, nSpecial;
1402 register Unique maxunique;
1405 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1407 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1409 rwvolume = ip->u.special.parentId;
1410 /* This isn't quite right, as there could (in error) be different
1411 parent inodes in different special vnodes */
1414 if (maxunique < ip->u.vnode.vnodeUniquifier)
1415 maxunique = ip->u.vnode.vnodeUniquifier;
1419 summary->volumeId = volume;
1420 summary->RWvolumeId = rwvolume;
1421 summary->nInodes =n;
1422 summary->nSpecialInodes = nSpecial;
1423 summary->maxUniquifier = maxunique;
1426 int OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber)
1428 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1429 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1430 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1435 * Collect list of inodes in file named by path. If a truly fatal error,
1436 * unlink the file and abort. For lessor errors, return -1. The file will
1437 * be unlinked by the caller.
1439 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1443 struct ViceInodeInfo *ip;
1444 struct InodeSummary summary;
1445 char summaryFileName[50];
1448 char *dev = fileSysPath;
1449 char *wpath = fileSysPath;
1451 char *dev = fileSysDeviceName;
1452 char *wpath = filesysfulldev;
1454 char *part = fileSysPath;
1457 /* This file used to come from vfsck; cobble it up ourselves now... */
1458 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1460 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1465 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1467 if (forceSal && !ForceSalvage) {
1468 Log("***Forced salvage of all volumes on this partition***\n");
1471 inodeFd = open(path, O_RDWR);
1472 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1474 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1476 tdir = (tmpdir ? tmpdir : part);
1478 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1479 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1481 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1483 summaryFile = fopen(summaryFileName, "a+");
1484 if (summaryFile == NULL) {
1487 Abort("Unable to create inode summary file\n");
1489 if (!canfork || debug || Fork() == 0) {
1491 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1493 fclose(summaryFile); close(inodeFd);
1494 unlink(summaryFileName);
1495 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1496 RemoveTheForce(fileSysPath);
1497 Log("%s vice inodes on %s; not salvaged\n",
1498 singleVolumeNumber? "No applicable": "No", dev);
1501 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1503 fclose(summaryFile); close(inodeFd);
1505 unlink(summaryFileName);
1506 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1508 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1509 fclose(summaryFile); close(inodeFd);
1511 unlink(summaryFileName);
1512 Abort("Unable to read inode table; %s not salvaged\n", dev);
1514 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1515 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1516 write(inodeFd, ip, status.st_size) != status.st_size) {
1517 fclose(summaryFile); close(inodeFd);
1519 unlink(summaryFileName);
1520 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1524 CountVolumeInodes(ip, nInodes, &summary);
1525 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1526 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1527 fclose(summaryFile); close(inodeFd);
1530 summary.index += (summary.nInodes);
1531 nInodes -= summary.nInodes;
1532 ip += summary.nInodes;
1534 /* Following fflush is not fclose, because if it was debug mode would not work */
1535 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1536 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1537 fclose(summaryFile); close(inodeFd);
1540 if (canfork && !debug) {
1546 if (Wait("Inode summary") == -1) {
1547 fclose(summaryFile); close(inodeFd);
1549 unlink(summaryFileName);
1550 Exit(1); /* salvage of this partition aborted */
1553 assert(fstat(fileno(summaryFile), &status) != -1);
1554 if ( status.st_size != 0 ) {
1556 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1557 assert(inodeSummary != NULL);
1558 /* For GNU we need to do lseek to get the file pointer moved. */
1559 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1560 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1561 assert(ret == status.st_size);
1563 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1564 fclose(summaryFile);
1566 unlink(summaryFileName);
1570 /* Comparison routine for volume sort.
1571 This is setup so that a read-write volume comes immediately before
1572 any read-only clones of that volume */
1573 int CompareVolumes(const void *_p1, const void *_p2)
1575 register const struct VolumeSummary *p1 = _p1;
1576 register const struct VolumeSummary *p2 = _p2;
1577 if (p1->header.parent != p2->header.parent)
1578 return p1->header.parent < p2->header.parent? -1: 1;
1579 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1581 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1583 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1586 void GetVolumeSummary(VolumeId singleVolumeNumber)
1589 afs_int32 nvols = 0;
1590 struct VolumeSummary *vsp, vs;
1591 struct VolumeDiskHeader diskHeader;
1594 /* Get headers from volume directory */
1595 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1596 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1597 if (!singleVolumeNumber) {
1598 while (dp = readdir(dirp)) {
1599 char *p = dp->d_name;
1600 p = strrchr(dp->d_name, '.');
1601 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1603 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1604 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1605 == sizeof (diskHeader) &&
1606 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1607 DiskToVolumeHeader(&vs.header, &diskHeader);
1614 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1618 if (!nvols) nvols = 1;
1619 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1621 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1622 assert(volumeSummaryp != NULL);
1625 vsp = volumeSummaryp;
1626 while (dp = readdir(dirp)) {
1627 char *p = dp->d_name;
1628 p = strrchr(dp->d_name, '.');
1629 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1632 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1633 || read(fd, &diskHeader, sizeof (diskHeader))
1634 != sizeof (diskHeader)
1635 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1640 if (!singleVolumeNumber) {
1641 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1642 dp->d_name, (Testing? "it would have been ":""));
1648 char nameShouldBe[64];
1649 DiskToVolumeHeader(&vsp->header, &diskHeader);
1650 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1651 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1654 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1655 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1656 if (singleVolumeNumber)
1657 AskOffline(vsp->header.id);
1658 if (strcmp(nameShouldBe, dp->d_name)) {
1659 if (!Showmode) Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", dp->d_name, (Testing ? "it would have been ":""));
1664 vsp->fileName = ToString(dp->d_name);
1674 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1677 /* Find the link table. This should be associated with the RW volume or, if
1678 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1680 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1681 struct ViceInodeInfo *allInodes)
1684 struct ViceInodeInfo *ip;
1686 for (i=0; i<nVols; i++) {
1687 ip = allInodes + isp[i].index;
1688 for (j=0; j<isp[i].nSpecialInodes; j++) {
1689 if (ip[j].u.special.type == VI_LINKTABLE)
1690 return ip[j].inodeNumber;
1696 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1698 struct versionStamp version;
1701 if (!VALID_INO(ino))
1702 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1703 isp->volumeId, INODESPECIAL,
1704 VI_LINKTABLE, isp->RWvolumeId);
1705 if (!VALID_INO(ino))
1706 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1707 isp->RWvolumeId, errno);
1708 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1709 fdP = IH_OPEN(VGLinkH);
1711 Abort("Can't open link table for volume %u (error = %d)\n",
1712 isp->RWvolumeId, errno);
1714 if (FDH_TRUNC(fdP, 0)<0)
1715 Abort("Can't truncate link table for volume %u (error = %d)\n",
1716 isp->RWvolumeId, errno);
1718 version.magic = LINKTABLEMAGIC;
1719 version.version = LINKTABLEVERSION;
1721 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1723 Abort("Can't truncate link table for volume %u (error = %d)\n",
1724 isp->RWvolumeId, errno);
1726 FDH_REALLYCLOSE(fdP);
1728 /* If the volume summary exits (i.e., the V*.vol header file exists),
1729 * then set this inode there as well.
1731 if (isp->volSummary)
1732 isp->volSummary->header.linkTable = ino;
1738 void *nt_SVG(void *arg)
1740 SVGParms_t *parms = (SVGParms_t*)arg;
1741 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1745 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1748 pthread_attr_t tattr;
1752 /* Initialize per volume global variables, even if later code does so */
1756 memset(&VolInfo, 0, sizeof(VolInfo));
1758 parms.svgp_inodeSummaryp = isp;
1759 parms.svgp_count = nVols;
1760 code = pthread_attr_init(&tattr);
1762 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1766 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1768 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1772 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1774 Log("Failed to create thread to salvage volume group %u\n",
1778 (void) pthread_join(tid, NULL);
1780 #endif /* AFS_NT40_ENV */
1782 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1784 struct ViceInodeInfo *inodes,*allInodes,*ip;
1785 int i, totalInodes, size, salvageTo;
1789 int dec_VGLinkH = 0;
1791 FdHandle_t *fdP = NULL;
1794 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1795 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1796 if (!ForceSalvage && QuickCheck(isp, nVols))
1799 if (ShowMounts && !haveRWvolume)
1801 if (canfork && !debug && Fork() != 0) {
1802 (void) Wait("Salvage volume group");
1805 for (i = 0, totalInodes = 0; i<nVols; i++)
1806 totalInodes += isp[i].nInodes;
1807 size = totalInodes * sizeof (struct ViceInodeInfo);
1808 inodes = (struct ViceInodeInfo *) malloc(size);
1809 allInodes = inodes - isp->index; /* this would the base of all the inodes
1810 for the partition, if all the inodes
1811 had been read into memory */
1812 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1);
1813 assert(read(inodeFd,inodes,size) == size);
1815 /* Don't try to salvage a read write volume if there isn't one on this
1817 salvageTo = haveRWvolume? 0:1;
1819 #ifdef AFS_NAMEI_ENV
1820 ino = FindLinkHandle(isp, nVols, allInodes);
1821 if (VALID_INO(ino)) {
1822 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1823 fdP = IH_OPEN(VGLinkH);
1825 if (!VALID_INO(ino) || fdP == NULL) {
1826 Log("%s link table for volume %u.\n",
1827 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1829 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1832 CreateLinkTable(isp, ino);
1836 FDH_REALLYCLOSE(fdP);
1838 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1841 /* Salvage in reverse order--read/write volume last; this way any
1842 Inodes not referenced by the time we salvage the read/write volume
1843 can be picked up by the read/write volume */
1844 /* ACTUALLY, that's not done right now--the inodes just vanish */
1845 for (i = nVols-1; i>=salvageTo; i--) {
1847 struct InodeSummary *lisp = &isp[i];
1848 #ifdef AFS_NAMEI_ENV
1849 /* If only the RO is present on this partition, the link table
1850 * shows up as a RW volume special file. Need to make sure the
1851 * salvager doesn't try to salvage the non-existent RW.
1853 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1854 /* If this only special inode is the link table, continue */
1855 if (inodes->u.special.type == VI_LINKTABLE) {
1861 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1862 lisp->volumeId, (Testing?"(READONLY mode)":""));
1863 /* Check inodes twice. The second time do things seriously. This
1864 way the whole RO volume can be deleted, below, if anything goes wrong */
1865 for (check = 1; check>=0; check--) {
1867 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1868 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1869 if (rw && deleteMe) {
1870 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1871 volume won't be called */
1877 if (rw && check == 1)
1879 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1880 MaybeZapVolume(lisp,"Vnode index", 0, check);
1886 /* Fix actual inode counts */
1888 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1889 static int TraceBadLinkCounts = 0;
1890 #ifdef AFS_NAMEI_ENV
1891 if (VGLinkH->ih_ino == ip->inodeNumber) {
1892 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1893 VGLinkH_p1 = ip->u.param[0];
1894 continue; /* Deal with this last. */
1897 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1898 TraceBadLinkCounts--; /* Limit reports, per volume */
1899 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1900 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1901 ip->byteCount, ip->u.param[0], ip->u.param[1],
1902 ip->u.param[2], ip->u.param[3]);
1904 while (ip->linkCount > 0) {
1905 /* below used to assert, not break */
1907 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1908 Log ("idec failed. inode %s errno %d\n",
1909 PrintInode(NULL, ip->inodeNumber), errno);
1915 while (ip->linkCount < 0) {
1916 /* these used to be asserts */
1918 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1919 Log ("iinc failed. inode %s errno %d\n",
1920 PrintInode(NULL, ip->inodeNumber) ,errno);
1927 #ifdef AFS_NAMEI_ENV
1928 while (dec_VGLinkH > 0) {
1929 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1930 Log("idec failed on link table, errno = %d\n", errno);
1934 while (dec_VGLinkH < 0) {
1935 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1936 Log("iinc failed on link table, errno = %d\n", errno);
1943 /* Directory consistency checks on the rw volume */
1945 SalvageVolume(isp, VGLinkH);
1946 IH_RELEASE(VGLinkH);
1948 if (canfork && !debug) {
1954 int QuickCheck(register struct InodeSummary *isp, int nVols)
1956 /* Check headers BEFORE forking */
1960 for (i = 0; i<nVols; i++) {
1961 struct VolumeSummary *vs = isp[i].volSummary;
1962 VolumeDiskData volHeader;
1964 /* Don't salvage just because phantom rw volume is there... */
1965 /* (If a read-only volume exists, read/write inodes must also exist) */
1966 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1970 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1971 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1972 == sizeof(volHeader)
1973 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1974 && volHeader.dontSalvage == DONT_SALVAGE
1975 && volHeader.needsSalvaged == 0
1976 && volHeader.destroyMe == 0) {
1977 if (volHeader.inUse == 1) {
1978 volHeader.inUse = 0;
1979 volHeader.inService = 1;
1981 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1982 != sizeof(volHeader)) {
1999 /* SalvageVolumeHeaderFile
2001 * Salvage the top level V*.vol header file. Make sure the special files
2002 * exist and that there are no duplicates.
2004 * Calls SalvageHeader for each possible type of volume special file.
2007 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2008 register struct ViceInodeInfo *inodes,
2009 int RW, int check, int *deleteMe)
2013 register struct ViceInodeInfo *ip;
2014 int allinodesobsolete = 1;
2015 struct VolumeDiskHeader diskHeader;
2019 memset(&tempHeader, 0, sizeof(tempHeader));
2020 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2021 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2022 tempHeader.id = isp->volumeId;
2023 tempHeader.parent = isp->RWvolumeId;
2024 /* Check for duplicates (inodes are sorted by type field) */
2025 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2026 ip = &inodes[isp->index+i];
2027 if (ip->u.special.type == (ip+1)->u.special.type) {
2028 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2032 for (i = 0; i<isp->nSpecialInodes; i++) {
2033 ip = &inodes[isp->index+i];
2034 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2036 Log("Rubbish header inode\n");
2039 Log("Rubbish header inode; deleted\n");
2041 else if (!stuff[ip->u.special.type-1].obsolete) {
2042 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2043 if (!check && ip->u.special.type != VI_LINKTABLE)
2044 ip->linkCount--; /* Keep the inode around */
2045 allinodesobsolete = 0;
2049 if (allinodesobsolete) {
2056 VGLinkH_cnt ++; /* one for every header. */
2058 if (!RW && !check && isp->volSummary) {
2059 ClearROInUseBit(isp->volSummary);
2063 for (i = 0; i< MAXINODETYPE; i++) {
2064 if (stuff[i].inodeType == VI_LINKTABLE) {
2065 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2066 * And we may have recreated the link table earlier, so set the
2067 * RW header as well.
2069 if (VALID_INO(VGLinkH->ih_ino)) {
2070 *stuff[i].inode = VGLinkH->ih_ino;
2074 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2078 if (isp->volSummary == NULL) {
2080 sprintf(name, VFORMAT, isp->volumeId);
2082 Log("No header file for volume %u\n", isp->volumeId);
2085 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2086 isp->volumeId, (Testing?"it would have been ":""),
2087 fileSysPathName, name);
2088 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2089 assert(headerFd != -1);
2090 isp->volSummary = (struct VolumeSummary *)
2091 malloc(sizeof(struct VolumeSummary));
2092 isp->volSummary->fileName = ToString(name);
2096 /* hack: these two fields are obsolete... */
2097 isp->volSummary->header.volumeAcl = 0;
2098 isp->volSummary->header.volumeMountTable = 0;
2100 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2101 /* We often remove the name before calling us, so we make a fake one up */
2102 if (isp->volSummary->fileName) {
2103 strcpy(name, isp->volSummary->fileName);
2105 sprintf(name, VFORMAT, isp->volumeId);
2106 isp->volSummary->fileName = ToString(name);
2109 Log("Header file %s is damaged or no longer valid%s\n",
2110 name, (check ? "" : "; repairing"));
2114 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2115 assert(headerFd != -1);
2119 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2121 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2123 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2124 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2125 != sizeof(struct VolumeDiskHeader)) {
2126 Log("Couldn't rewrite volume header file!\n");
2133 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2134 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2138 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2139 int check, int *deleteMe)
2142 VolumeDiskData volumeInfo;
2143 struct versionStamp fileHeader;
2152 #ifndef AFS_NAMEI_ENV
2153 if ( sp->inodeType == VI_LINKTABLE)
2156 if (*(sp->inode) == 0) {
2158 Log("Missing inode in volume header (%s)\n", sp->description);
2161 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2162 sp->description, (Testing ? "it would have recreated it": "recreating"));
2164 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2165 isp->volumeId, INODESPECIAL,
2166 sp->inodeType, isp->RWvolumeId);
2167 if (!VALID_INO(*(sp->inode)))
2168 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2169 sp->description, errno);
2174 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2175 fdP = IH_OPEN(specH);
2176 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2177 /* bail out early and destroy the volume */
2178 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2180 if (deleteMe) *deleteMe = 1;
2185 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2186 sp->description, errno);
2189 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2190 || header.fileHeader.magic != sp->stamp.magic)) {
2192 Log("Part of the header (%s) is corrupted\n", sp->description);
2193 FDH_REALLYCLOSE(fdP);
2197 Log("Part of the header (%s) is corrupted; recreating\n",
2201 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2204 FDH_REALLYCLOSE(fdP);
2208 if (recreate && !Testing) {
2210 Abort("Internal error: recreating volume header (%s) in check mode\n",
2212 code = FDH_TRUNC(fdP, 0);
2214 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2215 sp->description, errno);
2217 /* The following code should be moved into vutil.c */
2218 if (sp->inodeType == VI_VOLINFO) {
2220 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2221 header.volumeInfo.stamp = sp->stamp;
2222 header.volumeInfo.id = isp->volumeId;
2223 header.volumeInfo.parentId = isp->RWvolumeId;
2224 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2225 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2226 header.volumeInfo.inService = 0;
2227 header.volumeInfo.blessed = 0;
2228 /* The + 1000 is a hack in case there are any files out in venus caches */
2229 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2230 header.volumeInfo.type =
2231 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2232 header.volumeInfo.needsCallback = 0;
2233 gettimeofday(&tp,0);
2234 header.volumeInfo.creationDate = tp.tv_sec;
2235 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2236 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2238 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2239 sizeof(header.volumeInfo));
2240 if (code != sizeof(header.volumeInfo)) {
2242 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2243 sp->description, errno);
2244 Abort("Unable to write entire volume header file (%s)\n",
2249 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2250 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2252 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2253 if (code != sizeof(sp->stamp)) {
2255 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2256 sp->description, errno);
2257 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2262 FDH_REALLYCLOSE(fdP);
2264 if (sp->inodeType == VI_VOLINFO) {
2265 VolInfo = header.volumeInfo;
2268 if (VolInfo.updateDate) {
2269 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2270 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2271 (Testing?"it would have been ":""), update);
2273 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2274 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2283 int SalvageVnodes(register struct InodeSummary *rwIsp,
2284 register struct InodeSummary * thisIsp,
2285 register struct ViceInodeInfo * inodes, int check)
2287 int ilarge, ismall, ioffset, RW, nInodes;
2288 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2289 if (Showmode) return 0;
2290 RW = (rwIsp == thisIsp);
2291 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2292 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2293 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2294 if (check && ismall == -1)
2296 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2297 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2298 return (ilarge==0 && ismall==0 ? 0: -1);
2301 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2302 register struct ViceInodeInfo *ip,
2303 int nInodes, struct VolumeSummary *volSummary, int check)
2305 VolumeId volumeNumber;
2306 char buf[SIZEOF_LARGEDISKVNODE];
2307 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2309 StreamHandle_t *file;
2310 struct VnodeClassInfo *vcp;
2312 int vnodeIndex, nVnodes;
2313 afs_ino_str_t stmp1, stmp2;
2317 volumeNumber = volSummary->header.id;
2318 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2319 fdP = IH_OPEN(handle);
2320 assert(fdP != NULL);
2321 file = FDH_FDOPEN(fdP, "r+");
2322 assert(file != NULL);
2323 vcp = &VnodeClassInfo[class];
2324 size = OS_SIZE(fdP->fd_fd);
2326 nVnodes = (size / vcp->diskSize) - 1;
2328 assert((nVnodes+1) * vcp->diskSize == size);
2329 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2334 for (vnodeIndex = 0;
2335 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2336 nVnodes--, vnodeIndex++) {
2337 if (vnode->type != vNull) {
2338 int vnodeChanged = 0;
2339 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2340 /* Log programs that belong to root (potentially suid root);
2341 don't bother for read-only or backup volumes */
2342 #ifdef notdef /* This is done elsewhere */
2343 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2344 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2345 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2346 vnode->owner, vnode->modeBits);
2348 if (VNDISK_GET_INO(vnode) == 0) {
2350 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2351 memset(vnode, 0, vcp->diskSize);
2356 if (vcp->magic != vnode->vnodeMagic) {
2357 /* bad magic #, probably partially created vnode */
2358 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2359 memset(vnode, 0, vcp->diskSize);
2363 /* ****** Should do a bit more salvage here: e.g. make sure
2364 vnode type matches what it should be given the index */
2365 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2366 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2367 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2368 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2375 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2376 /* The following doesn't work, because the version number
2377 is not maintained correctly by the file server */
2378 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2379 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2381 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2388 /* For RW volume, look for vnode with matching inode number;
2389 if no such match, take the first determined by our sort
2391 register struct ViceInodeInfo *lip = ip;
2392 register lnInodes = nInodes;
2393 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2394 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2403 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2404 /* "Matching" inode */
2408 vu = vnode->uniquifier;
2409 iu = ip->u.vnode.vnodeUniquifier;
2410 vd = vnode->dataVersion;
2411 id = ip->u.vnode.inodeDataVersion;
2413 * Because of the possibility of the uniquifier overflows (> 4M)
2414 * we compare them modulo the low 22-bits; we shouldn't worry
2415 * about mismatching since they shouldn't to many old
2416 * uniquifiers of the same vnode...
2418 if (IUnique(vu) != IUnique(iu)) {
2420 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2421 vnodeNumber, IUnique(vu), IUnique(iu));
2424 vnode->uniquifier = iu;
2425 #ifdef AFS_3DISPARES
2426 vnode->dataVersion = (id >= vd ?
2427 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2428 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2430 #if defined(AFS_SGI_EXMAG)
2431 vnode->dataVersion = (id >= vd ?
2432 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2433 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2435 vnode->dataVersion = (id>vd ? id:vd);
2436 #endif /* AFS_SGI_EXMAG */
2437 #endif /* AFS_3DISPARES */
2441 /* don't bother checking for vd > id any more, since
2442 partial file transfers always result in this state,
2443 and you can't do much else anyway (you've already
2444 found the best data you can) */
2445 #ifdef AFS_3DISPARES
2446 if (!vnodeIsDirectory(vnodeNumber) &&
2447 ((vd < id && (id-vd) < 1887437) ||
2448 ((vd > id && (vd-id) > 1887437)))) {
2450 #if defined(AFS_SGI_EXMAG)
2451 if (!vnodeIsDirectory(vnodeNumber) &&
2452 ((vd < id && (id-vd) < 15099494) ||
2453 ((vd > id && (vd-id) > 15099494)))) {
2455 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2456 #endif /* AFS_SGI_EXMAG */
2458 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2459 vnode->dataVersion = id;
2464 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2467 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2469 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2470 PrintInode(stmp2, ip->inodeNumber),
2473 VNDISK_SET_INO(vnode, ip->inodeNumber);
2478 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2480 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2481 PrintInode(stmp2, ip->inodeNumber),
2484 VNDISK_SET_INO(vnode, ip->inodeNumber);
2487 if (ip->byteCount != vnode->length) {
2489 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2490 vnodeNumber, vnode->length, ip->byteCount);
2494 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2495 vnodeNumber, vnode->length, ip->byteCount);
2496 vnode->length = ip->byteCount;
2500 ip->linkCount--; /* Keep the inode around */
2504 else { /* no matching inode */
2505 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2506 /* No matching inode--get rid of the vnode */
2508 if (VNDISK_GET_INO(vnode)) {
2510 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2511 vnodeNumber, vnode->uniquifier,
2512 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2515 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2520 if (VNDISK_GET_INO(vnode)) {
2522 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2523 vnodeNumber, vnode->uniquifier,
2524 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2525 ctime((time_t *)&(vnode->serverModifyTime)));
2528 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime((time_t *)&(vnode->serverModifyTime)));
2530 memset(vnode, 0, vcp->diskSize);
2533 /* Should not reach here becuase we checked for
2534 * (inodeNumber == 0) above. And where we zero the vnode,
2535 * we also goto vnodeDone.
2539 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2543 } /* VNDISK_GET_INO(vnode) != 0 */
2545 assert(!(vnodeChanged && check));
2546 if (vnodeChanged && !Testing) {
2547 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2548 (char*)vnode, vcp->diskSize)
2550 VolumeChanged = 1; /* For break call back */
2561 struct VnodeEssence *CheckVnodeNumber(VnodeId vnodeNumber)
2564 struct VnodeInfo *vip;
2567 class = vnodeIdToClass(vnodeNumber);
2568 vip = &vnodeInfo[class];
2569 offset = vnodeIdToBitNumber(vnodeNumber);
2570 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2573 void CopyOnWrite(register struct DirSummary *dir)
2575 /* Copy the directory unconditionally if we are going to change it:
2576 * not just if was cloned.
2578 struct VnodeDiskObject vnode;
2579 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2580 Inode oldinode, newinode;
2583 if (dir->copied || Testing)
2585 DFlush(); /* Well justified paranoia... */
2587 code = IH_IREAD(vnodeInfo[vLarge].handle,
2588 vnodeIndexOffset(vcp, dir->vnodeNumber),
2589 (char*)&vnode, sizeof (vnode));
2590 assert(code == sizeof(vnode));
2591 oldinode = VNDISK_GET_INO(&vnode);
2592 /* Increment the version number by a whole lot to avoid problems with
2593 * clients that were promised new version numbers--but the file server
2594 * crashed before the versions were written to disk.
2596 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2597 dir->rwVid, dir->vnodeNumber,
2598 vnode.uniquifier, vnode.dataVersion += 200);
2599 assert(VALID_INO(newinode));
2600 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2602 VNDISK_SET_INO(&vnode, newinode);
2603 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2604 vnodeIndexOffset(vcp, dir->vnodeNumber),
2605 (char*)&vnode, sizeof (vnode));
2606 assert(code == sizeof (vnode));
2608 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2609 fileSysDevice, newinode);
2610 /* Don't delete the original inode right away, because the directory is
2611 * still being scanned.
2617 * This function should either successfully create a new dir, or give up
2618 * and leave things the way they were. In particular, if it fails to write
2619 * the new dir properly, it should return w/o changing the reference to the
2622 void CopyAndSalvage(register struct DirSummary *dir)
2624 struct VnodeDiskObject vnode;
2625 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2626 Inode oldinode, newinode;
2628 register afs_int32 code;
2629 afs_int32 parentUnique= 1;
2630 struct VnodeEssence *vnodeEssence;
2634 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2635 code = IH_IREAD(vnodeInfo[vLarge].handle,
2636 vnodeIndexOffset(vcp, dir->vnodeNumber),
2637 (char*)&vnode, sizeof (vnode));
2638 assert(code == sizeof (vnode));
2639 oldinode = VNDISK_GET_INO(&vnode);
2640 /* Increment the version number by a whole lot to avoid problems with
2641 * clients that were promised new version numbers--but the file server
2642 * crashed before the versions were written to disk.
2644 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2645 dir->rwVid, dir->vnodeNumber,
2647 vnode.dataVersion += 200);
2648 assert(VALID_INO(newinode));
2649 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2651 /* Assign . and .. vnode numbers from dir and vnode.parent.
2652 * The uniquifier for . is in the vnode.
2653 * The uniquifier for .. might be set to a bogus value of 1 and
2654 * the salvager will later clean it up.
2656 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2657 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2659 code = DirSalvage(&dir->dirHandle, &newdir,
2660 dir->vnodeNumber, vnode.uniquifier,
2661 (vnode.parent?vnode.parent:dir->vnodeNumber),
2663 if (code == 0) code = DFlush();
2665 /* didn't really build the new directory properly, let's just give up. */
2666 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2668 Log("Directory salvage returned code %d, continuing.\n", code);
2671 Log("Checking the results of the directory salvage...\n");
2672 if (!DirOK(&newdir)) {
2673 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2674 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2679 VNDISK_SET_INO(&vnode, newinode);
2680 vnode.length = Length(&newdir);
2681 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2682 vnodeIndexOffset(vcp, dir->vnodeNumber),
2683 (char*)&vnode, sizeof (vnode));
2684 assert(code == sizeof (vnode));
2686 nt_sync(fileSysDevice);
2688 sync(); /* this is slow, but hopefully rarely called. We don't have
2689 * an open FD on the file itself to fsync.
2692 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2694 dir->dirHandle = newdir;
2697 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2700 struct VnodeEssence *vnodeEssence;
2701 afs_int32 dirOrphaned, todelete;
2703 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2705 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2706 if (vnodeEssence == NULL) {
2708 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2709 dir->vnodeNumber, dir->name?dir->name:"??",
2710 name, vnodeNumber, unique);
2714 assert(Delete(&dir->dirHandle, name) == 0);
2720 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2721 * mount inode for the partition. If this inode were deleted, it would crash
2724 if (vnodeEssence->InodeNumber == 0) {
2725 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2726 dir->vnodeNumber, (dir->name?dir->name:"??"),
2727 name, vnodeNumber, unique,
2728 (Testing?"-- would have deleted":" -- deleted"));
2731 assert(Delete(&dir->dirHandle, name) == 0);
2737 if (!(vnodeNumber & 1) && !Showmode &&
2738 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2739 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2740 dir->vnodeNumber, (dir->name?dir->name:"??"),
2741 name, vnodeNumber, unique,
2742 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2746 assert(Delete(&dir->dirHandle, name) == 0);
2752 /* Check if the Uniquifiers match. If not, change the directory entry
2753 * so its unique matches the vnode unique. Delete if the unique is zero
2754 * or if the directory is orphaned.
2756 if (!vnodeEssence->unique ||
2757 (vnodeEssence->unique) != unique) {
2758 if (!vnodeEssence->unique &&
2759 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2760 /* This is an orphaned directory. Don't delete the . or ..
2761 * entry. Otherwise, it will get created in the next
2762 * salvage and deleted again here. So Just skip it.
2767 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2770 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2771 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2772 name, vnodeNumber, unique, vnodeEssence->unique,
2773 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2777 fid.Vnode = vnodeNumber;
2778 fid.Unique = vnodeEssence->unique;
2780 assert(Delete(&dir->dirHandle, name) == 0);
2782 assert(Create(&dir->dirHandle, name, &fid) == 0);
2784 if (todelete) return; /* no need to continue */
2787 if (strcmp(name,".") == 0) {
2788 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2790 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2791 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2794 assert(Delete(&dir->dirHandle, ".") == 0);
2795 fid.Vnode = dir->vnodeNumber;
2796 fid.Unique = dir->unique;
2797 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2800 vnodeNumber = fid.Vnode; /* Get the new Essence */
2801 unique = fid.Unique;
2802 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2806 else if (strcmp(name,"..") == 0) {
2809 struct VnodeEssence *dotdot;
2810 pa.Vnode = dir->parent;
2811 dotdot = CheckVnodeNumber(pa.Vnode);
2812 assert (dotdot != NULL); /* XXX Should not be assert */
2813 pa.Unique = dotdot->unique;
2816 pa.Vnode = dir->vnodeNumber;
2817 pa.Unique = dir->unique;
2819 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2820 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2821 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2824 assert(Delete(&dir->dirHandle, "..") == 0);
2825 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2828 vnodeNumber = pa.Vnode; /* Get the new Essence */
2830 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2832 dir->haveDotDot = 1;
2833 } else if (strncmp(name,".__afs",6) == 0) {
2835 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2836 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2840 assert(Delete(&dir->dirHandle, name) == 0);
2842 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2843 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2847 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2848 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2849 vnodeEssence->owner, vnodeEssence->group,
2850 vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber,
2852 if (ShowMounts && (vnodeEssence->type == vSymlink) &&
2853 !(vnodeEssence->modeBits & 0111)) {
2859 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2860 vnodeEssence->InodeNumber);
2862 assert(fdP != NULL);
2863 size = FDH_SIZE(fdP);
2865 memset(buf, 0, 1024);
2866 if (size > 1024) size = 1024;
2867 code = FDH_READ(fdP, buf, size);
2868 assert(code == size);
2869 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2870 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2871 dir->name?dir->name:"??", name, buf);
2872 FDH_REALLYCLOSE(fdP);
2875 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2876 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2877 vnodeEssence->owner, vnodeEssence->group,
2878 vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber,
2880 if (vnodeIdToClass(vnodeNumber) == vLarge &&
2881 vnodeEssence->name == NULL) {
2883 if (n = (char*)malloc(strlen(name)+1))
2885 vnodeEssence->name = n;
2888 /* The directory entry points to the vnode. Check to see if the
2889 * vnode points back to the directory. If not, then let the
2890 * directory claim it (else it might end up orphaned). Vnodes
2891 * already claimed by another directory are deleted from this
2892 * directory: hardlinks to the same vnode are not allowed
2893 * from different directories.
2895 if (vnodeEssence->parent != dir->vnodeNumber) {
2896 if (!vnodeEssence->claimed && !dirOrphaned) {
2897 /* Vnode does not point back to this directory.
2898 * Orphaned dirs cannot claim a file (it may belong to
2899 * another non-orphaned dir).
2902 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2903 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2904 vnodeNumber, unique, (Testing?"would have been ":""),
2905 vnodeEssence->parent, dir->vnodeNumber);
2907 vnodeEssence->parent = dir->vnodeNumber;
2908 vnodeEssence->changed = 1;
2910 /* Vnode was claimed by another directory */
2913 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2914 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2915 vnodeEssence->parent, vnodeNumber, unique,
2916 (Testing?"would have been ":""));
2918 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2919 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2920 vnodeEssence->parent, vnodeNumber, unique,
2921 (Testing?"would have been ":""));
2926 assert(Delete(&dir->dirHandle, name) == 0);
2931 /* This directory claims the vnode */
2932 vnodeEssence->claimed = 1;
2934 vnodeEssence->count--;
2937 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2940 register struct VnodeInfo *vip = &vnodeInfo[class];
2941 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2942 char buf[SIZEOF_LARGEDISKVNODE];
2943 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2945 StreamHandle_t *file;
2950 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2951 fdP = IH_OPEN(vip->handle);
2952 assert(fdP != NULL);
2953 file = FDH_FDOPEN(fdP, "r+");
2954 assert(file != NULL);
2955 size = OS_SIZE(fdP->fd_fd);
2957 vip->nVnodes = (size / vcp->diskSize) - 1;
2958 if (vip->nVnodes > 0) {
2959 assert((vip->nVnodes+1)*vcp->diskSize == size);
2960 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2961 assert((vip->vnodes = (struct VnodeEssence *)
2962 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2963 if (class == vLarge) {
2964 assert((vip->inodes = (Inode *)
2965 calloc(vip->nVnodes, sizeof (Inode))) != NULL);
2976 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2977 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2978 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2979 nVnodes--, vnodeIndex++) {
2980 if (vnode->type != vNull) {
2981 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2982 vip->nAllocatedVnodes++;
2983 vep->count = vnode->linkCount;
2984 vep->blockCount = nBlocks(vnode->length);
2985 vip->volumeBlockCount += vep->blockCount;
2986 vep->parent = vnode->parent;
2987 vep->unique = vnode->uniquifier;
2988 if (*maxu < vnode->uniquifier)
2989 *maxu = vnode->uniquifier;
2990 vep->modeBits = vnode->modeBits;
2991 vep->InodeNumber = VNDISK_GET_INO(vnode);
2992 vep->type = vnode->type;
2993 vep->author = vnode->author;
2994 vep->owner = vnode->owner;
2995 vep->group = vnode->group;
2996 if (vnode->type == vDirectory) {
2997 assert(class == vLarge);
2998 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3006 static char *GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3008 struct VnodeEssence *parentvp;
3014 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3016 strcat(path, vp->name);
3022 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3023 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3025 static int IsVnodeOrphaned(VnodeId vnode)
3027 struct VnodeEssence *vep;
3029 if (vnode == 0) return(1); /* Vnode zero does not exist */
3030 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3031 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3032 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3034 return( IsVnodeOrphaned(vep->parent) );
3037 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3038 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3041 static struct DirSummary dir;
3042 static struct DirHandle dirHandle;
3043 struct VnodeEssence *parent;
3044 static char path[MAXPATHLEN];
3047 if (dirVnodeInfo->vnodes[i].salvaged)
3048 return; /* already salvaged */
3051 dirVnodeInfo->vnodes[i].salvaged = 1;
3053 if (dirVnodeInfo->inodes[i] == 0)
3054 return; /* Not allocated to a directory */
3056 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3057 if (parent && parent->salvaged == 0)
3058 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3059 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3060 rootdir, rootdirfound);
3061 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3062 dir.unique = dirVnodeInfo->vnodes[i].unique;
3065 dir.parent = dirVnodeInfo->vnodes[i].parent;
3066 dir.haveDot = dir.haveDotDot = 0;
3067 dir.ds_linkH = alinkH;
3068 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3070 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3073 Log("Directory bad, vnode %d; %s...\n",
3074 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3077 CopyAndSalvage(&dir);
3081 dirHandle = dir.dirHandle;
3083 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3086 /* If enumeration failed for random reasons, we will probably delete
3087 * too much stuff, so we guard against this instead.
3089 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3092 /* Delete the old directory if it was copied in order to salvage.
3093 * CopyOnWrite has written the new inode # to the disk, but we still
3094 * have the old one in our local structure here. Thus, we idec the
3098 if (dir.copied && !Testing) {
3099 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3101 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3104 /* Remember rootdir DirSummary _after_ it has been judged */
3105 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3106 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3113 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3115 /* This routine, for now, will only be called for read-write volumes */
3117 int BlocksInVolume = 0, FilesInVolume = 0;
3118 register VnodeClass class;
3119 struct DirSummary rootdir, oldrootdir;
3120 struct VnodeInfo *dirVnodeInfo;
3121 struct VnodeDiskObject vnode;
3122 VolumeDiskData volHeader;
3124 int orphaned, rootdirfound = 0;
3125 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3126 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3127 struct VnodeEssence *vep;
3132 VnodeId LFVnode, ThisVnode;
3133 Unique LFUnique, ThisUnique;
3136 vid = rwIsp->volSummary->header.id;
3137 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3138 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3139 assert(nBytes == sizeof(volHeader));
3140 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3141 assert (volHeader.destroyMe != DESTROY_ME);
3142 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3144 DistilVnodeEssence(vid, vLarge,
3145 rwIsp->volSummary->header.largeVnodeIndex,
3147 DistilVnodeEssence(vid, vSmall,
3148 rwIsp->volSummary->header.smallVnodeIndex,
3151 dirVnodeInfo = &vnodeInfo[vLarge];
3152 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3153 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3154 &rootdir, &rootdirfound);
3161 /* Parse each vnode looking for orphaned vnodes and
3162 * connect them to the tree as orphaned (if requested).
3164 oldrootdir = rootdir;
3165 for (class=0; class < nVNODECLASSES; class++) {
3166 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3167 vep = &(vnodeInfo[class].vnodes[v]);
3168 ThisVnode = bitNumberToVnodeNumber(v, class);
3169 ThisUnique = vep->unique;
3171 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3172 continue; /* Ignore unused, claimed, and root vnodes */
3174 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3175 * entry in this vnode had incremented the parent link count (In
3176 * JudgeEntry()). We need to go to the parent and decrement that
3177 * link count. But if the parent's unique is zero, then the parent
3178 * link count was not incremented in JudgeEntry().
3180 if (class == vLarge) { /* directory vnode */
3181 pv = vnodeIdToBitNumber(vep->parent);
3182 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3183 vnodeInfo[vLarge].vnodes[pv].count++;
3187 continue; /* If no rootdir, can't attach orphaned files */
3189 /* Here we attach orphaned files and directories into the
3190 * root directory, LVVnode, making sure link counts stay correct.
3192 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3193 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3194 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3196 /* Update this orphaned vnode's info. Its parent info and
3197 * link count (do for orphaned directories and files).
3199 vep->parent = LFVnode; /* Parent is the root dir */
3200 vep->unique = LFUnique;
3203 vep->count--; /* Inc link count (root dir will pt to it) */
3205 /* If this orphaned vnode is a directory, change '..'.
3206 * The name of the orphaned dir/file is unknown, so we
3207 * build a unique name. No need to CopyOnWrite the directory
3208 * since it is not connected to tree in BK or RO volume and
3209 * won't be visible there.
3211 if (class == vLarge) {
3215 /* Remove and recreate the ".." entry in this orphaned directory */
3216 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3218 pa.Unique = LFUnique;
3219 assert(Delete(&dh, "..") == 0);
3220 assert(Create(&dh, "..", &pa) == 0);
3222 /* The original parent's link count was decremented above.
3223 * Here we increment the new parent's link count.
3225 pv = vnodeIdToBitNumber(LFVnode);
3226 vnodeInfo[vLarge].vnodes[pv].count--;
3230 /* Go to the root dir and add this entry. The link count of the
3231 * root dir was incremented when ".." was created. Try 10 times.
3233 for (j=0; j<10; j++) {
3234 pa.Vnode = ThisVnode;
3235 pa.Unique = ThisUnique;
3237 sprintf(npath, "%s.%d.%d",
3238 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3239 ThisVnode, ThisUnique);
3241 CopyOnWrite(&rootdir);
3242 code = Create(&rootdir.dirHandle, npath, &pa);
3245 ThisUnique += 50; /* Try creating a different file */
3248 Log("Attaching orphaned %s to volume's root dir as %s\n",
3249 ((class == vLarge)?"directory":"file"), npath);
3251 } /* for each vnode in the class */
3252 } /* for each class of vnode */
3254 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3256 if (!oldrootdir.copied && rootdir.copied) {
3257 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3259 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3262 DFlush(); /* Flush the changes */
3263 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3264 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3265 orphans = ORPH_IGNORE;
3268 /* Write out all changed vnodes. Orphaned files and directories
3269 * will get removed here also (if requested).
3271 for (class = 0; class < nVNODECLASSES; class++){
3272 int nVnodes = vnodeInfo[class].nVnodes;
3273 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3274 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3275 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3276 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3277 for (i = 0; i<nVnodes; i++) {
3278 register struct VnodeEssence *vnp = &vnodes[i];
3279 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3281 /* If the vnode is good but is unclaimed (not listed in
3282 * any directory entries), then it is orphaned.
3285 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3286 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3290 if (vnp->changed || vnp->count) {
3293 nBytes = IH_IREAD(vnodeInfo[class].handle,
3294 vnodeIndexOffset(vcp, vnodeNumber),
3295 (char*)&vnode, sizeof (vnode));
3296 assert(nBytes == sizeof(vnode));
3298 vnode.parent = vnp->parent;
3299 oldCount = vnode.linkCount;
3300 vnode.linkCount = vnode.linkCount - vnp->count;
3303 orphaned = IsVnodeOrphaned(vnodeNumber);
3305 if (!vnp->todelete) {
3306 /* Orphans should have already been attached (if requested) */
3307 assert(orphans != ORPH_ATTACH);
3308 oblocks += vnp->blockCount;
3311 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3312 BlocksInVolume -= vnp->blockCount;
3314 if (VNDISK_GET_INO(&vnode)) {
3315 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3318 memset(&vnode, 0, sizeof(vnode));
3320 } else if (vnp->count) {
3322 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3323 vnodeNumber, oldCount,
3324 (Testing?"would have changed to":"now"), vnode.linkCount);
3328 vnode.dataVersion++;
3330 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3331 vnodeIndexOffset(vcp, vnodeNumber),
3332 (char*)&vnode, sizeof (vnode));
3333 assert(nBytes == sizeof(vnode));
3339 if (!Showmode && ofiles) {
3340 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3341 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3345 for (class = 0; class < nVNODECLASSES; class++) {
3346 register struct VnodeInfo *vip = &vnodeInfo[class];
3347 for (i=0; i<vip->nVnodes; i++)
3348 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3349 if (vip->vnodes) free(vip->vnodes);
3350 if (vip->inodes) free(vip->inodes);
3353 /* Set correct resource utilization statistics */
3354 volHeader.filecount = FilesInVolume;
3355 volHeader.diskused = BlocksInVolume;
3357 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3358 if (volHeader.uniquifier < (maxunique + 1)) {
3359 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3360 /* Plus 2,000 in case there are workstations out there with
3361 * cached vnodes that have since been deleted
3363 volHeader.uniquifier = (maxunique + 1 + 2000);
3366 /* Turn off the inUse bit; the volume's been salvaged! */
3367 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3368 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3369 volHeader.inService = 1; /* allow service again */
3370 volHeader.needsCallback = (VolumeChanged != 0);
3371 volHeader.dontSalvage = DONT_SALVAGE;
3374 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3375 assert(nBytes == sizeof(volHeader));
3378 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3379 (Testing?"It would have ":""), volHeader.name,
3380 volHeader.id, FilesInVolume, BlocksInVolume);
3382 IH_RELEASE(vnodeInfo[vSmall].handle);
3383 IH_RELEASE(vnodeInfo[vLarge].handle);
3388 void ClearROInUseBit(struct VolumeSummary *summary)
3390 IHandle_t *h = summary->volumeInfoHandle;
3393 VolumeDiskData volHeader;
3395 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3396 assert(nBytes == sizeof(volHeader));
3397 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3398 volHeader.inUse = 0;
3399 volHeader.needsSalvaged = 0;
3400 volHeader.inService = 1;
3401 volHeader.dontSalvage = DONT_SALVAGE;
3403 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3404 assert(nBytes == sizeof(volHeader));
3409 * Possible delete the volume.
3411 * deleteMe - Always do so, only a partial volume.
3413 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3414 int deleteMe, int check)
3416 if (readOnly(isp) || deleteMe) {
3417 if (isp->volSummary && isp->volSummary->fileName) {
3419 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);
3420 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3422 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3423 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3426 unlink(isp->volSummary->fileName);
3430 Log("%s salvage was unsuccessful: read-write volume %u\n",
3431 message, isp->volumeId);
3432 Abort("Salvage of volume %u aborted\n",
3438 void AskOffline(VolumeId volumeId)
3440 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3441 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3442 Abort("Salvage aborted\n");
3446 void AskOnline(VolumeId volumeId, char *partition)
3448 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3449 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3450 volumeId, partition);
3454 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3456 /* Volume parameter is passed in case iopen is upgraded in future to
3457 * require a volume Id to be passed
3460 IHandle_t *srcH, *destH;
3461 FdHandle_t *srcFdP, *destFdP;
3464 IH_INIT(srcH, device, rwvolume, inode1);
3465 srcFdP = IH_OPEN(srcH);
3466 assert(srcFdP != NULL);
3467 IH_INIT(destH, device, rwvolume, inode2);
3468 destFdP = IH_OPEN(destH);
3470 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3471 assert(FDH_WRITE(destFdP, buf, n) == n);
3473 FDH_REALLYCLOSE(srcFdP);
3474 FDH_REALLYCLOSE(destFdP);
3480 void PrintInodeList(void)
3482 register struct ViceInodeInfo *ip;
3483 struct ViceInodeInfo *buf;
3487 assert(fstat(inodeFd, &status) == 0);
3488 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3489 assert(buf != NULL);
3490 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3491 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3492 for(ip = buf; nInodes--; ip++) {
3493 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3494 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3495 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3500 void PrintInodeSummary(void)
3503 struct InodeSummary *isp;
3505 for (i=0; i<nVolumesInInodeFile; i++) {
3506 isp = &inodeSummary[i];
3507 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3508 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3509 isp->nSpecialInodes, isp->maxUniquifier);
3513 void PrintVolumeSummary(void)
3516 struct VolumeSummary *vsp;
3518 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3519 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3527 assert(0); /* Fork is never executed in the NT code path */
3538 if (ShowLog) showlog();
3540 if (main_thread != pthread_self())
3541 pthread_exit((void*)code);
3549 int Wait(char *prog)
3553 pid = wait(&status);
3555 if (WCOREDUMP(status))
3556 Log("\"%s\" core dumped!\n", prog);
3557 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3562 static char *TimeStamp(time_t clock, int precision)
3565 static char timestamp[20];
3566 lt = localtime(&clock);
3568 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3570 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3574 void CheckLogFile(void)
3576 char oldSlvgLog[AFSDIR_PATH_MAX];
3578 #ifndef AFS_NT40_ENV
3585 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3586 strcat(oldSlvgLog, ".old");
3588 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3589 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3591 if (!logFile) { /* still nothing, use stdout */
3596 #ifndef AFS_NAMEI_ENV
3597 AFS_DEBUG_IOPS_LOG(logFile);
3606 #ifndef AFS_NT40_ENV
3608 printf("Can't show log since using syslog.\n");
3617 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3620 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3623 while (fgets(line, sizeof(line), logFile))
3629 void Log(a,b,c,d,e,f,g,h,i,j,k)
3630 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3634 #ifndef AFS_NT40_ENV
3637 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3641 gettimeofday(&now, 0);
3642 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3643 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3648 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3649 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3651 #ifndef AFS_NT40_ENV
3654 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3658 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3660 if (ShowLog) showlog();
3667 char *ToString(char *s)
3670 p = (char *) malloc(strlen(s)+1);
3677 /* Remove the FORCESALVAGE file */
3678 void RemoveTheForce(char *path)
3680 if (!Testing && ForceSalvage) {
3681 if (chdir(path) == 0)
3682 unlink("FORCESALVAGE");
3686 #ifndef AFS_AIX32_ENV
3688 * UseTheForceLuke - see if we can use the force
3690 int UseTheForceLuke(char *path)
3694 assert(chdir(path) != -1);
3696 return (stat("FORCESALVAGE", &force) == 0);
3700 * UseTheForceLuke - see if we can use the force
3703 * The VRMIX fsck will not muck with the filesystem it is supposedly
3704 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3705 * muck directly with the root inode, which is within the normal
3707 * ListViceInodes() has a side effect of setting ForceSalvage if
3708 * it detects a need, based on root inode examination.
3710 int UseTheForceLuke(char *path)
3713 return 0; /* sorry OB1 */
3718 /* NT support routines */
3720 static char execpathname[MAX_PATH];
3721 int nt_SalvagePartition(char *partName, int jobn)
3726 if (!*execpathname) {
3727 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3728 if (!n || n == 1023)
3731 job.cj_magic = SALVAGER_MAGIC;
3732 job.cj_number = jobn;
3733 (void) strcpy(job.cj_part, partName);
3734 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3739 int nt_SetupPartitionSalvage(void *datap, int len)
3741 childJob_t *jobp = (childJob_t*)datap;
3742 char logname[AFSDIR_PATH_MAX];
3744 if (len != sizeof(childJob_t))
3746 if (jobp->cj_magic != SALVAGER_MAGIC)
3751 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3753 logFile = fopen(logname, "w");
3754 if (!logFile) logFile = stdout;
3760 #endif /* AFS_NT40_ENV */