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("salvager", 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 afs_fsize_t vnodeLength;
2320 int vnodeIndex, nVnodes;
2321 afs_ino_str_t stmp1, stmp2;
2325 volumeNumber = volSummary->header.id;
2326 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2327 fdP = IH_OPEN(handle);
2328 assert(fdP != NULL);
2329 file = FDH_FDOPEN(fdP, "r+");
2330 assert(file != NULL);
2331 vcp = &VnodeClassInfo[class];
2332 size = OS_SIZE(fdP->fd_fd);
2334 nVnodes = (size / vcp->diskSize) - 1;
2336 assert((nVnodes+1) * vcp->diskSize == size);
2337 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2342 for (vnodeIndex = 0;
2343 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2344 nVnodes--, vnodeIndex++) {
2345 if (vnode->type != vNull) {
2346 int vnodeChanged = 0;
2347 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2348 /* Log programs that belong to root (potentially suid root);
2349 don't bother for read-only or backup volumes */
2350 #ifdef notdef /* This is done elsewhere */
2351 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2352 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2353 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2354 vnode->owner, vnode->modeBits);
2356 if (VNDISK_GET_INO(vnode) == 0) {
2358 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2359 memset(vnode, 0, vcp->diskSize);
2364 if (vcp->magic != vnode->vnodeMagic) {
2365 /* bad magic #, probably partially created vnode */
2366 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2367 memset(vnode, 0, vcp->diskSize);
2371 /* ****** Should do a bit more salvage here: e.g. make sure
2372 vnode type matches what it should be given the index */
2373 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2374 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2375 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2376 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2383 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2384 /* The following doesn't work, because the version number
2385 is not maintained correctly by the file server */
2386 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2387 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2389 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2396 /* For RW volume, look for vnode with matching inode number;
2397 if no such match, take the first determined by our sort
2399 register struct ViceInodeInfo *lip = ip;
2400 register lnInodes = nInodes;
2401 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2402 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2411 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2412 /* "Matching" inode */
2416 vu = vnode->uniquifier;
2417 iu = ip->u.vnode.vnodeUniquifier;
2418 vd = vnode->dataVersion;
2419 id = ip->u.vnode.inodeDataVersion;
2421 * Because of the possibility of the uniquifier overflows (> 4M)
2422 * we compare them modulo the low 22-bits; we shouldn't worry
2423 * about mismatching since they shouldn't to many old
2424 * uniquifiers of the same vnode...
2426 if (IUnique(vu) != IUnique(iu)) {
2428 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2429 vnodeNumber, IUnique(vu), IUnique(iu));
2432 vnode->uniquifier = iu;
2433 #ifdef AFS_3DISPARES
2434 vnode->dataVersion = (id >= vd ?
2435 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2436 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2438 #if defined(AFS_SGI_EXMAG)
2439 vnode->dataVersion = (id >= vd ?
2440 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2441 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2443 vnode->dataVersion = (id>vd ? id:vd);
2444 #endif /* AFS_SGI_EXMAG */
2445 #endif /* AFS_3DISPARES */
2449 /* don't bother checking for vd > id any more, since
2450 partial file transfers always result in this state,
2451 and you can't do much else anyway (you've already
2452 found the best data you can) */
2453 #ifdef AFS_3DISPARES
2454 if (!vnodeIsDirectory(vnodeNumber) &&
2455 ((vd < id && (id-vd) < 1887437) ||
2456 ((vd > id && (vd-id) > 1887437)))) {
2458 #if defined(AFS_SGI_EXMAG)
2459 if (!vnodeIsDirectory(vnodeNumber) &&
2460 ((vd < id && (id-vd) < 15099494) ||
2461 ((vd > id && (vd-id) > 15099494)))) {
2463 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2464 #endif /* AFS_SGI_EXMAG */
2466 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2467 vnode->dataVersion = id;
2472 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2475 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2477 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2478 PrintInode(stmp2, ip->inodeNumber),
2481 VNDISK_SET_INO(vnode, ip->inodeNumber);
2486 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2488 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2489 PrintInode(stmp2, ip->inodeNumber),
2492 VNDISK_SET_INO(vnode, ip->inodeNumber);
2495 VNDISK_GET_LEN(vnodeLength, vnode);
2496 if (ip->byteCount != vnodeLength) {
2498 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2499 vnodeNumber, vnodeLength, ip->byteCount);
2503 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2504 vnodeNumber, vnodeLength, ip->byteCount);
2505 VNDISK_SET_LEN(vnode, ip->byteCount);
2509 ip->linkCount--; /* Keep the inode around */
2513 else { /* no matching inode */
2514 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2515 /* No matching inode--get rid of the vnode */
2517 if (VNDISK_GET_INO(vnode)) {
2519 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2520 vnodeNumber, vnode->uniquifier,
2521 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2524 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2529 if (VNDISK_GET_INO(vnode)) {
2531 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2532 vnodeNumber, vnode->uniquifier,
2533 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2534 ctime((time_t *)&(vnode->serverModifyTime)));
2537 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)));
2539 memset(vnode, 0, vcp->diskSize);
2542 /* Should not reach here becuase we checked for
2543 * (inodeNumber == 0) above. And where we zero the vnode,
2544 * we also goto vnodeDone.
2548 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2552 } /* VNDISK_GET_INO(vnode) != 0 */
2554 assert(!(vnodeChanged && check));
2555 if (vnodeChanged && !Testing) {
2556 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2557 (char*)vnode, vcp->diskSize)
2559 VolumeChanged = 1; /* For break call back */
2570 struct VnodeEssence *CheckVnodeNumber(VnodeId vnodeNumber)
2573 struct VnodeInfo *vip;
2576 class = vnodeIdToClass(vnodeNumber);
2577 vip = &vnodeInfo[class];
2578 offset = vnodeIdToBitNumber(vnodeNumber);
2579 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2582 void CopyOnWrite(register struct DirSummary *dir)
2584 /* Copy the directory unconditionally if we are going to change it:
2585 * not just if was cloned.
2587 struct VnodeDiskObject vnode;
2588 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2589 Inode oldinode, newinode;
2592 if (dir->copied || Testing)
2594 DFlush(); /* Well justified paranoia... */
2596 code = IH_IREAD(vnodeInfo[vLarge].handle,
2597 vnodeIndexOffset(vcp, dir->vnodeNumber),
2598 (char*)&vnode, sizeof (vnode));
2599 assert(code == sizeof(vnode));
2600 oldinode = VNDISK_GET_INO(&vnode);
2601 /* Increment the version number by a whole lot to avoid problems with
2602 * clients that were promised new version numbers--but the file server
2603 * crashed before the versions were written to disk.
2605 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2606 dir->rwVid, dir->vnodeNumber,
2607 vnode.uniquifier, vnode.dataVersion += 200);
2608 assert(VALID_INO(newinode));
2609 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2611 VNDISK_SET_INO(&vnode, newinode);
2612 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2613 vnodeIndexOffset(vcp, dir->vnodeNumber),
2614 (char*)&vnode, sizeof (vnode));
2615 assert(code == sizeof (vnode));
2617 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2618 fileSysDevice, newinode);
2619 /* Don't delete the original inode right away, because the directory is
2620 * still being scanned.
2626 * This function should either successfully create a new dir, or give up
2627 * and leave things the way they were. In particular, if it fails to write
2628 * the new dir properly, it should return w/o changing the reference to the
2631 void CopyAndSalvage(register struct DirSummary *dir)
2633 struct VnodeDiskObject vnode;
2634 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2635 Inode oldinode, newinode;
2637 register afs_int32 code;
2638 afs_int32 parentUnique= 1;
2639 struct VnodeEssence *vnodeEssence;
2643 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2644 code = IH_IREAD(vnodeInfo[vLarge].handle,
2645 vnodeIndexOffset(vcp, dir->vnodeNumber),
2646 (char*)&vnode, sizeof (vnode));
2647 assert(code == sizeof (vnode));
2648 oldinode = VNDISK_GET_INO(&vnode);
2649 /* Increment the version number by a whole lot to avoid problems with
2650 * clients that were promised new version numbers--but the file server
2651 * crashed before the versions were written to disk.
2653 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2654 dir->rwVid, dir->vnodeNumber,
2656 vnode.dataVersion += 200);
2657 assert(VALID_INO(newinode));
2658 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2660 /* Assign . and .. vnode numbers from dir and vnode.parent.
2661 * The uniquifier for . is in the vnode.
2662 * The uniquifier for .. might be set to a bogus value of 1 and
2663 * the salvager will later clean it up.
2665 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2666 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2668 code = DirSalvage(&dir->dirHandle, &newdir,
2669 dir->vnodeNumber, vnode.uniquifier,
2670 (vnode.parent?vnode.parent:dir->vnodeNumber),
2672 if (code == 0) code = DFlush();
2674 /* didn't really build the new directory properly, let's just give up. */
2675 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2677 Log("Directory salvage returned code %d, continuing.\n", code);
2680 Log("Checking the results of the directory salvage...\n");
2681 if (!DirOK(&newdir)) {
2682 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2683 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2688 VNDISK_SET_INO(&vnode, newinode);
2689 VNDISK_SET_LEN(&vnode, Length(&newdir));
2690 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2691 vnodeIndexOffset(vcp, dir->vnodeNumber),
2692 (char*)&vnode, sizeof (vnode));
2693 assert(code == sizeof (vnode));
2695 nt_sync(fileSysDevice);
2697 sync(); /* this is slow, but hopefully rarely called. We don't have
2698 * an open FD on the file itself to fsync.
2701 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2703 dir->dirHandle = newdir;
2706 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2709 struct VnodeEssence *vnodeEssence;
2710 afs_int32 dirOrphaned, todelete;
2712 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2714 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2715 if (vnodeEssence == NULL) {
2717 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2718 dir->vnodeNumber, dir->name?dir->name:"??",
2719 name, vnodeNumber, unique);
2723 assert(Delete(&dir->dirHandle, name) == 0);
2729 #ifndef AFS_NAMEI_ENV
2730 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2731 * mount inode for the partition. If this inode were deleted, it would crash
2734 if (vnodeEssence->InodeNumber == 0) {
2735 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2736 dir->vnodeNumber, (dir->name?dir->name:"??"),
2737 name, vnodeNumber, unique,
2738 (Testing?"-- would have deleted":" -- deleted"));
2741 assert(Delete(&dir->dirHandle, name) == 0);
2748 if (!(vnodeNumber & 1) && !Showmode &&
2749 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2750 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2751 dir->vnodeNumber, (dir->name?dir->name:"??"),
2752 name, vnodeNumber, unique,
2753 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2757 assert(Delete(&dir->dirHandle, name) == 0);
2763 /* Check if the Uniquifiers match. If not, change the directory entry
2764 * so its unique matches the vnode unique. Delete if the unique is zero
2765 * or if the directory is orphaned.
2767 if (!vnodeEssence->unique ||
2768 (vnodeEssence->unique) != unique) {
2769 if (!vnodeEssence->unique &&
2770 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2771 /* This is an orphaned directory. Don't delete the . or ..
2772 * entry. Otherwise, it will get created in the next
2773 * salvage and deleted again here. So Just skip it.
2778 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2781 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2782 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2783 name, vnodeNumber, unique, vnodeEssence->unique,
2784 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2788 fid.Vnode = vnodeNumber;
2789 fid.Unique = vnodeEssence->unique;
2791 assert(Delete(&dir->dirHandle, name) == 0);
2793 assert(Create(&dir->dirHandle, name, &fid) == 0);
2795 if (todelete) return; /* no need to continue */
2798 if (strcmp(name,".") == 0) {
2799 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2801 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2802 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2805 assert(Delete(&dir->dirHandle, ".") == 0);
2806 fid.Vnode = dir->vnodeNumber;
2807 fid.Unique = dir->unique;
2808 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2811 vnodeNumber = fid.Vnode; /* Get the new Essence */
2812 unique = fid.Unique;
2813 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2817 else if (strcmp(name,"..") == 0) {
2820 struct VnodeEssence *dotdot;
2821 pa.Vnode = dir->parent;
2822 dotdot = CheckVnodeNumber(pa.Vnode);
2823 assert (dotdot != NULL); /* XXX Should not be assert */
2824 pa.Unique = dotdot->unique;
2827 pa.Vnode = dir->vnodeNumber;
2828 pa.Unique = dir->unique;
2830 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2831 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2832 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2835 assert(Delete(&dir->dirHandle, "..") == 0);
2836 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2839 vnodeNumber = pa.Vnode; /* Get the new Essence */
2841 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2843 dir->haveDotDot = 1;
2844 } else if (strncmp(name,".__afs",6) == 0) {
2846 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2847 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2851 assert(Delete(&dir->dirHandle, name) == 0);
2853 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2854 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2858 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2859 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2860 vnodeEssence->owner, vnodeEssence->group,
2861 vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber,
2863 if (ShowMounts && (vnodeEssence->type == vSymlink) &&
2864 !(vnodeEssence->modeBits & 0111)) {
2870 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2871 vnodeEssence->InodeNumber);
2873 assert(fdP != NULL);
2874 size = FDH_SIZE(fdP);
2876 memset(buf, 0, 1024);
2877 if (size > 1024) size = 1024;
2878 code = FDH_READ(fdP, buf, size);
2879 assert(code == size);
2880 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2881 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2882 dir->name?dir->name:"??", name, buf);
2883 FDH_REALLYCLOSE(fdP);
2886 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2887 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2888 vnodeEssence->owner, vnodeEssence->group,
2889 vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber,
2891 if (vnodeIdToClass(vnodeNumber) == vLarge &&
2892 vnodeEssence->name == NULL) {
2894 if (n = (char*)malloc(strlen(name)+1))
2896 vnodeEssence->name = n;
2899 /* The directory entry points to the vnode. Check to see if the
2900 * vnode points back to the directory. If not, then let the
2901 * directory claim it (else it might end up orphaned). Vnodes
2902 * already claimed by another directory are deleted from this
2903 * directory: hardlinks to the same vnode are not allowed
2904 * from different directories.
2906 if (vnodeEssence->parent != dir->vnodeNumber) {
2907 if (!vnodeEssence->claimed && !dirOrphaned) {
2908 /* Vnode does not point back to this directory.
2909 * Orphaned dirs cannot claim a file (it may belong to
2910 * another non-orphaned dir).
2913 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2914 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2915 vnodeNumber, unique, (Testing?"would have been ":""),
2916 vnodeEssence->parent, dir->vnodeNumber);
2918 vnodeEssence->parent = dir->vnodeNumber;
2919 vnodeEssence->changed = 1;
2921 /* Vnode was claimed by another directory */
2924 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2925 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2926 vnodeEssence->parent, vnodeNumber, unique,
2927 (Testing?"would have been ":""));
2929 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2930 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2931 vnodeEssence->parent, vnodeNumber, unique,
2932 (Testing?"would have been ":""));
2937 assert(Delete(&dir->dirHandle, name) == 0);
2942 /* This directory claims the vnode */
2943 vnodeEssence->claimed = 1;
2945 vnodeEssence->count--;
2948 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2951 register struct VnodeInfo *vip = &vnodeInfo[class];
2952 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2953 char buf[SIZEOF_LARGEDISKVNODE];
2954 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2956 StreamHandle_t *file;
2961 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2962 fdP = IH_OPEN(vip->handle);
2963 assert(fdP != NULL);
2964 file = FDH_FDOPEN(fdP, "r+");
2965 assert(file != NULL);
2966 size = OS_SIZE(fdP->fd_fd);
2968 vip->nVnodes = (size / vcp->diskSize) - 1;
2969 if (vip->nVnodes > 0) {
2970 assert((vip->nVnodes+1)*vcp->diskSize == size);
2971 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2972 assert((vip->vnodes = (struct VnodeEssence *)
2973 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2974 if (class == vLarge) {
2975 assert((vip->inodes = (Inode *)
2976 calloc(vip->nVnodes, sizeof (Inode))) != NULL);
2987 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2988 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2989 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2990 nVnodes--, vnodeIndex++) {
2991 if (vnode->type != vNull) {
2992 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2993 afs_fsize_t vnodeLength;
2994 vip->nAllocatedVnodes++;
2995 vep->count = vnode->linkCount;
2996 VNDISK_GET_LEN(vnodeLength, vnode);
2997 vep->blockCount = nBlocks(vnodeLength);
2998 vip->volumeBlockCount += vep->blockCount;
2999 vep->parent = vnode->parent;
3000 vep->unique = vnode->uniquifier;
3001 if (*maxu < vnode->uniquifier)
3002 *maxu = vnode->uniquifier;
3003 vep->modeBits = vnode->modeBits;
3004 vep->InodeNumber = VNDISK_GET_INO(vnode);
3005 vep->type = vnode->type;
3006 vep->author = vnode->author;
3007 vep->owner = vnode->owner;
3008 vep->group = vnode->group;
3009 if (vnode->type == vDirectory) {
3010 assert(class == vLarge);
3011 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3019 static char *GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3021 struct VnodeEssence *parentvp;
3027 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3029 strcat(path, vp->name);
3035 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3036 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3038 static int IsVnodeOrphaned(VnodeId vnode)
3040 struct VnodeEssence *vep;
3042 if (vnode == 0) return(1); /* Vnode zero does not exist */
3043 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3044 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3045 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3047 return( IsVnodeOrphaned(vep->parent) );
3050 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3051 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3054 static struct DirSummary dir;
3055 static struct DirHandle dirHandle;
3056 struct VnodeEssence *parent;
3057 static char path[MAXPATHLEN];
3060 if (dirVnodeInfo->vnodes[i].salvaged)
3061 return; /* already salvaged */
3064 dirVnodeInfo->vnodes[i].salvaged = 1;
3066 if (dirVnodeInfo->inodes[i] == 0)
3067 return; /* Not allocated to a directory */
3069 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3070 if (parent && parent->salvaged == 0)
3071 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3072 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3073 rootdir, rootdirfound);
3074 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3075 dir.unique = dirVnodeInfo->vnodes[i].unique;
3078 dir.parent = dirVnodeInfo->vnodes[i].parent;
3079 dir.haveDot = dir.haveDotDot = 0;
3080 dir.ds_linkH = alinkH;
3081 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3083 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3086 Log("Directory bad, vnode %d; %s...\n",
3087 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3090 CopyAndSalvage(&dir);
3094 dirHandle = dir.dirHandle;
3096 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3099 /* If enumeration failed for random reasons, we will probably delete
3100 * too much stuff, so we guard against this instead.
3102 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3105 /* Delete the old directory if it was copied in order to salvage.
3106 * CopyOnWrite has written the new inode # to the disk, but we still
3107 * have the old one in our local structure here. Thus, we idec the
3111 if (dir.copied && !Testing) {
3112 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3114 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3117 /* Remember rootdir DirSummary _after_ it has been judged */
3118 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3119 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3126 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3128 /* This routine, for now, will only be called for read-write volumes */
3130 int BlocksInVolume = 0, FilesInVolume = 0;
3131 register VnodeClass class;
3132 struct DirSummary rootdir, oldrootdir;
3133 struct VnodeInfo *dirVnodeInfo;
3134 struct VnodeDiskObject vnode;
3135 VolumeDiskData volHeader;
3137 int orphaned, rootdirfound = 0;
3138 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3139 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3140 struct VnodeEssence *vep;
3145 VnodeId LFVnode, ThisVnode;
3146 Unique LFUnique, ThisUnique;
3149 vid = rwIsp->volSummary->header.id;
3150 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3151 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3152 assert(nBytes == sizeof(volHeader));
3153 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3154 assert (volHeader.destroyMe != DESTROY_ME);
3155 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3157 DistilVnodeEssence(vid, vLarge,
3158 rwIsp->volSummary->header.largeVnodeIndex,
3160 DistilVnodeEssence(vid, vSmall,
3161 rwIsp->volSummary->header.smallVnodeIndex,
3164 dirVnodeInfo = &vnodeInfo[vLarge];
3165 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3166 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3167 &rootdir, &rootdirfound);
3174 /* Parse each vnode looking for orphaned vnodes and
3175 * connect them to the tree as orphaned (if requested).
3177 oldrootdir = rootdir;
3178 for (class=0; class < nVNODECLASSES; class++) {
3179 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3180 vep = &(vnodeInfo[class].vnodes[v]);
3181 ThisVnode = bitNumberToVnodeNumber(v, class);
3182 ThisUnique = vep->unique;
3184 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3185 continue; /* Ignore unused, claimed, and root vnodes */
3187 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3188 * entry in this vnode had incremented the parent link count (In
3189 * JudgeEntry()). We need to go to the parent and decrement that
3190 * link count. But if the parent's unique is zero, then the parent
3191 * link count was not incremented in JudgeEntry().
3193 if (class == vLarge) { /* directory vnode */
3194 pv = vnodeIdToBitNumber(vep->parent);
3195 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3196 vnodeInfo[vLarge].vnodes[pv].count++;
3200 continue; /* If no rootdir, can't attach orphaned files */
3202 /* Here we attach orphaned files and directories into the
3203 * root directory, LVVnode, making sure link counts stay correct.
3205 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3206 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3207 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3209 /* Update this orphaned vnode's info. Its parent info and
3210 * link count (do for orphaned directories and files).
3212 vep->parent = LFVnode; /* Parent is the root dir */
3213 vep->unique = LFUnique;
3216 vep->count--; /* Inc link count (root dir will pt to it) */
3218 /* If this orphaned vnode is a directory, change '..'.
3219 * The name of the orphaned dir/file is unknown, so we
3220 * build a unique name. No need to CopyOnWrite the directory
3221 * since it is not connected to tree in BK or RO volume and
3222 * won't be visible there.
3224 if (class == vLarge) {
3228 /* Remove and recreate the ".." entry in this orphaned directory */
3229 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3231 pa.Unique = LFUnique;
3232 assert(Delete(&dh, "..") == 0);
3233 assert(Create(&dh, "..", &pa) == 0);
3235 /* The original parent's link count was decremented above.
3236 * Here we increment the new parent's link count.
3238 pv = vnodeIdToBitNumber(LFVnode);
3239 vnodeInfo[vLarge].vnodes[pv].count--;
3243 /* Go to the root dir and add this entry. The link count of the
3244 * root dir was incremented when ".." was created. Try 10 times.
3246 for (j=0; j<10; j++) {
3247 pa.Vnode = ThisVnode;
3248 pa.Unique = ThisUnique;
3250 sprintf(npath, "%s.%d.%d",
3251 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3252 ThisVnode, ThisUnique);
3254 CopyOnWrite(&rootdir);
3255 code = Create(&rootdir.dirHandle, npath, &pa);
3258 ThisUnique += 50; /* Try creating a different file */
3261 Log("Attaching orphaned %s to volume's root dir as %s\n",
3262 ((class == vLarge)?"directory":"file"), npath);
3264 } /* for each vnode in the class */
3265 } /* for each class of vnode */
3267 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3269 if (!oldrootdir.copied && rootdir.copied) {
3270 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3272 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3275 DFlush(); /* Flush the changes */
3276 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3277 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3278 orphans = ORPH_IGNORE;
3281 /* Write out all changed vnodes. Orphaned files and directories
3282 * will get removed here also (if requested).
3284 for (class = 0; class < nVNODECLASSES; class++){
3285 int nVnodes = vnodeInfo[class].nVnodes;
3286 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3287 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3288 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3289 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3290 for (i = 0; i<nVnodes; i++) {
3291 register struct VnodeEssence *vnp = &vnodes[i];
3292 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3294 /* If the vnode is good but is unclaimed (not listed in
3295 * any directory entries), then it is orphaned.
3298 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3299 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3303 if (vnp->changed || vnp->count) {
3306 nBytes = IH_IREAD(vnodeInfo[class].handle,
3307 vnodeIndexOffset(vcp, vnodeNumber),
3308 (char*)&vnode, sizeof (vnode));
3309 assert(nBytes == sizeof(vnode));
3311 vnode.parent = vnp->parent;
3312 oldCount = vnode.linkCount;
3313 vnode.linkCount = vnode.linkCount - vnp->count;
3316 orphaned = IsVnodeOrphaned(vnodeNumber);
3318 if (!vnp->todelete) {
3319 /* Orphans should have already been attached (if requested) */
3320 assert(orphans != ORPH_ATTACH);
3321 oblocks += vnp->blockCount;
3324 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3325 BlocksInVolume -= vnp->blockCount;
3327 if (VNDISK_GET_INO(&vnode)) {
3328 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3331 memset(&vnode, 0, sizeof(vnode));
3333 } else if (vnp->count) {
3335 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3336 vnodeNumber, oldCount,
3337 (Testing?"would have changed to":"now"), vnode.linkCount);
3341 vnode.dataVersion++;
3343 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3344 vnodeIndexOffset(vcp, vnodeNumber),
3345 (char*)&vnode, sizeof (vnode));
3346 assert(nBytes == sizeof(vnode));
3352 if (!Showmode && ofiles) {
3353 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3354 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3358 for (class = 0; class < nVNODECLASSES; class++) {
3359 register struct VnodeInfo *vip = &vnodeInfo[class];
3360 for (i=0; i<vip->nVnodes; i++)
3361 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3362 if (vip->vnodes) free(vip->vnodes);
3363 if (vip->inodes) free(vip->inodes);
3366 /* Set correct resource utilization statistics */
3367 volHeader.filecount = FilesInVolume;
3368 volHeader.diskused = BlocksInVolume;
3370 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3371 if (volHeader.uniquifier < (maxunique + 1)) {
3372 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3373 /* Plus 2,000 in case there are workstations out there with
3374 * cached vnodes that have since been deleted
3376 volHeader.uniquifier = (maxunique + 1 + 2000);
3379 /* Turn off the inUse bit; the volume's been salvaged! */
3380 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3381 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3382 volHeader.inService = 1; /* allow service again */
3383 volHeader.needsCallback = (VolumeChanged != 0);
3384 volHeader.dontSalvage = DONT_SALVAGE;
3387 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3388 assert(nBytes == sizeof(volHeader));
3391 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3392 (Testing?"It would have ":""), volHeader.name,
3393 volHeader.id, FilesInVolume, BlocksInVolume);
3395 IH_RELEASE(vnodeInfo[vSmall].handle);
3396 IH_RELEASE(vnodeInfo[vLarge].handle);
3401 void ClearROInUseBit(struct VolumeSummary *summary)
3403 IHandle_t *h = summary->volumeInfoHandle;
3406 VolumeDiskData volHeader;
3408 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3409 assert(nBytes == sizeof(volHeader));
3410 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3411 volHeader.inUse = 0;
3412 volHeader.needsSalvaged = 0;
3413 volHeader.inService = 1;
3414 volHeader.dontSalvage = DONT_SALVAGE;
3416 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3417 assert(nBytes == sizeof(volHeader));
3422 * Possible delete the volume.
3424 * deleteMe - Always do so, only a partial volume.
3426 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3427 int deleteMe, int check)
3429 if (readOnly(isp) || deleteMe) {
3430 if (isp->volSummary && isp->volSummary->fileName) {
3432 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);
3433 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3435 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3436 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3439 unlink(isp->volSummary->fileName);
3443 Log("%s salvage was unsuccessful: read-write volume %u\n",
3444 message, isp->volumeId);
3445 Abort("Salvage of volume %u aborted\n",
3451 void AskOffline(VolumeId volumeId)
3453 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3454 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3455 Abort("Salvage aborted\n");
3459 void AskOnline(VolumeId volumeId, char *partition)
3461 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3462 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3463 volumeId, partition);
3467 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3469 /* Volume parameter is passed in case iopen is upgraded in future to
3470 * require a volume Id to be passed
3473 IHandle_t *srcH, *destH;
3474 FdHandle_t *srcFdP, *destFdP;
3477 IH_INIT(srcH, device, rwvolume, inode1);
3478 srcFdP = IH_OPEN(srcH);
3479 assert(srcFdP != NULL);
3480 IH_INIT(destH, device, rwvolume, inode2);
3481 destFdP = IH_OPEN(destH);
3483 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3484 assert(FDH_WRITE(destFdP, buf, n) == n);
3486 FDH_REALLYCLOSE(srcFdP);
3487 FDH_REALLYCLOSE(destFdP);
3493 void PrintInodeList(void)
3495 register struct ViceInodeInfo *ip;
3496 struct ViceInodeInfo *buf;
3500 assert(fstat(inodeFd, &status) == 0);
3501 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3502 assert(buf != NULL);
3503 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3504 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3505 for(ip = buf; nInodes--; ip++) {
3506 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3507 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3508 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3513 void PrintInodeSummary(void)
3516 struct InodeSummary *isp;
3518 for (i=0; i<nVolumesInInodeFile; i++) {
3519 isp = &inodeSummary[i];
3520 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3521 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3522 isp->nSpecialInodes, isp->maxUniquifier);
3526 void PrintVolumeSummary(void)
3529 struct VolumeSummary *vsp;
3531 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3532 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3540 assert(0); /* Fork is never executed in the NT code path */
3551 if (ShowLog) showlog();
3553 if (main_thread != pthread_self())
3554 pthread_exit((void*)code);
3562 int Wait(char *prog)
3566 pid = wait(&status);
3568 if (WCOREDUMP(status))
3569 Log("\"%s\" core dumped!\n", prog);
3570 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3575 static char *TimeStamp(time_t clock, int precision)
3578 static char timestamp[20];
3579 lt = localtime(&clock);
3581 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3583 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3587 void CheckLogFile(void)
3589 char oldSlvgLog[AFSDIR_PATH_MAX];
3591 #ifndef AFS_NT40_ENV
3598 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3599 strcat(oldSlvgLog, ".old");
3601 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3602 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3604 if (!logFile) { /* still nothing, use stdout */
3609 #ifndef AFS_NAMEI_ENV
3610 AFS_DEBUG_IOPS_LOG(logFile);
3615 #ifndef AFS_NT40_ENV
3616 void TimeStampLogFile(void)
3618 char stampSlvgLog[AFSDIR_PATH_MAX];
3623 lt = localtime(&now);
3624 sprintf(stampSlvgLog, "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3625 AFSDIR_SERVER_SLVGLOG_FILEPATH,
3626 lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday,
3627 lt->tm_hour, lt->tm_min, lt->tm_sec);
3629 /* try to link the logfile to a timestamped filename */
3630 /* if it fails, oh well, nothing we can do */
3631 link(AFSDIR_SERVER_SLVGLOG_FILEPATH, stampSlvgLog);
3639 #ifndef AFS_NT40_ENV
3641 printf("Can't show log since using syslog.\n");
3650 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3653 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3656 while (fgets(line, sizeof(line), logFile))
3662 void Log(a,b,c,d,e,f,g,h,i,j,k)
3663 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3667 #ifndef AFS_NT40_ENV
3670 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3674 gettimeofday(&now, 0);
3675 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3676 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3681 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3682 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3684 #ifndef AFS_NT40_ENV
3687 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3691 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3693 if (ShowLog) showlog();
3700 char *ToString(char *s)
3703 p = (char *) malloc(strlen(s)+1);
3710 /* Remove the FORCESALVAGE file */
3711 void RemoveTheForce(char *path)
3713 if (!Testing && ForceSalvage) {
3714 if (chdir(path) == 0)
3715 unlink("FORCESALVAGE");
3719 #ifndef AFS_AIX32_ENV
3721 * UseTheForceLuke - see if we can use the force
3723 int UseTheForceLuke(char *path)
3727 assert(chdir(path) != -1);
3729 return (stat("FORCESALVAGE", &force) == 0);
3733 * UseTheForceLuke - see if we can use the force
3736 * The VRMIX fsck will not muck with the filesystem it is supposedly
3737 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3738 * muck directly with the root inode, which is within the normal
3740 * ListViceInodes() has a side effect of setting ForceSalvage if
3741 * it detects a need, based on root inode examination.
3743 int UseTheForceLuke(char *path)
3746 return 0; /* sorry OB1 */
3751 /* NT support routines */
3753 static char execpathname[MAX_PATH];
3754 int nt_SalvagePartition(char *partName, int jobn)
3759 if (!*execpathname) {
3760 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3761 if (!n || n == 1023)
3764 job.cj_magic = SALVAGER_MAGIC;
3765 job.cj_number = jobn;
3766 (void) strcpy(job.cj_part, partName);
3767 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3772 int nt_SetupPartitionSalvage(void *datap, int len)
3774 childJob_t *jobp = (childJob_t*)datap;
3775 char logname[AFSDIR_PATH_MAX];
3777 if (len != sizeof(childJob_t))
3779 if (jobp->cj_magic != SALVAGER_MAGIC)
3784 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3786 logFile = fopen(logname, "w");
3787 if (!logFile) logFile = stdout;
3793 #endif /* AFS_NT40_ENV */