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);
402 void TimeStampLogFile(void);
404 void ClearROInUseBit(struct VolumeSummary *summary);
405 void CopyAndSalvage(register struct DirSummary *dir);
406 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
407 void CopyOnWrite(register struct DirSummary *dir);
408 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
409 register struct InodeSummary * summary);
410 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
411 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
413 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
414 void GetVolumeSummary(VolumeId singleVolumeNumber);
415 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
417 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
420 void ObtainSalvageLock(void);
421 void PrintInodeList(void);
422 void PrintInodeSummary(void);
423 void PrintVolumeSummary(void);
424 int QuickCheck(register struct InodeSummary *isp, int nVols);
425 void RemoveTheForce(char *path);
426 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
427 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
429 void SalvageFileSysParallel(struct DiskPartition *partP);
430 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
431 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
432 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
433 int check, int *deleteMe);
434 int SalvageIndex(Inode ino, VnodeClass class, int RW,
435 register struct ViceInodeInfo *ip,
436 int nInodes, struct VolumeSummary *volSummary, int check);
437 int SalvageVnodes(register struct InodeSummary *rwIsp,
438 register struct InodeSummary * thisIsp,
439 register struct ViceInodeInfo * inodes, int check);
440 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
441 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
443 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
445 #define SalvageVolumeGroup DoSalvageVolumeGroup
447 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
448 register struct ViceInodeInfo *inodes,
449 int RW, int check, int *deleteMe);
451 int UseTheForceLuke(char *path);
453 static int IsVnodeOrphaned(VnodeId vnode);
455 /* Uniquifier stored in the Inode */
456 static Unique IUnique(Unique u)
459 return(u & 0x3fffff);
461 #if defined(AFS_SGI_EXMAG)
462 return(u & SGI_UNIQMASK);
465 #endif /* AFS_SGI_EXMAG */
469 static int BadError(register int aerror)
471 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
473 return 0; /* otherwise may be transient, e.g. EMFILE */
478 static int handleit(struct cmd_syndesc *as)
480 register struct cmd_item *ti;
481 char pname[100], *temp;
482 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0, i;
483 struct DiskPartition *partP;
485 #ifdef AFS_SGI_VNODE_GLUE
486 if (afs_init_kernel_config(-1) <0) {
487 printf("Can't determine NUMA configuration, not starting salvager.\n");
493 for (i = 0; i < CMD_MAXPARMS; i++) {
494 if (as->parms[i].items) {
500 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
509 #endif /* FAST_RESTART */
510 if (ti = as->parms[0].items) { /* -partition */
512 strncpy(pname, ti->data, 100);
514 if (ti = as->parms[1].items) { /* -volumeid */
516 printf("You must also specify '-partition' option with the '-volumeid' option\n");
520 vid = atoi(ti->data);
522 if (as->parms[2].items) /* -debug */
524 if (as->parms[3].items) /* -nowrite */
526 if (as->parms[4].items) /* -inodes */
528 if (as->parms[5].items) /* -force */
530 if (as->parms[6].items) /* -oktozap */
532 if (as->parms[7].items) /* -rootinodes */
534 if (as->parms[8].items) /* -RebuildDirs */
536 if (as->parms[9].items) /* -ForceReads */
538 if (ti = as->parms[10].items) {/* -Parallel # */
540 if (strncmp(temp,"all",3) == 0) {
544 if (strlen(temp) != 0) {
545 Parallel = atoi(temp);
546 if (Parallel < 1) Parallel = 1;
547 if (Parallel > MAXPARALLEL) {
548 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
549 Parallel = MAXPARALLEL;
553 if (ti = as->parms[11].items) {/* -tmpdir */
557 dirp = opendir(tmpdir);
559 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
564 if (ti = as->parms[12].items) /* -showlog */
566 if (ti = as->parms[13].items) { /* -log */
571 if (ti = as->parms[14].items) { /* -showmounts */
576 if (ti = as->parms[15].items) { /* -orphans */
578 orphans = ORPH_IGNORE;
579 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
580 orphans = ORPH_REMOVE;
581 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
582 orphans = ORPH_ATTACH;
585 #ifndef AFS_NT40_ENV /* ignore options on NT */
586 if ( ti = as->parms[16].items) { /* -syslog */
590 if ( ti = as->parms[17].items) { /* -syslogfacility */
591 useSyslogFacility = atoi(ti->data);
594 if (ti = as->parms[18].items) { /* -datelogs */
600 if (ti = as->parms[19].items) { /* -DontSalvage */
601 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
609 #endif /* FAST_RESTART */
611 /* Note: if seemvol we initialize this as a standard volume utility: this has the
612 implication that the file server may be running; negotations have to be made with
613 the file server in this case to take the read write volume and associated read-only
614 volumes off line before salvaging */
617 if (afs_winsockInit()<0) {
618 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
619 AFSDIR_SALVAGER_FILE, 0);
620 Log("Failed to initailize winsock, exiting.\n");
625 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
628 if (myjob.cj_number != NOT_CHILD) {
631 (void) strcpy(pname, myjob.cj_part);
636 for (partP = DiskPartitionList; partP; partP = partP->next) {
637 SalvageFileSysParallel(partP);
639 SalvageFileSysParallel(0);
642 partP = VGetPartition(pname, 0);
644 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
649 SalvageFileSys(partP, 0);
651 /* Salvage individual volume */
653 Log("salvage: invalid volume id specified; salvage aborted\n");
656 SalvageFileSys (partP, vid);
664 #include "AFS_component_version_number.c"
668 char *save_args[MAX_ARGS];
670 pthread_t main_thread;
673 int main(int argc, char **argv)
675 struct cmd_syndesc *ts;
677 char commandLine[150];
680 extern char cml_version_number[];
684 * The following signal action for AIX is necessary so that in case of a
685 * crash (i.e. core is generated) we can include the user's data section
686 * in the core dump. Unfortunately, by default, only a partial core is
687 * generated which, in many cases, isn't too useful.
689 struct sigaction nsa;
691 sigemptyset(&nsa.sa_mask);
692 nsa.sa_handler = SIG_DFL;
693 nsa.sa_flags = SA_FULLDUMP;
694 sigaction(SIGABRT, &nsa, NULL);
695 sigaction(SIGSEGV, &nsa, NULL);
698 /* Initialize directory paths */
699 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
701 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
703 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
707 main_thread = pthread_self();
708 if (spawnDatap && spawnDataLen) {
709 /* This is a child per partition salvager. Don't setup log or
710 * try to lock the salvager lock.
712 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
717 for (commandLine[0] = '\0', i=0; i<argc; i++) {
718 if (i > 0) strcat(commandLine, " ");
719 strcat(commandLine, argv[i]);
722 /* All entries to the log will be appended. Useful if there are
723 * multiple salvagers appending to the log.
728 #ifdef AFS_LINUX20_ENV
729 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
731 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
737 if (geteuid() != 0) {
738 printf("Salvager must be run as root.\n");
744 /* bad for normal help flag processing, but can do nada */
746 fprintf(logFile, "%s\n", cml_version_number);
747 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
749 /* Get and hold a lock for the duration of the salvage to make sure
750 * that no other salvage runs at the same time. The routine
751 * VInitVolumePackage (called below) makes sure that a file server or
752 * other volume utilities don't interfere with the salvage.
759 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
760 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
761 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
762 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
763 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
764 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
765 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
766 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
767 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
768 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
769 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
770 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
771 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
772 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
773 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
774 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
775 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
777 /* note - syslog isn't avail on NT, but if we make it conditional, have
778 to deal with screwy offsets for cmd params */
779 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
780 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
781 cmd_AddParm(ts, "-datelogs", CMD_FLAG, CMD_OPTIONAL, "Include timestamp in logfile filename");
784 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");
785 #endif /* FAST_RESTART */
786 err = cmd_Dispatch(argc, argv);
790 /* Get the salvage lock if not already held. Hold until process exits. */
791 void ObtainSalvageLock(void)
796 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
797 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
799 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
801 "salvager: There appears to be another salvager running! Aborted.\n");
805 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
806 assert(salvageLock >= 0);
807 #ifdef AFS_DARWIN_ENV
808 if (flock(salvageLock, LOCK_EX) == -1) {
810 if (lockf(salvageLock, F_LOCK, 0) == -1) {
813 "salvager: There appears to be another salvager running! Aborted.\n");
820 #ifdef AFS_SGI_XFS_IOPS_ENV
821 /* Check if the given partition is mounted. For XFS, the root inode is not a
822 * constant. So we check the hard way.
824 int IsPartitionMounted(char *part)
827 struct mntent *mntent;
829 assert(mntfp = setmntent(MOUNTED, "r"));
830 while (mntent = getmntent(mntfp)) {
831 if (!strcmp(part, mntent->mnt_dir))
836 return mntent ? 1 : 1;
839 /* Check if the given inode is the root of the filesystem. */
840 #ifndef AFS_SGI_XFS_IOPS_ENV
841 int IsRootInode(struct stat *status)
844 * The root inode is not a fixed value in XFS partitions. So we need to
845 * see if the partition is in the list of mounted partitions. This only
846 * affects the SalvageFileSys path, so we check there.
848 return (status->st_ino == ROOTINODE);
853 #ifndef AFS_NAMEI_ENV
854 /* We don't want to salvage big files filesystems, since we can't put volumes on
857 int CheckIfBigFilesFS(char *mountPoint, char *devName)
859 struct superblock fs;
862 if (strncmp(devName, "/dev/", 5)) {
863 (void) sprintf(name, "/dev/%s", devName);
866 (void) strcpy(name, devName);
869 if (ReadSuper(&fs, name)<0) {
870 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
873 if (IsBigFilesFileSystem(&fs)) {
874 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
883 #define HDSTR "\\Device\\Harddisk"
884 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
885 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
892 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
894 if (strncmp(res, HDSTR, HDLEN)) {
897 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
898 res, HDSTR, p1->devName);
902 d1 = atoi(&res[HDLEN]);
904 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
906 if (strncmp(res, HDSTR, HDLEN)) {
909 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
910 res, HDSTR, p2->devName);
914 d2 = atoi(&res[HDLEN]);
919 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
922 /* This assumes that two partitions with the same device number divided by
923 * PartsPerDisk are on the same disk.
925 void SalvageFileSysParallel(struct DiskPartition *partP)
928 struct DiskPartition *partP;
929 int pid; /* Pid for this job */
930 int jobnumb; /* Log file job number */
931 struct job *nextjob; /* Next partition on disk to salvage */
933 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
934 struct job *thisjob = 0;
935 static int numjobs = 0;
936 static int jobcount = 0;
942 char logFileName[256];
946 /* We have a partition to salvage. Copy it into thisjob */
947 thisjob = (struct job *) malloc(sizeof(struct job));
949 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
952 memset(thisjob, 0, sizeof(struct job));
953 thisjob->partP = partP;
954 thisjob->jobnumb = jobcount;
957 else if (jobcount == 0) {
958 /* We are asking to wait for all jobs (partp == 0), yet we never
961 Log("No file system partitions named %s* found; not salvaged\n",
962 VICE_PARTITION_PREFIX);
966 if (debug || Parallel == 1) {
968 SalvageFileSys(thisjob->partP, 0);
975 /* Check to see if thisjob is for a disk that we are already
976 * salvaging. If it is, link it in as the next job to do. The
977 * jobs array has 1 entry per disk being salvages. numjobs is
978 * the total number of disks currently being salvaged. In
979 * order to keep thejobs array compact, when a disk is
980 * completed, the hightest element in the jobs array is moved
981 * down to now open slot.
983 for (j=0; j<numjobs; j++) {
984 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
985 /* On same disk, add it to this list and return */
986 thisjob->nextjob = jobs[j]->nextjob;
987 jobs[j]->nextjob = thisjob;
994 /* Loop until we start thisjob or until all existing jobs are finished */
995 while ( thisjob || (!partP && (numjobs > 0)) ) {
996 startjob = -1; /* No new job to start */
998 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
999 /* Either the max jobs are running or we have to wait for all
1000 * the jobs to finish. In either case, we wait for at least one
1001 * job to finish. When it's done, clean up after it.
1003 pid = wait(&wstatus);
1005 for (j=0; j<numjobs; j++) { /* Find which job it is */
1006 if (pid == jobs[j]->pid) break;
1008 assert(j < numjobs);
1009 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1010 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1013 numjobs--; /* job no longer running */
1014 oldjob = jobs[j]; /* remember */
1015 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1016 free(oldjob); /* free the old job */
1018 /* If there is another partition on the disk to salvage, then
1019 * say we will start it (startjob). If not, then put thisjob there
1020 * and say we will start it.
1022 if (jobs[j]) { /* Another partitions to salvage */
1023 startjob = j; /* Will start it */
1024 } else { /* There is not another partition to salvage */
1026 jobs[j] = thisjob; /* Add thisjob */
1028 startjob = j; /* Will start it */
1030 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1031 startjob = -1; /* Don't start it - already running */
1035 /* We don't have to wait for a job to complete */
1037 jobs[numjobs] = thisjob; /* Add this job */
1039 startjob = numjobs; /* Will start it */
1043 /* Start up a new salvage job on a partition in job slot "startjob" */
1044 if (startjob != -1) {
1046 Log("Starting salvage of file system partition %s\n",
1047 jobs[startjob]->partP->name);
1049 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1050 * commands and pass the child job number via the data path.
1052 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1053 jobs[startjob]->jobnumb);
1054 jobs[startjob]->pid = pid;
1059 jobs[startjob]->pid = pid;
1065 for (fd =0; fd < 16; fd++) close(fd);
1066 open("/", 0); dup2(0, 1); dup2(0, 2);
1067 #ifndef AFS_NT40_ENV
1069 openlog(NULL, LOG_PID, useSyslogFacility);
1073 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1074 logFile = fopen(logFileName, "w");
1076 if (!logFile) logFile = stdout;
1078 SalvageFileSys1(jobs[startjob]->partP, 0);
1083 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1085 /* If waited for all jobs to complete, now collect log files and return */
1086 #ifndef AFS_NT40_ENV
1087 if ( ! useSyslog ) /* if syslogging - no need to collect */
1090 for (i=0; i<jobcount; i++) {
1091 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1092 if (passLog = fopen(logFileName, "r")) {
1093 while(fgets(buf, sizeof(buf), passLog)) {
1094 fputs(buf, logFile);
1098 (void)unlink(logFileName);
1106 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1108 if (!canfork || debug || Fork() == 0) {
1109 SalvageFileSys1(partP, singleVolumeNumber);
1110 if (canfork && !debug) {
1116 Wait("SalvageFileSys");
1119 char *get_DevName(char *pbuffer, char *wpath)
1121 char pbuf[128], *ptr;
1122 strcpy(pbuf, pbuffer);
1123 ptr = (char *)strrchr(pbuf, '/');
1126 strcpy(wpath, pbuf);
1129 ptr = (char *)strrchr(pbuffer, '/');
1131 strcpy(pbuffer, ptr+1);
1137 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1140 char inodeListPath[256];
1141 static char tmpDevName[100];
1142 static char wpath[100];
1143 struct VolumeSummary *vsp, *esp;
1146 fileSysPartition = partP;
1147 fileSysDevice = fileSysPartition->device;
1148 fileSysPathName = VPartitionPath(fileSysPartition);
1151 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1152 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1153 name = partP->devName;
1155 fileSysPath = fileSysPathName;
1156 strcpy(tmpDevName, partP->devName);
1157 name = get_DevName(tmpDevName, wpath);
1158 fileSysDeviceName = name;
1159 filesysfulldev = wpath;
1162 VLockPartition(partP->name);
1163 if (singleVolumeNumber || ForceSalvage)
1166 ForceSalvage = UseTheForceLuke(fileSysPath);
1168 if (singleVolumeNumber) {
1169 if (!VConnectFS()) {
1170 Abort("Couldn't connect to file server\n");
1172 AskOffline(singleVolumeNumber);
1175 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1177 Log("***Forced salvage of all volumes on this partition***\n");
1182 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1189 assert((dirp = opendir(fileSysPath)) != NULL);
1190 while (dp = readdir(dirp)) {
1191 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1192 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1194 Log("Removing old salvager temp files %s\n", dp->d_name);
1195 strcpy(npath, fileSysPath);
1197 strcat(npath, dp->d_name);
1203 tdir = (tmpdir ? tmpdir : fileSysPath);
1205 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1206 (void) strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
1208 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1210 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1211 unlink(inodeListPath);
1215 /* Using nt_unlink here since we're really using the delete on close
1216 * semantics of unlink. In most places in the salvager, we really do
1217 * mean to unlink the file at that point. Those places have been
1218 * modified to actually do that so that the NT crt can be used there.
1220 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1221 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1223 inodeFd = open(inodeListPath, O_RDONLY);
1224 unlink(inodeListPath);
1227 Abort("Temporary file %s is missing...\n",
1229 if (ListInodeOption) {
1233 /* enumerate volumes in the partition.
1234 figure out sets of read-only + rw volumes.
1235 salvage each set, read-only volumes first, then read-write.
1236 Fix up inodes on last volume in set (whether it is read-write
1239 GetVolumeSummary(singleVolumeNumber);
1241 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1242 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1243 for (j=i; j < nVolumesInInodeFile
1244 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1245 VolumeId vid = inodeSummary[j].volumeId;
1246 struct VolumeSummary *tsp;
1247 /* Scan volume list (from partition root directory) looking for the
1248 current rw volume number in the volume list from the inode scan.
1249 If there is one here that is not in the inode volume list,
1251 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1253 DeleteExtraVolumeHeaderFile(vsp);
1255 /* Now match up the volume summary info from the root directory with the
1256 entry in the volume list obtained from scanning inodes */
1257 inodeSummary[j].volSummary = NULL;
1258 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1259 if (tsp->header.id == vid) {
1260 inodeSummary[j].volSummary = tsp;
1266 /* Salvage the group of volumes (several read-only + 1 read/write)
1267 * starting with the current read-only volume we're looking at.
1269 SalvageVolumeGroup(&inodeSummary[i], j-i);
1272 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1273 for ( ; vsp<esp; vsp++) {
1275 DeleteExtraVolumeHeaderFile(vsp);
1278 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1279 RemoveTheForce(fileSysPath);
1281 if (!Testing && singleVolumeNumber) {
1282 AskOnline(singleVolumeNumber, fileSysPartition->name);
1284 /* Step through the volumeSummary list and set all volumes on-line.
1285 * The volumes were taken off-line in GetVolumeSummary.
1287 for (j=0; j<nVolumes; j++) {
1288 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1293 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1294 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1297 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1300 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1302 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1303 vsp->fileName, (Testing? "would have been ":""));
1305 unlink(vsp->fileName);
1309 CompareInodes(const void *_p1, const void *_p2)
1311 register const struct ViceInodeInfo *p1 = _p1;
1312 register const struct ViceInodeInfo *p2 = _p2;
1313 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1314 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1315 VolumeId p1rwid, p2rwid;
1316 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1317 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1318 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1319 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1320 if (p1rwid < p2rwid)
1322 if (p1rwid > p2rwid)
1324 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1325 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1326 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1327 return (p1->u.special.type < p2->u.special.type? -1: 1);
1328 if (p1->u.vnode.volumeId == p1rwid)
1330 if (p2->u.vnode.volumeId == p2rwid)
1332 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1334 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1335 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1336 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1338 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1340 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1342 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1344 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1346 /* The following tests are reversed, so that the most desirable
1347 of several similar inodes comes first */
1348 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1349 #ifdef AFS_3DISPARES
1350 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1351 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1354 #ifdef AFS_SGI_EXMAG
1355 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1356 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1361 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1362 #ifdef AFS_3DISPARES
1363 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1364 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1367 #ifdef AFS_SGI_EXMAG
1368 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1369 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1374 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1375 #ifdef AFS_3DISPARES
1376 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1377 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1380 #ifdef AFS_SGI_EXMAG
1381 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1382 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1387 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1388 #ifdef AFS_3DISPARES
1389 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1390 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1393 #ifdef AFS_SGI_EXMAG
1394 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1395 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1403 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1404 register struct InodeSummary * summary)
1406 int volume = ip->u.vnode.volumeId;
1407 int rwvolume = volume;
1408 register n, nSpecial;
1409 register Unique maxunique;
1412 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1414 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1416 rwvolume = ip->u.special.parentId;
1417 /* This isn't quite right, as there could (in error) be different
1418 parent inodes in different special vnodes */
1421 if (maxunique < ip->u.vnode.vnodeUniquifier)
1422 maxunique = ip->u.vnode.vnodeUniquifier;
1426 summary->volumeId = volume;
1427 summary->RWvolumeId = rwvolume;
1428 summary->nInodes =n;
1429 summary->nSpecialInodes = nSpecial;
1430 summary->maxUniquifier = maxunique;
1433 int OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber)
1435 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1436 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1437 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1442 * Collect list of inodes in file named by path. If a truly fatal error,
1443 * unlink the file and abort. For lessor errors, return -1. The file will
1444 * be unlinked by the caller.
1446 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1450 struct ViceInodeInfo *ip;
1451 struct InodeSummary summary;
1452 char summaryFileName[50];
1455 char *dev = fileSysPath;
1456 char *wpath = fileSysPath;
1458 char *dev = fileSysDeviceName;
1459 char *wpath = filesysfulldev;
1461 char *part = fileSysPath;
1464 /* This file used to come from vfsck; cobble it up ourselves now... */
1465 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1467 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1472 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1474 if (forceSal && !ForceSalvage) {
1475 Log("***Forced salvage of all volumes on this partition***\n");
1478 inodeFd = open(path, O_RDWR);
1479 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1481 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1483 tdir = (tmpdir ? tmpdir : part);
1485 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1486 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1488 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1490 summaryFile = fopen(summaryFileName, "a+");
1491 if (summaryFile == NULL) {
1494 Abort("Unable to create inode summary file\n");
1496 if (!canfork || debug || Fork() == 0) {
1498 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1500 fclose(summaryFile); close(inodeFd);
1501 unlink(summaryFileName);
1502 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1503 RemoveTheForce(fileSysPath);
1504 Log("%s vice inodes on %s; not salvaged\n",
1505 singleVolumeNumber? "No applicable": "No", dev);
1508 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1510 fclose(summaryFile); close(inodeFd);
1512 unlink(summaryFileName);
1513 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1515 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1516 fclose(summaryFile); close(inodeFd);
1518 unlink(summaryFileName);
1519 Abort("Unable to read inode table; %s not salvaged\n", dev);
1521 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1522 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1523 write(inodeFd, ip, status.st_size) != status.st_size) {
1524 fclose(summaryFile); close(inodeFd);
1526 unlink(summaryFileName);
1527 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1531 CountVolumeInodes(ip, nInodes, &summary);
1532 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1533 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1534 fclose(summaryFile); close(inodeFd);
1537 summary.index += (summary.nInodes);
1538 nInodes -= summary.nInodes;
1539 ip += summary.nInodes;
1541 /* Following fflush is not fclose, because if it was debug mode would not work */
1542 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1543 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1544 fclose(summaryFile); close(inodeFd);
1547 if (canfork && !debug) {
1553 if (Wait("Inode summary") == -1) {
1554 fclose(summaryFile); close(inodeFd);
1556 unlink(summaryFileName);
1557 Exit(1); /* salvage of this partition aborted */
1560 assert(fstat(fileno(summaryFile), &status) != -1);
1561 if ( status.st_size != 0 ) {
1563 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1564 assert(inodeSummary != NULL);
1565 /* For GNU we need to do lseek to get the file pointer moved. */
1566 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1567 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1568 assert(ret == status.st_size);
1570 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1571 fclose(summaryFile);
1573 unlink(summaryFileName);
1577 /* Comparison routine for volume sort.
1578 This is setup so that a read-write volume comes immediately before
1579 any read-only clones of that volume */
1580 int CompareVolumes(const void *_p1, const void *_p2)
1582 register const struct VolumeSummary *p1 = _p1;
1583 register const struct VolumeSummary *p2 = _p2;
1584 if (p1->header.parent != p2->header.parent)
1585 return p1->header.parent < p2->header.parent? -1: 1;
1586 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1588 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1590 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1593 void GetVolumeSummary(VolumeId singleVolumeNumber)
1596 afs_int32 nvols = 0;
1597 struct VolumeSummary *vsp, vs;
1598 struct VolumeDiskHeader diskHeader;
1601 /* Get headers from volume directory */
1602 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1603 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1604 if (!singleVolumeNumber) {
1605 while (dp = readdir(dirp)) {
1606 char *p = dp->d_name;
1607 p = strrchr(dp->d_name, '.');
1608 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1610 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1611 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1612 == sizeof (diskHeader) &&
1613 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1614 DiskToVolumeHeader(&vs.header, &diskHeader);
1621 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1625 if (!nvols) nvols = 1;
1626 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1628 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1629 assert(volumeSummaryp != NULL);
1632 vsp = volumeSummaryp;
1633 while (dp = readdir(dirp)) {
1634 char *p = dp->d_name;
1635 p = strrchr(dp->d_name, '.');
1636 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1639 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1640 || read(fd, &diskHeader, sizeof (diskHeader))
1641 != sizeof (diskHeader)
1642 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1647 if (!singleVolumeNumber) {
1648 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1649 dp->d_name, (Testing? "it would have been ":""));
1655 char nameShouldBe[64];
1656 DiskToVolumeHeader(&vsp->header, &diskHeader);
1657 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1658 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1661 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1662 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1663 if (singleVolumeNumber)
1664 AskOffline(vsp->header.id);
1665 if (strcmp(nameShouldBe, dp->d_name)) {
1666 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 ":""));
1671 vsp->fileName = ToString(dp->d_name);
1681 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1684 /* Find the link table. This should be associated with the RW volume or, if
1685 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1687 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1688 struct ViceInodeInfo *allInodes)
1691 struct ViceInodeInfo *ip;
1693 for (i=0; i<nVols; i++) {
1694 ip = allInodes + isp[i].index;
1695 for (j=0; j<isp[i].nSpecialInodes; j++) {
1696 if (ip[j].u.special.type == VI_LINKTABLE)
1697 return ip[j].inodeNumber;
1703 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1705 struct versionStamp version;
1708 if (!VALID_INO(ino))
1709 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1710 isp->volumeId, INODESPECIAL,
1711 VI_LINKTABLE, isp->RWvolumeId);
1712 if (!VALID_INO(ino))
1713 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1714 isp->RWvolumeId, errno);
1715 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1716 fdP = IH_OPEN(VGLinkH);
1718 Abort("Can't open link table for volume %u (error = %d)\n",
1719 isp->RWvolumeId, errno);
1721 if (FDH_TRUNC(fdP, 0)<0)
1722 Abort("Can't truncate link table for volume %u (error = %d)\n",
1723 isp->RWvolumeId, errno);
1725 version.magic = LINKTABLEMAGIC;
1726 version.version = LINKTABLEVERSION;
1728 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1730 Abort("Can't truncate link table for volume %u (error = %d)\n",
1731 isp->RWvolumeId, errno);
1733 FDH_REALLYCLOSE(fdP);
1735 /* If the volume summary exits (i.e., the V*.vol header file exists),
1736 * then set this inode there as well.
1738 if (isp->volSummary)
1739 isp->volSummary->header.linkTable = ino;
1745 void *nt_SVG(void *arg)
1747 SVGParms_t *parms = (SVGParms_t*)arg;
1748 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1752 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1755 pthread_attr_t tattr;
1759 /* Initialize per volume global variables, even if later code does so */
1763 memset(&VolInfo, 0, sizeof(VolInfo));
1765 parms.svgp_inodeSummaryp = isp;
1766 parms.svgp_count = nVols;
1767 code = pthread_attr_init(&tattr);
1769 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1773 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1775 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1779 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1781 Log("Failed to create thread to salvage volume group %u\n",
1785 (void) pthread_join(tid, NULL);
1787 #endif /* AFS_NT40_ENV */
1789 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1791 struct ViceInodeInfo *inodes,*allInodes,*ip;
1792 int i, totalInodes, size, salvageTo;
1796 int dec_VGLinkH = 0;
1798 FdHandle_t *fdP = NULL;
1801 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1802 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1803 if (!ForceSalvage && QuickCheck(isp, nVols))
1806 if (ShowMounts && !haveRWvolume)
1808 if (canfork && !debug && Fork() != 0) {
1809 (void) Wait("Salvage volume group");
1812 for (i = 0, totalInodes = 0; i<nVols; i++)
1813 totalInodes += isp[i].nInodes;
1814 size = totalInodes * sizeof (struct ViceInodeInfo);
1815 inodes = (struct ViceInodeInfo *) malloc(size);
1816 allInodes = inodes - isp->index; /* this would the base of all the inodes
1817 for the partition, if all the inodes
1818 had been read into memory */
1819 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1);
1820 assert(read(inodeFd,inodes,size) == size);
1822 /* Don't try to salvage a read write volume if there isn't one on this
1824 salvageTo = haveRWvolume? 0:1;
1826 #ifdef AFS_NAMEI_ENV
1827 ino = FindLinkHandle(isp, nVols, allInodes);
1828 if (VALID_INO(ino)) {
1829 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1830 fdP = IH_OPEN(VGLinkH);
1832 if (!VALID_INO(ino) || fdP == NULL) {
1833 Log("%s link table for volume %u.\n",
1834 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1836 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1839 CreateLinkTable(isp, ino);
1843 FDH_REALLYCLOSE(fdP);
1845 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1848 /* Salvage in reverse order--read/write volume last; this way any
1849 Inodes not referenced by the time we salvage the read/write volume
1850 can be picked up by the read/write volume */
1851 /* ACTUALLY, that's not done right now--the inodes just vanish */
1852 for (i = nVols-1; i>=salvageTo; i--) {
1854 struct InodeSummary *lisp = &isp[i];
1855 #ifdef AFS_NAMEI_ENV
1856 /* If only the RO is present on this partition, the link table
1857 * shows up as a RW volume special file. Need to make sure the
1858 * salvager doesn't try to salvage the non-existent RW.
1860 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1861 /* If this only special inode is the link table, continue */
1862 if (inodes->u.special.type == VI_LINKTABLE) {
1868 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1869 lisp->volumeId, (Testing?"(READONLY mode)":""));
1870 /* Check inodes twice. The second time do things seriously. This
1871 way the whole RO volume can be deleted, below, if anything goes wrong */
1872 for (check = 1; check>=0; check--) {
1874 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1875 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1876 if (rw && deleteMe) {
1877 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1878 volume won't be called */
1884 if (rw && check == 1)
1886 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1887 MaybeZapVolume(lisp,"Vnode index", 0, check);
1893 /* Fix actual inode counts */
1895 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1896 static int TraceBadLinkCounts = 0;
1897 #ifdef AFS_NAMEI_ENV
1898 if (VGLinkH->ih_ino == ip->inodeNumber) {
1899 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1900 VGLinkH_p1 = ip->u.param[0];
1901 continue; /* Deal with this last. */
1904 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1905 TraceBadLinkCounts--; /* Limit reports, per volume */
1906 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1907 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1908 ip->byteCount, ip->u.param[0], ip->u.param[1],
1909 ip->u.param[2], ip->u.param[3]);
1911 while (ip->linkCount > 0) {
1912 /* below used to assert, not break */
1914 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1915 Log ("idec failed. inode %s errno %d\n",
1916 PrintInode(NULL, ip->inodeNumber), errno);
1922 while (ip->linkCount < 0) {
1923 /* these used to be asserts */
1925 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1926 Log ("iinc failed. inode %s errno %d\n",
1927 PrintInode(NULL, ip->inodeNumber) ,errno);
1934 #ifdef AFS_NAMEI_ENV
1935 while (dec_VGLinkH > 0) {
1936 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1937 Log("idec failed on link table, errno = %d\n", errno);
1941 while (dec_VGLinkH < 0) {
1942 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1943 Log("iinc failed on link table, errno = %d\n", errno);
1950 /* Directory consistency checks on the rw volume */
1952 SalvageVolume(isp, VGLinkH);
1953 IH_RELEASE(VGLinkH);
1955 if (canfork && !debug) {
1961 int QuickCheck(register struct InodeSummary *isp, int nVols)
1963 /* Check headers BEFORE forking */
1967 for (i = 0; i<nVols; i++) {
1968 struct VolumeSummary *vs = isp[i].volSummary;
1969 VolumeDiskData volHeader;
1971 /* Don't salvage just because phantom rw volume is there... */
1972 /* (If a read-only volume exists, read/write inodes must also exist) */
1973 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1977 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1978 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1979 == sizeof(volHeader)
1980 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1981 && volHeader.dontSalvage == DONT_SALVAGE
1982 && volHeader.needsSalvaged == 0
1983 && volHeader.destroyMe == 0) {
1984 if (volHeader.inUse == 1) {
1985 volHeader.inUse = 0;
1986 volHeader.inService = 1;
1988 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1989 != sizeof(volHeader)) {
2006 /* SalvageVolumeHeaderFile
2008 * Salvage the top level V*.vol header file. Make sure the special files
2009 * exist and that there are no duplicates.
2011 * Calls SalvageHeader for each possible type of volume special file.
2014 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2015 register struct ViceInodeInfo *inodes,
2016 int RW, int check, int *deleteMe)
2020 register struct ViceInodeInfo *ip;
2021 int allinodesobsolete = 1;
2022 struct VolumeDiskHeader diskHeader;
2026 memset(&tempHeader, 0, sizeof(tempHeader));
2027 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2028 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2029 tempHeader.id = isp->volumeId;
2030 tempHeader.parent = isp->RWvolumeId;
2031 /* Check for duplicates (inodes are sorted by type field) */
2032 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2033 ip = &inodes[isp->index+i];
2034 if (ip->u.special.type == (ip+1)->u.special.type) {
2035 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2039 for (i = 0; i<isp->nSpecialInodes; i++) {
2040 ip = &inodes[isp->index+i];
2041 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2043 Log("Rubbish header inode\n");
2046 Log("Rubbish header inode; deleted\n");
2048 else if (!stuff[ip->u.special.type-1].obsolete) {
2049 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2050 if (!check && ip->u.special.type != VI_LINKTABLE)
2051 ip->linkCount--; /* Keep the inode around */
2052 allinodesobsolete = 0;
2056 if (allinodesobsolete) {
2063 VGLinkH_cnt ++; /* one for every header. */
2065 if (!RW && !check && isp->volSummary) {
2066 ClearROInUseBit(isp->volSummary);
2070 for (i = 0; i< MAXINODETYPE; i++) {
2071 if (stuff[i].inodeType == VI_LINKTABLE) {
2072 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2073 * And we may have recreated the link table earlier, so set the
2074 * RW header as well.
2076 if (VALID_INO(VGLinkH->ih_ino)) {
2077 *stuff[i].inode = VGLinkH->ih_ino;
2081 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2085 if (isp->volSummary == NULL) {
2087 sprintf(name, VFORMAT, isp->volumeId);
2089 Log("No header file for volume %u\n", isp->volumeId);
2092 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2093 isp->volumeId, (Testing?"it would have been ":""),
2094 fileSysPathName, name);
2095 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2096 assert(headerFd != -1);
2097 isp->volSummary = (struct VolumeSummary *)
2098 malloc(sizeof(struct VolumeSummary));
2099 isp->volSummary->fileName = ToString(name);
2103 /* hack: these two fields are obsolete... */
2104 isp->volSummary->header.volumeAcl = 0;
2105 isp->volSummary->header.volumeMountTable = 0;
2107 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2108 /* We often remove the name before calling us, so we make a fake one up */
2109 if (isp->volSummary->fileName) {
2110 strcpy(name, isp->volSummary->fileName);
2112 sprintf(name, VFORMAT, isp->volumeId);
2113 isp->volSummary->fileName = ToString(name);
2116 Log("Header file %s is damaged or no longer valid%s\n",
2117 name, (check ? "" : "; repairing"));
2121 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2122 assert(headerFd != -1);
2126 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2128 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2130 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2131 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2132 != sizeof(struct VolumeDiskHeader)) {
2133 Log("Couldn't rewrite volume header file!\n");
2140 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2141 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2145 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2146 int check, int *deleteMe)
2149 VolumeDiskData volumeInfo;
2150 struct versionStamp fileHeader;
2159 #ifndef AFS_NAMEI_ENV
2160 if ( sp->inodeType == VI_LINKTABLE)
2163 if (*(sp->inode) == 0) {
2165 Log("Missing inode in volume header (%s)\n", sp->description);
2168 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2169 sp->description, (Testing ? "it would have recreated it": "recreating"));
2171 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2172 isp->volumeId, INODESPECIAL,
2173 sp->inodeType, isp->RWvolumeId);
2174 if (!VALID_INO(*(sp->inode)))
2175 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2176 sp->description, errno);
2181 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2182 fdP = IH_OPEN(specH);
2183 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2184 /* bail out early and destroy the volume */
2185 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2187 if (deleteMe) *deleteMe = 1;
2192 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2193 sp->description, errno);
2196 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2197 || header.fileHeader.magic != sp->stamp.magic)) {
2199 Log("Part of the header (%s) is corrupted\n", sp->description);
2200 FDH_REALLYCLOSE(fdP);
2204 Log("Part of the header (%s) is corrupted; recreating\n",
2208 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2211 FDH_REALLYCLOSE(fdP);
2215 if (recreate && !Testing) {
2217 Abort("Internal error: recreating volume header (%s) in check mode\n",
2219 code = FDH_TRUNC(fdP, 0);
2221 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2222 sp->description, errno);
2224 /* The following code should be moved into vutil.c */
2225 if (sp->inodeType == VI_VOLINFO) {
2227 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2228 header.volumeInfo.stamp = sp->stamp;
2229 header.volumeInfo.id = isp->volumeId;
2230 header.volumeInfo.parentId = isp->RWvolumeId;
2231 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2232 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2233 header.volumeInfo.inService = 0;
2234 header.volumeInfo.blessed = 0;
2235 /* The + 1000 is a hack in case there are any files out in venus caches */
2236 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2237 header.volumeInfo.type =
2238 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2239 header.volumeInfo.needsCallback = 0;
2240 gettimeofday(&tp,0);
2241 header.volumeInfo.creationDate = tp.tv_sec;
2242 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2243 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2245 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2246 sizeof(header.volumeInfo));
2247 if (code != sizeof(header.volumeInfo)) {
2249 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2250 sp->description, errno);
2251 Abort("Unable to write entire volume header file (%s)\n",
2256 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2257 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2259 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2260 if (code != sizeof(sp->stamp)) {
2262 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2263 sp->description, errno);
2264 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2269 FDH_REALLYCLOSE(fdP);
2271 if (sp->inodeType == VI_VOLINFO) {
2272 VolInfo = header.volumeInfo;
2275 if (VolInfo.updateDate) {
2276 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2277 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2278 (Testing?"it would have been ":""), update);
2280 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2281 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2290 int SalvageVnodes(register struct InodeSummary *rwIsp,
2291 register struct InodeSummary * thisIsp,
2292 register struct ViceInodeInfo * inodes, int check)
2294 int ilarge, ismall, ioffset, RW, nInodes;
2295 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2296 if (Showmode) return 0;
2297 RW = (rwIsp == thisIsp);
2298 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2299 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2300 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2301 if (check && ismall == -1)
2303 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2304 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2305 return (ilarge==0 && ismall==0 ? 0: -1);
2308 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2309 register struct ViceInodeInfo *ip,
2310 int nInodes, struct VolumeSummary *volSummary, int check)
2312 VolumeId volumeNumber;
2313 char buf[SIZEOF_LARGEDISKVNODE];
2314 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2316 StreamHandle_t *file;
2317 struct VnodeClassInfo *vcp;
2319 int vnodeIndex, nVnodes;
2320 afs_ino_str_t stmp1, stmp2;
2324 volumeNumber = volSummary->header.id;
2325 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2326 fdP = IH_OPEN(handle);
2327 assert(fdP != NULL);
2328 file = FDH_FDOPEN(fdP, "r+");
2329 assert(file != NULL);
2330 vcp = &VnodeClassInfo[class];
2331 size = OS_SIZE(fdP->fd_fd);
2333 nVnodes = (size / vcp->diskSize) - 1;
2335 assert((nVnodes+1) * vcp->diskSize == size);
2336 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2341 for (vnodeIndex = 0;
2342 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2343 nVnodes--, vnodeIndex++) {
2344 if (vnode->type != vNull) {
2345 int vnodeChanged = 0;
2346 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2347 /* Log programs that belong to root (potentially suid root);
2348 don't bother for read-only or backup volumes */
2349 #ifdef notdef /* This is done elsewhere */
2350 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2351 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2352 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2353 vnode->owner, vnode->modeBits);
2355 if (VNDISK_GET_INO(vnode) == 0) {
2357 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2358 memset(vnode, 0, vcp->diskSize);
2363 if (vcp->magic != vnode->vnodeMagic) {
2364 /* bad magic #, probably partially created vnode */
2365 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2366 memset(vnode, 0, vcp->diskSize);
2370 /* ****** Should do a bit more salvage here: e.g. make sure
2371 vnode type matches what it should be given the index */
2372 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2373 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2374 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2375 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2382 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2383 /* The following doesn't work, because the version number
2384 is not maintained correctly by the file server */
2385 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2386 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2388 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2395 /* For RW volume, look for vnode with matching inode number;
2396 if no such match, take the first determined by our sort
2398 register struct ViceInodeInfo *lip = ip;
2399 register lnInodes = nInodes;
2400 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2401 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2410 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2411 /* "Matching" inode */
2415 vu = vnode->uniquifier;
2416 iu = ip->u.vnode.vnodeUniquifier;
2417 vd = vnode->dataVersion;
2418 id = ip->u.vnode.inodeDataVersion;
2420 * Because of the possibility of the uniquifier overflows (> 4M)
2421 * we compare them modulo the low 22-bits; we shouldn't worry
2422 * about mismatching since they shouldn't to many old
2423 * uniquifiers of the same vnode...
2425 if (IUnique(vu) != IUnique(iu)) {
2427 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2428 vnodeNumber, IUnique(vu), IUnique(iu));
2431 vnode->uniquifier = iu;
2432 #ifdef AFS_3DISPARES
2433 vnode->dataVersion = (id >= vd ?
2434 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2435 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2437 #if defined(AFS_SGI_EXMAG)
2438 vnode->dataVersion = (id >= vd ?
2439 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2440 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2442 vnode->dataVersion = (id>vd ? id:vd);
2443 #endif /* AFS_SGI_EXMAG */
2444 #endif /* AFS_3DISPARES */
2448 /* don't bother checking for vd > id any more, since
2449 partial file transfers always result in this state,
2450 and you can't do much else anyway (you've already
2451 found the best data you can) */
2452 #ifdef AFS_3DISPARES
2453 if (!vnodeIsDirectory(vnodeNumber) &&
2454 ((vd < id && (id-vd) < 1887437) ||
2455 ((vd > id && (vd-id) > 1887437)))) {
2457 #if defined(AFS_SGI_EXMAG)
2458 if (!vnodeIsDirectory(vnodeNumber) &&
2459 ((vd < id && (id-vd) < 15099494) ||
2460 ((vd > id && (vd-id) > 15099494)))) {
2462 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2463 #endif /* AFS_SGI_EXMAG */
2465 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2466 vnode->dataVersion = id;
2471 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2474 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2476 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2477 PrintInode(stmp2, ip->inodeNumber),
2480 VNDISK_SET_INO(vnode, ip->inodeNumber);
2485 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2487 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2488 PrintInode(stmp2, ip->inodeNumber),
2491 VNDISK_SET_INO(vnode, ip->inodeNumber);
2494 if (ip->byteCount != vnode->length) {
2496 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2497 vnodeNumber, vnode->length, ip->byteCount);
2501 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2502 vnodeNumber, vnode->length, ip->byteCount);
2503 vnode->length = ip->byteCount;
2507 ip->linkCount--; /* Keep the inode around */
2511 else { /* no matching inode */
2512 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2513 /* No matching inode--get rid of the vnode */
2515 if (VNDISK_GET_INO(vnode)) {
2517 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2518 vnodeNumber, vnode->uniquifier,
2519 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2522 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2527 if (VNDISK_GET_INO(vnode)) {
2529 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2530 vnodeNumber, vnode->uniquifier,
2531 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2532 ctime((time_t *)&(vnode->serverModifyTime)));
2535 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)));
2537 memset(vnode, 0, vcp->diskSize);
2540 /* Should not reach here becuase we checked for
2541 * (inodeNumber == 0) above. And where we zero the vnode,
2542 * we also goto vnodeDone.
2546 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2550 } /* VNDISK_GET_INO(vnode) != 0 */
2552 assert(!(vnodeChanged && check));
2553 if (vnodeChanged && !Testing) {
2554 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2555 (char*)vnode, vcp->diskSize)
2557 VolumeChanged = 1; /* For break call back */
2568 struct VnodeEssence *CheckVnodeNumber(VnodeId vnodeNumber)
2571 struct VnodeInfo *vip;
2574 class = vnodeIdToClass(vnodeNumber);
2575 vip = &vnodeInfo[class];
2576 offset = vnodeIdToBitNumber(vnodeNumber);
2577 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2580 void CopyOnWrite(register struct DirSummary *dir)
2582 /* Copy the directory unconditionally if we are going to change it:
2583 * not just if was cloned.
2585 struct VnodeDiskObject vnode;
2586 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2587 Inode oldinode, newinode;
2590 if (dir->copied || Testing)
2592 DFlush(); /* Well justified paranoia... */
2594 code = IH_IREAD(vnodeInfo[vLarge].handle,
2595 vnodeIndexOffset(vcp, dir->vnodeNumber),
2596 (char*)&vnode, sizeof (vnode));
2597 assert(code == sizeof(vnode));
2598 oldinode = VNDISK_GET_INO(&vnode);
2599 /* Increment the version number by a whole lot to avoid problems with
2600 * clients that were promised new version numbers--but the file server
2601 * crashed before the versions were written to disk.
2603 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2604 dir->rwVid, dir->vnodeNumber,
2605 vnode.uniquifier, vnode.dataVersion += 200);
2606 assert(VALID_INO(newinode));
2607 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2609 VNDISK_SET_INO(&vnode, newinode);
2610 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2611 vnodeIndexOffset(vcp, dir->vnodeNumber),
2612 (char*)&vnode, sizeof (vnode));
2613 assert(code == sizeof (vnode));
2615 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2616 fileSysDevice, newinode);
2617 /* Don't delete the original inode right away, because the directory is
2618 * still being scanned.
2624 * This function should either successfully create a new dir, or give up
2625 * and leave things the way they were. In particular, if it fails to write
2626 * the new dir properly, it should return w/o changing the reference to the
2629 void CopyAndSalvage(register struct DirSummary *dir)
2631 struct VnodeDiskObject vnode;
2632 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2633 Inode oldinode, newinode;
2635 register afs_int32 code;
2636 afs_int32 parentUnique= 1;
2637 struct VnodeEssence *vnodeEssence;
2641 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2642 code = IH_IREAD(vnodeInfo[vLarge].handle,
2643 vnodeIndexOffset(vcp, dir->vnodeNumber),
2644 (char*)&vnode, sizeof (vnode));
2645 assert(code == sizeof (vnode));
2646 oldinode = VNDISK_GET_INO(&vnode);
2647 /* Increment the version number by a whole lot to avoid problems with
2648 * clients that were promised new version numbers--but the file server
2649 * crashed before the versions were written to disk.
2651 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2652 dir->rwVid, dir->vnodeNumber,
2654 vnode.dataVersion += 200);
2655 assert(VALID_INO(newinode));
2656 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2658 /* Assign . and .. vnode numbers from dir and vnode.parent.
2659 * The uniquifier for . is in the vnode.
2660 * The uniquifier for .. might be set to a bogus value of 1 and
2661 * the salvager will later clean it up.
2663 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2664 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2666 code = DirSalvage(&dir->dirHandle, &newdir,
2667 dir->vnodeNumber, vnode.uniquifier,
2668 (vnode.parent?vnode.parent:dir->vnodeNumber),
2670 if (code == 0) code = DFlush();
2672 /* didn't really build the new directory properly, let's just give up. */
2673 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2675 Log("Directory salvage returned code %d, continuing.\n", code);
2678 Log("Checking the results of the directory salvage...\n");
2679 if (!DirOK(&newdir)) {
2680 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2681 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2686 VNDISK_SET_INO(&vnode, newinode);
2687 vnode.length = Length(&newdir);
2688 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2689 vnodeIndexOffset(vcp, dir->vnodeNumber),
2690 (char*)&vnode, sizeof (vnode));
2691 assert(code == sizeof (vnode));
2693 nt_sync(fileSysDevice);
2695 sync(); /* this is slow, but hopefully rarely called. We don't have
2696 * an open FD on the file itself to fsync.
2699 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2701 dir->dirHandle = newdir;
2704 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2707 struct VnodeEssence *vnodeEssence;
2708 afs_int32 dirOrphaned, todelete;
2710 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2712 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2713 if (vnodeEssence == NULL) {
2715 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2716 dir->vnodeNumber, dir->name?dir->name:"??",
2717 name, vnodeNumber, unique);
2721 assert(Delete(&dir->dirHandle, name) == 0);
2727 #ifndef AFS_NAMEI_ENV
2728 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2729 * mount inode for the partition. If this inode were deleted, it would crash
2732 if (vnodeEssence->InodeNumber == 0) {
2733 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2734 dir->vnodeNumber, (dir->name?dir->name:"??"),
2735 name, vnodeNumber, unique,
2736 (Testing?"-- would have deleted":" -- deleted"));
2739 assert(Delete(&dir->dirHandle, name) == 0);
2746 if (!(vnodeNumber & 1) && !Showmode &&
2747 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2748 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2749 dir->vnodeNumber, (dir->name?dir->name:"??"),
2750 name, vnodeNumber, unique,
2751 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2755 assert(Delete(&dir->dirHandle, name) == 0);
2761 /* Check if the Uniquifiers match. If not, change the directory entry
2762 * so its unique matches the vnode unique. Delete if the unique is zero
2763 * or if the directory is orphaned.
2765 if (!vnodeEssence->unique ||
2766 (vnodeEssence->unique) != unique) {
2767 if (!vnodeEssence->unique &&
2768 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2769 /* This is an orphaned directory. Don't delete the . or ..
2770 * entry. Otherwise, it will get created in the next
2771 * salvage and deleted again here. So Just skip it.
2776 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2779 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2780 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2781 name, vnodeNumber, unique, vnodeEssence->unique,
2782 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2786 fid.Vnode = vnodeNumber;
2787 fid.Unique = vnodeEssence->unique;
2789 assert(Delete(&dir->dirHandle, name) == 0);
2791 assert(Create(&dir->dirHandle, name, &fid) == 0);
2793 if (todelete) return; /* no need to continue */
2796 if (strcmp(name,".") == 0) {
2797 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2799 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2800 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2803 assert(Delete(&dir->dirHandle, ".") == 0);
2804 fid.Vnode = dir->vnodeNumber;
2805 fid.Unique = dir->unique;
2806 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2809 vnodeNumber = fid.Vnode; /* Get the new Essence */
2810 unique = fid.Unique;
2811 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2815 else if (strcmp(name,"..") == 0) {
2818 struct VnodeEssence *dotdot;
2819 pa.Vnode = dir->parent;
2820 dotdot = CheckVnodeNumber(pa.Vnode);
2821 assert (dotdot != NULL); /* XXX Should not be assert */
2822 pa.Unique = dotdot->unique;
2825 pa.Vnode = dir->vnodeNumber;
2826 pa.Unique = dir->unique;
2828 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2829 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2830 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2833 assert(Delete(&dir->dirHandle, "..") == 0);
2834 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2837 vnodeNumber = pa.Vnode; /* Get the new Essence */
2839 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2841 dir->haveDotDot = 1;
2842 } else if (strncmp(name,".__afs",6) == 0) {
2844 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2845 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2849 assert(Delete(&dir->dirHandle, name) == 0);
2851 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2852 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2856 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2857 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2858 vnodeEssence->owner, vnodeEssence->group,
2859 vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber,
2861 if (ShowMounts && (vnodeEssence->type == vSymlink) &&
2862 !(vnodeEssence->modeBits & 0111)) {
2868 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2869 vnodeEssence->InodeNumber);
2871 assert(fdP != NULL);
2872 size = FDH_SIZE(fdP);
2874 memset(buf, 0, 1024);
2875 if (size > 1024) size = 1024;
2876 code = FDH_READ(fdP, buf, size);
2877 assert(code == size);
2878 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2879 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2880 dir->name?dir->name:"??", name, buf);
2881 FDH_REALLYCLOSE(fdP);
2884 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2885 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2886 vnodeEssence->owner, vnodeEssence->group,
2887 vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber,
2889 if (vnodeIdToClass(vnodeNumber) == vLarge &&
2890 vnodeEssence->name == NULL) {
2892 if (n = (char*)malloc(strlen(name)+1))
2894 vnodeEssence->name = n;
2897 /* The directory entry points to the vnode. Check to see if the
2898 * vnode points back to the directory. If not, then let the
2899 * directory claim it (else it might end up orphaned). Vnodes
2900 * already claimed by another directory are deleted from this
2901 * directory: hardlinks to the same vnode are not allowed
2902 * from different directories.
2904 if (vnodeEssence->parent != dir->vnodeNumber) {
2905 if (!vnodeEssence->claimed && !dirOrphaned) {
2906 /* Vnode does not point back to this directory.
2907 * Orphaned dirs cannot claim a file (it may belong to
2908 * another non-orphaned dir).
2911 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2912 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2913 vnodeNumber, unique, (Testing?"would have been ":""),
2914 vnodeEssence->parent, dir->vnodeNumber);
2916 vnodeEssence->parent = dir->vnodeNumber;
2917 vnodeEssence->changed = 1;
2919 /* Vnode was claimed by another directory */
2922 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2923 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2924 vnodeEssence->parent, vnodeNumber, unique,
2925 (Testing?"would have been ":""));
2927 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2928 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2929 vnodeEssence->parent, vnodeNumber, unique,
2930 (Testing?"would have been ":""));
2935 assert(Delete(&dir->dirHandle, name) == 0);
2940 /* This directory claims the vnode */
2941 vnodeEssence->claimed = 1;
2943 vnodeEssence->count--;
2946 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2949 register struct VnodeInfo *vip = &vnodeInfo[class];
2950 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2951 char buf[SIZEOF_LARGEDISKVNODE];
2952 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2954 StreamHandle_t *file;
2959 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2960 fdP = IH_OPEN(vip->handle);
2961 assert(fdP != NULL);
2962 file = FDH_FDOPEN(fdP, "r+");
2963 assert(file != NULL);
2964 size = OS_SIZE(fdP->fd_fd);
2966 vip->nVnodes = (size / vcp->diskSize) - 1;
2967 if (vip->nVnodes > 0) {
2968 assert((vip->nVnodes+1)*vcp->diskSize == size);
2969 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2970 assert((vip->vnodes = (struct VnodeEssence *)
2971 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2972 if (class == vLarge) {
2973 assert((vip->inodes = (Inode *)
2974 calloc(vip->nVnodes, sizeof (Inode))) != NULL);
2985 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2986 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2987 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2988 nVnodes--, vnodeIndex++) {
2989 if (vnode->type != vNull) {
2990 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2991 vip->nAllocatedVnodes++;
2992 vep->count = vnode->linkCount;
2993 vep->blockCount = nBlocks(vnode->length);
2994 vip->volumeBlockCount += vep->blockCount;
2995 vep->parent = vnode->parent;
2996 vep->unique = vnode->uniquifier;
2997 if (*maxu < vnode->uniquifier)
2998 *maxu = vnode->uniquifier;
2999 vep->modeBits = vnode->modeBits;
3000 vep->InodeNumber = VNDISK_GET_INO(vnode);
3001 vep->type = vnode->type;
3002 vep->author = vnode->author;
3003 vep->owner = vnode->owner;
3004 vep->group = vnode->group;
3005 if (vnode->type == vDirectory) {
3006 assert(class == vLarge);
3007 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3015 static char *GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3017 struct VnodeEssence *parentvp;
3023 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3025 strcat(path, vp->name);
3031 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3032 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3034 static int IsVnodeOrphaned(VnodeId vnode)
3036 struct VnodeEssence *vep;
3038 if (vnode == 0) return(1); /* Vnode zero does not exist */
3039 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3040 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3041 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3043 return( IsVnodeOrphaned(vep->parent) );
3046 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3047 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3050 static struct DirSummary dir;
3051 static struct DirHandle dirHandle;
3052 struct VnodeEssence *parent;
3053 static char path[MAXPATHLEN];
3056 if (dirVnodeInfo->vnodes[i].salvaged)
3057 return; /* already salvaged */
3060 dirVnodeInfo->vnodes[i].salvaged = 1;
3062 if (dirVnodeInfo->inodes[i] == 0)
3063 return; /* Not allocated to a directory */
3065 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3066 if (parent && parent->salvaged == 0)
3067 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3068 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3069 rootdir, rootdirfound);
3070 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3071 dir.unique = dirVnodeInfo->vnodes[i].unique;
3074 dir.parent = dirVnodeInfo->vnodes[i].parent;
3075 dir.haveDot = dir.haveDotDot = 0;
3076 dir.ds_linkH = alinkH;
3077 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3079 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3082 Log("Directory bad, vnode %d; %s...\n",
3083 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3086 CopyAndSalvage(&dir);
3090 dirHandle = dir.dirHandle;
3092 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3095 /* If enumeration failed for random reasons, we will probably delete
3096 * too much stuff, so we guard against this instead.
3098 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3101 /* Delete the old directory if it was copied in order to salvage.
3102 * CopyOnWrite has written the new inode # to the disk, but we still
3103 * have the old one in our local structure here. Thus, we idec the
3107 if (dir.copied && !Testing) {
3108 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3110 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3113 /* Remember rootdir DirSummary _after_ it has been judged */
3114 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3115 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3122 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3124 /* This routine, for now, will only be called for read-write volumes */
3126 int BlocksInVolume = 0, FilesInVolume = 0;
3127 register VnodeClass class;
3128 struct DirSummary rootdir, oldrootdir;
3129 struct VnodeInfo *dirVnodeInfo;
3130 struct VnodeDiskObject vnode;
3131 VolumeDiskData volHeader;
3133 int orphaned, rootdirfound = 0;
3134 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3135 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3136 struct VnodeEssence *vep;
3141 VnodeId LFVnode, ThisVnode;
3142 Unique LFUnique, ThisUnique;
3145 vid = rwIsp->volSummary->header.id;
3146 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3147 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3148 assert(nBytes == sizeof(volHeader));
3149 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3150 assert (volHeader.destroyMe != DESTROY_ME);
3151 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3153 DistilVnodeEssence(vid, vLarge,
3154 rwIsp->volSummary->header.largeVnodeIndex,
3156 DistilVnodeEssence(vid, vSmall,
3157 rwIsp->volSummary->header.smallVnodeIndex,
3160 dirVnodeInfo = &vnodeInfo[vLarge];
3161 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3162 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3163 &rootdir, &rootdirfound);
3170 /* Parse each vnode looking for orphaned vnodes and
3171 * connect them to the tree as orphaned (if requested).
3173 oldrootdir = rootdir;
3174 for (class=0; class < nVNODECLASSES; class++) {
3175 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3176 vep = &(vnodeInfo[class].vnodes[v]);
3177 ThisVnode = bitNumberToVnodeNumber(v, class);
3178 ThisUnique = vep->unique;
3180 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3181 continue; /* Ignore unused, claimed, and root vnodes */
3183 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3184 * entry in this vnode had incremented the parent link count (In
3185 * JudgeEntry()). We need to go to the parent and decrement that
3186 * link count. But if the parent's unique is zero, then the parent
3187 * link count was not incremented in JudgeEntry().
3189 if (class == vLarge) { /* directory vnode */
3190 pv = vnodeIdToBitNumber(vep->parent);
3191 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3192 vnodeInfo[vLarge].vnodes[pv].count++;
3196 continue; /* If no rootdir, can't attach orphaned files */
3198 /* Here we attach orphaned files and directories into the
3199 * root directory, LVVnode, making sure link counts stay correct.
3201 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3202 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3203 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3205 /* Update this orphaned vnode's info. Its parent info and
3206 * link count (do for orphaned directories and files).
3208 vep->parent = LFVnode; /* Parent is the root dir */
3209 vep->unique = LFUnique;
3212 vep->count--; /* Inc link count (root dir will pt to it) */
3214 /* If this orphaned vnode is a directory, change '..'.
3215 * The name of the orphaned dir/file is unknown, so we
3216 * build a unique name. No need to CopyOnWrite the directory
3217 * since it is not connected to tree in BK or RO volume and
3218 * won't be visible there.
3220 if (class == vLarge) {
3224 /* Remove and recreate the ".." entry in this orphaned directory */
3225 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3227 pa.Unique = LFUnique;
3228 assert(Delete(&dh, "..") == 0);
3229 assert(Create(&dh, "..", &pa) == 0);
3231 /* The original parent's link count was decremented above.
3232 * Here we increment the new parent's link count.
3234 pv = vnodeIdToBitNumber(LFVnode);
3235 vnodeInfo[vLarge].vnodes[pv].count--;
3239 /* Go to the root dir and add this entry. The link count of the
3240 * root dir was incremented when ".." was created. Try 10 times.
3242 for (j=0; j<10; j++) {
3243 pa.Vnode = ThisVnode;
3244 pa.Unique = ThisUnique;
3246 sprintf(npath, "%s.%d.%d",
3247 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3248 ThisVnode, ThisUnique);
3250 CopyOnWrite(&rootdir);
3251 code = Create(&rootdir.dirHandle, npath, &pa);
3254 ThisUnique += 50; /* Try creating a different file */
3257 Log("Attaching orphaned %s to volume's root dir as %s\n",
3258 ((class == vLarge)?"directory":"file"), npath);
3260 } /* for each vnode in the class */
3261 } /* for each class of vnode */
3263 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3265 if (!oldrootdir.copied && rootdir.copied) {
3266 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3268 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3271 DFlush(); /* Flush the changes */
3272 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3273 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3274 orphans = ORPH_IGNORE;
3277 /* Write out all changed vnodes. Orphaned files and directories
3278 * will get removed here also (if requested).
3280 for (class = 0; class < nVNODECLASSES; class++){
3281 int nVnodes = vnodeInfo[class].nVnodes;
3282 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3283 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3284 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3285 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3286 for (i = 0; i<nVnodes; i++) {
3287 register struct VnodeEssence *vnp = &vnodes[i];
3288 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3290 /* If the vnode is good but is unclaimed (not listed in
3291 * any directory entries), then it is orphaned.
3294 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3295 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3299 if (vnp->changed || vnp->count) {
3302 nBytes = IH_IREAD(vnodeInfo[class].handle,
3303 vnodeIndexOffset(vcp, vnodeNumber),
3304 (char*)&vnode, sizeof (vnode));
3305 assert(nBytes == sizeof(vnode));
3307 vnode.parent = vnp->parent;
3308 oldCount = vnode.linkCount;
3309 vnode.linkCount = vnode.linkCount - vnp->count;
3312 orphaned = IsVnodeOrphaned(vnodeNumber);
3314 if (!vnp->todelete) {
3315 /* Orphans should have already been attached (if requested) */
3316 assert(orphans != ORPH_ATTACH);
3317 oblocks += vnp->blockCount;
3320 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3321 BlocksInVolume -= vnp->blockCount;
3323 if (VNDISK_GET_INO(&vnode)) {
3324 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3327 memset(&vnode, 0, sizeof(vnode));
3329 } else if (vnp->count) {
3331 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3332 vnodeNumber, oldCount,
3333 (Testing?"would have changed to":"now"), vnode.linkCount);
3337 vnode.dataVersion++;
3339 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3340 vnodeIndexOffset(vcp, vnodeNumber),
3341 (char*)&vnode, sizeof (vnode));
3342 assert(nBytes == sizeof(vnode));
3348 if (!Showmode && ofiles) {
3349 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3350 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3354 for (class = 0; class < nVNODECLASSES; class++) {
3355 register struct VnodeInfo *vip = &vnodeInfo[class];
3356 for (i=0; i<vip->nVnodes; i++)
3357 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3358 if (vip->vnodes) free(vip->vnodes);
3359 if (vip->inodes) free(vip->inodes);
3362 /* Set correct resource utilization statistics */
3363 volHeader.filecount = FilesInVolume;
3364 volHeader.diskused = BlocksInVolume;
3366 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3367 if (volHeader.uniquifier < (maxunique + 1)) {
3368 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3369 /* Plus 2,000 in case there are workstations out there with
3370 * cached vnodes that have since been deleted
3372 volHeader.uniquifier = (maxunique + 1 + 2000);
3375 /* Turn off the inUse bit; the volume's been salvaged! */
3376 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3377 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3378 volHeader.inService = 1; /* allow service again */
3379 volHeader.needsCallback = (VolumeChanged != 0);
3380 volHeader.dontSalvage = DONT_SALVAGE;
3383 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3384 assert(nBytes == sizeof(volHeader));
3387 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3388 (Testing?"It would have ":""), volHeader.name,
3389 volHeader.id, FilesInVolume, BlocksInVolume);
3391 IH_RELEASE(vnodeInfo[vSmall].handle);
3392 IH_RELEASE(vnodeInfo[vLarge].handle);
3397 void ClearROInUseBit(struct VolumeSummary *summary)
3399 IHandle_t *h = summary->volumeInfoHandle;
3402 VolumeDiskData volHeader;
3404 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3405 assert(nBytes == sizeof(volHeader));
3406 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3407 volHeader.inUse = 0;
3408 volHeader.needsSalvaged = 0;
3409 volHeader.inService = 1;
3410 volHeader.dontSalvage = DONT_SALVAGE;
3412 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3413 assert(nBytes == sizeof(volHeader));
3418 * Possible delete the volume.
3420 * deleteMe - Always do so, only a partial volume.
3422 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3423 int deleteMe, int check)
3425 if (readOnly(isp) || deleteMe) {
3426 if (isp->volSummary && isp->volSummary->fileName) {
3428 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);
3429 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3431 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3432 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3435 unlink(isp->volSummary->fileName);
3439 Log("%s salvage was unsuccessful: read-write volume %u\n",
3440 message, isp->volumeId);
3441 Abort("Salvage of volume %u aborted\n",
3447 void AskOffline(VolumeId volumeId)
3449 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3450 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3451 Abort("Salvage aborted\n");
3455 void AskOnline(VolumeId volumeId, char *partition)
3457 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3458 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3459 volumeId, partition);
3463 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3465 /* Volume parameter is passed in case iopen is upgraded in future to
3466 * require a volume Id to be passed
3469 IHandle_t *srcH, *destH;
3470 FdHandle_t *srcFdP, *destFdP;
3473 IH_INIT(srcH, device, rwvolume, inode1);
3474 srcFdP = IH_OPEN(srcH);
3475 assert(srcFdP != NULL);
3476 IH_INIT(destH, device, rwvolume, inode2);
3477 destFdP = IH_OPEN(destH);
3479 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3480 assert(FDH_WRITE(destFdP, buf, n) == n);
3482 FDH_REALLYCLOSE(srcFdP);
3483 FDH_REALLYCLOSE(destFdP);
3489 void PrintInodeList(void)
3491 register struct ViceInodeInfo *ip;
3492 struct ViceInodeInfo *buf;
3496 assert(fstat(inodeFd, &status) == 0);
3497 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3498 assert(buf != NULL);
3499 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3500 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3501 for(ip = buf; nInodes--; ip++) {
3502 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3503 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3504 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3509 void PrintInodeSummary(void)
3512 struct InodeSummary *isp;
3514 for (i=0; i<nVolumesInInodeFile; i++) {
3515 isp = &inodeSummary[i];
3516 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3517 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3518 isp->nSpecialInodes, isp->maxUniquifier);
3522 void PrintVolumeSummary(void)
3525 struct VolumeSummary *vsp;
3527 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3528 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3536 assert(0); /* Fork is never executed in the NT code path */
3547 if (ShowLog) showlog();
3549 if (main_thread != pthread_self())
3550 pthread_exit((void*)code);
3558 int Wait(char *prog)
3562 pid = wait(&status);
3564 if (WCOREDUMP(status))
3565 Log("\"%s\" core dumped!\n", prog);
3566 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3571 static char *TimeStamp(time_t clock, int precision)
3574 static char timestamp[20];
3575 lt = localtime(&clock);
3577 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3579 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3583 void CheckLogFile(void)
3585 char oldSlvgLog[AFSDIR_PATH_MAX];
3587 #ifndef AFS_NT40_ENV
3594 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3595 strcat(oldSlvgLog, ".old");
3597 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3598 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3600 if (!logFile) { /* still nothing, use stdout */
3605 #ifndef AFS_NAMEI_ENV
3606 AFS_DEBUG_IOPS_LOG(logFile);
3611 #ifndef AFS_NT40_ENV
3612 void TimeStampLogFile(void)
3614 char stampSlvgLog[AFSDIR_PATH_MAX];
3619 lt = localtime(&now);
3620 sprintf(stampSlvgLog, "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3621 AFSDIR_SERVER_SLVGLOG_FILEPATH,
3622 lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday,
3623 lt->tm_hour, lt->tm_min, lt->tm_sec);
3625 /* try to link the logfile to a timestamped filename */
3626 /* if it fails, oh well, nothing we can do */
3627 link(AFSDIR_SERVER_SLVGLOG_FILEPATH, stampSlvgLog);
3635 #ifndef AFS_NT40_ENV
3637 printf("Can't show log since using syslog.\n");
3646 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3649 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3652 while (fgets(line, sizeof(line), logFile))
3658 void Log(a,b,c,d,e,f,g,h,i,j,k)
3659 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3663 #ifndef AFS_NT40_ENV
3666 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3670 gettimeofday(&now, 0);
3671 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3672 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3677 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3678 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3680 #ifndef AFS_NT40_ENV
3683 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3687 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3689 if (ShowLog) showlog();
3696 char *ToString(char *s)
3699 p = (char *) malloc(strlen(s)+1);
3706 /* Remove the FORCESALVAGE file */
3707 void RemoveTheForce(char *path)
3709 if (!Testing && ForceSalvage) {
3710 if (chdir(path) == 0)
3711 unlink("FORCESALVAGE");
3715 #ifndef AFS_AIX32_ENV
3717 * UseTheForceLuke - see if we can use the force
3719 int UseTheForceLuke(char *path)
3723 assert(chdir(path) != -1);
3725 return (stat("FORCESALVAGE", &force) == 0);
3729 * UseTheForceLuke - see if we can use the force
3732 * The VRMIX fsck will not muck with the filesystem it is supposedly
3733 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3734 * muck directly with the root inode, which is within the normal
3736 * ListViceInodes() has a side effect of setting ForceSalvage if
3737 * it detects a need, based on root inode examination.
3739 int UseTheForceLuke(char *path)
3742 return 0; /* sorry OB1 */
3747 /* NT support routines */
3749 static char execpathname[MAX_PATH];
3750 int nt_SalvagePartition(char *partName, int jobn)
3755 if (!*execpathname) {
3756 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3757 if (!n || n == 1023)
3760 job.cj_magic = SALVAGER_MAGIC;
3761 job.cj_number = jobn;
3762 (void) strcpy(job.cj_part, partName);
3763 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3768 int nt_SetupPartitionSalvage(void *datap, int len)
3770 childJob_t *jobp = (childJob_t*)datap;
3771 char logname[AFSDIR_PATH_MAX];
3773 if (len != sizeof(childJob_t))
3775 if (jobp->cj_magic != SALVAGER_MAGIC)
3780 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3782 logFile = fopen(logname, "w");
3783 if (!logFile) logFile = stdout;
3789 #endif /* AFS_NT40_ENV */