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 /*@printflike@*/ void Log(const char *format, ...);
395 /*@printflike@*/ void Abort(const char *format, ...);
398 int Wait(char *prog);
399 char * ToString(char *s);
400 void AskOffline(VolumeId volumeId);
401 void AskOnline(VolumeId volumeId, char *partition);
402 void CheckLogFile(void);
404 void TimeStampLogFile(void);
406 void ClearROInUseBit(struct VolumeSummary *summary);
407 void CopyAndSalvage(register struct DirSummary *dir);
408 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
409 void CopyOnWrite(register struct DirSummary *dir);
410 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
411 register struct InodeSummary * summary);
412 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
413 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
415 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
416 void GetVolumeSummary(VolumeId singleVolumeNumber);
417 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
419 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
422 void ObtainSalvageLock(void);
423 void PrintInodeList(void);
424 void PrintInodeSummary(void);
425 void PrintVolumeSummary(void);
426 int QuickCheck(register struct InodeSummary *isp, int nVols);
427 void RemoveTheForce(char *path);
428 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
429 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
431 void SalvageFileSysParallel(struct DiskPartition *partP);
432 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
433 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
434 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
435 int check, int *deleteMe);
436 int SalvageIndex(Inode ino, VnodeClass class, int RW,
437 register struct ViceInodeInfo *ip,
438 int nInodes, struct VolumeSummary *volSummary, int check);
439 int SalvageVnodes(register struct InodeSummary *rwIsp,
440 register struct InodeSummary * thisIsp,
441 register struct ViceInodeInfo * inodes, int check);
442 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
443 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
445 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
447 #define SalvageVolumeGroup DoSalvageVolumeGroup
449 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
450 register struct ViceInodeInfo *inodes,
451 int RW, int check, int *deleteMe);
453 int UseTheForceLuke(char *path);
455 static int IsVnodeOrphaned(VnodeId vnode);
457 /* Uniquifier stored in the Inode */
458 static Unique IUnique(Unique u)
461 return(u & 0x3fffff);
463 #if defined(AFS_SGI_EXMAG)
464 return(u & SGI_UNIQMASK);
467 #endif /* AFS_SGI_EXMAG */
471 static int BadError(register int aerror)
473 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
475 return 0; /* otherwise may be transient, e.g. EMFILE */
480 static int handleit(struct cmd_syndesc *as)
482 register struct cmd_item *ti;
483 char pname[100], *temp;
484 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0, i;
485 struct DiskPartition *partP;
487 #ifdef AFS_SGI_VNODE_GLUE
488 if (afs_init_kernel_config(-1) <0) {
489 printf("Can't determine NUMA configuration, not starting salvager.\n");
495 for (i = 0; i < CMD_MAXPARMS; i++) {
496 if (as->parms[i].items) {
502 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
511 #endif /* FAST_RESTART */
512 if ((ti = as->parms[0].items)) { /* -partition */
514 strncpy(pname, ti->data, 100);
516 if ((ti = as->parms[1].items)) { /* -volumeid */
518 printf("You must also specify '-partition' option with the '-volumeid' option\n");
522 vid = atoi(ti->data);
524 if (as->parms[2].items) /* -debug */
526 if (as->parms[3].items) /* -nowrite */
528 if (as->parms[4].items) /* -inodes */
530 if (as->parms[5].items) /* -force */
532 if (as->parms[6].items) /* -oktozap */
534 if (as->parms[7].items) /* -rootinodes */
536 if (as->parms[8].items) /* -RebuildDirs */
538 if (as->parms[9].items) /* -ForceReads */
540 if ((ti = as->parms[10].items)) {/* -Parallel # */
542 if (strncmp(temp,"all",3) == 0) {
546 if (strlen(temp) != 0) {
547 Parallel = atoi(temp);
548 if (Parallel < 1) Parallel = 1;
549 if (Parallel > MAXPARALLEL) {
550 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
551 Parallel = MAXPARALLEL;
555 if ((ti = as->parms[11].items)) {/* -tmpdir */
559 dirp = opendir(tmpdir);
561 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
566 if ((ti = as->parms[12].items)) /* -showlog */
568 if ((ti = as->parms[13].items)) { /* -log */
573 if ((ti = as->parms[14].items)) { /* -showmounts */
578 if ((ti = as->parms[15].items)) { /* -orphans */
580 orphans = ORPH_IGNORE;
581 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
582 orphans = ORPH_REMOVE;
583 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
584 orphans = ORPH_ATTACH;
587 #ifndef AFS_NT40_ENV /* ignore options on NT */
588 if ((ti = as->parms[16].items)) { /* -syslog */
592 if ((ti = as->parms[17].items)) { /* -syslogfacility */
593 useSyslogFacility = atoi(ti->data);
596 if ((ti = as->parms[18].items)) { /* -datelogs */
602 if (ti = as->parms[19].items) { /* -DontSalvage */
603 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
611 #endif /* FAST_RESTART */
613 /* Note: if seemvol we initialize this as a standard volume utility: this has the
614 implication that the file server may be running; negotations have to be made with
615 the file server in this case to take the read write volume and associated read-only
616 volumes off line before salvaging */
619 if (afs_winsockInit()<0) {
620 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
621 AFSDIR_SALVAGER_FILE, 0);
622 Log("Failed to initailize winsock, exiting.\n");
627 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
630 if (myjob.cj_number != NOT_CHILD) {
633 (void) strcpy(pname, myjob.cj_part);
638 for (partP = DiskPartitionList; partP; partP = partP->next) {
639 SalvageFileSysParallel(partP);
641 SalvageFileSysParallel(0);
644 partP = VGetPartition(pname, 0);
646 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
651 SalvageFileSys(partP, 0);
653 /* Salvage individual volume */
655 Log("salvage: invalid volume id specified; salvage aborted\n");
658 SalvageFileSys (partP, vid);
666 #include "AFS_component_version_number.c"
670 char *save_args[MAX_ARGS];
672 pthread_t main_thread;
675 int main(int argc, char **argv)
677 struct cmd_syndesc *ts;
679 char commandLine[150];
682 extern char cml_version_number[];
686 * The following signal action for AIX is necessary so that in case of a
687 * crash (i.e. core is generated) we can include the user's data section
688 * in the core dump. Unfortunately, by default, only a partial core is
689 * generated which, in many cases, isn't too useful.
691 struct sigaction nsa;
693 sigemptyset(&nsa.sa_mask);
694 nsa.sa_handler = SIG_DFL;
695 nsa.sa_flags = SA_FULLDUMP;
696 sigaction(SIGABRT, &nsa, NULL);
697 sigaction(SIGSEGV, &nsa, NULL);
700 /* Initialize directory paths */
701 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
703 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
705 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
709 main_thread = pthread_self();
710 if (spawnDatap && spawnDataLen) {
711 /* This is a child per partition salvager. Don't setup log or
712 * try to lock the salvager lock.
714 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
719 for (commandLine[0] = '\0', i=0; i<argc; i++) {
720 if (i > 0) strcat(commandLine, " ");
721 strcat(commandLine, argv[i]);
724 /* All entries to the log will be appended. Useful if there are
725 * multiple salvagers appending to the log.
730 #ifdef AFS_LINUX20_ENV
731 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
733 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
739 if (geteuid() != 0) {
740 printf("Salvager must be run as root.\n");
746 /* bad for normal help flag processing, but can do nada */
748 fprintf(logFile, "%s\n", cml_version_number);
749 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
751 /* Get and hold a lock for the duration of the salvage to make sure
752 * that no other salvage runs at the same time. The routine
753 * VInitVolumePackage (called below) makes sure that a file server or
754 * other volume utilities don't interfere with the salvage.
761 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
762 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
763 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
764 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
765 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
766 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
767 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
768 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
769 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
770 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
771 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
772 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
773 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
774 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
775 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
776 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
777 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
779 /* note - syslog isn't avail on NT, but if we make it conditional, have
780 to deal with screwy offsets for cmd params */
781 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
782 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
783 cmd_AddParm(ts, "-datelogs", CMD_FLAG, CMD_OPTIONAL, "Include timestamp in logfile filename");
786 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");
787 #endif /* FAST_RESTART */
788 err = cmd_Dispatch(argc, argv);
792 /* Get the salvage lock if not already held. Hold until process exits. */
793 void ObtainSalvageLock(void)
798 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
799 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
801 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
803 "salvager: There appears to be another salvager running! Aborted.\n");
807 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
808 assert(salvageLock >= 0);
809 #ifdef AFS_DARWIN_ENV
810 if (flock(salvageLock, LOCK_EX) == -1) {
812 if (lockf(salvageLock, F_LOCK, 0) == -1) {
815 "salvager: There appears to be another salvager running! Aborted.\n");
822 #ifdef AFS_SGI_XFS_IOPS_ENV
823 /* Check if the given partition is mounted. For XFS, the root inode is not a
824 * constant. So we check the hard way.
826 int IsPartitionMounted(char *part)
829 struct mntent *mntent;
831 assert(mntfp = setmntent(MOUNTED, "r"));
832 while (mntent = getmntent(mntfp)) {
833 if (!strcmp(part, mntent->mnt_dir))
838 return mntent ? 1 : 1;
841 /* Check if the given inode is the root of the filesystem. */
842 #ifndef AFS_SGI_XFS_IOPS_ENV
843 int IsRootInode(struct stat *status)
846 * The root inode is not a fixed value in XFS partitions. So we need to
847 * see if the partition is in the list of mounted partitions. This only
848 * affects the SalvageFileSys path, so we check there.
850 return (status->st_ino == ROOTINODE);
855 #ifndef AFS_NAMEI_ENV
856 /* We don't want to salvage big files filesystems, since we can't put volumes on
859 int CheckIfBigFilesFS(char *mountPoint, char *devName)
861 struct superblock fs;
864 if (strncmp(devName, "/dev/", 5)) {
865 (void) sprintf(name, "/dev/%s", devName);
868 (void) strcpy(name, devName);
871 if (ReadSuper(&fs, name)<0) {
872 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
875 if (IsBigFilesFileSystem(&fs)) {
876 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
885 #define HDSTR "\\Device\\Harddisk"
886 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
887 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
894 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
896 if (strncmp(res, HDSTR, HDLEN)) {
899 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
900 res, HDSTR, p1->devName);
904 d1 = atoi(&res[HDLEN]);
906 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
908 if (strncmp(res, HDSTR, HDLEN)) {
911 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
912 res, HDSTR, p2->devName);
916 d2 = atoi(&res[HDLEN]);
921 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
924 /* This assumes that two partitions with the same device number divided by
925 * PartsPerDisk are on the same disk.
927 void SalvageFileSysParallel(struct DiskPartition *partP)
930 struct DiskPartition *partP;
931 int pid; /* Pid for this job */
932 int jobnumb; /* Log file job number */
933 struct job *nextjob; /* Next partition on disk to salvage */
935 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
936 struct job *thisjob = 0;
937 static int numjobs = 0;
938 static int jobcount = 0;
944 char logFileName[256];
948 /* We have a partition to salvage. Copy it into thisjob */
949 thisjob = (struct job *) malloc(sizeof(struct job));
951 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
954 memset(thisjob, 0, sizeof(struct job));
955 thisjob->partP = partP;
956 thisjob->jobnumb = jobcount;
959 else if (jobcount == 0) {
960 /* We are asking to wait for all jobs (partp == 0), yet we never
963 Log("No file system partitions named %s* found; not salvaged\n",
964 VICE_PARTITION_PREFIX);
968 if (debug || Parallel == 1) {
970 SalvageFileSys(thisjob->partP, 0);
977 /* Check to see if thisjob is for a disk that we are already
978 * salvaging. If it is, link it in as the next job to do. The
979 * jobs array has 1 entry per disk being salvages. numjobs is
980 * the total number of disks currently being salvaged. In
981 * order to keep thejobs array compact, when a disk is
982 * completed, the hightest element in the jobs array is moved
983 * down to now open slot.
985 for (j=0; j<numjobs; j++) {
986 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
987 /* On same disk, add it to this list and return */
988 thisjob->nextjob = jobs[j]->nextjob;
989 jobs[j]->nextjob = thisjob;
996 /* Loop until we start thisjob or until all existing jobs are finished */
997 while ( thisjob || (!partP && (numjobs > 0)) ) {
998 startjob = -1; /* No new job to start */
1000 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
1001 /* Either the max jobs are running or we have to wait for all
1002 * the jobs to finish. In either case, we wait for at least one
1003 * job to finish. When it's done, clean up after it.
1005 pid = wait(&wstatus);
1007 for (j=0; j<numjobs; j++) { /* Find which job it is */
1008 if (pid == jobs[j]->pid) break;
1010 assert(j < numjobs);
1011 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1012 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1015 numjobs--; /* job no longer running */
1016 oldjob = jobs[j]; /* remember */
1017 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1018 free(oldjob); /* free the old job */
1020 /* If there is another partition on the disk to salvage, then
1021 * say we will start it (startjob). If not, then put thisjob there
1022 * and say we will start it.
1024 if (jobs[j]) { /* Another partitions to salvage */
1025 startjob = j; /* Will start it */
1026 } else { /* There is not another partition to salvage */
1028 jobs[j] = thisjob; /* Add thisjob */
1030 startjob = j; /* Will start it */
1032 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1033 startjob = -1; /* Don't start it - already running */
1037 /* We don't have to wait for a job to complete */
1039 jobs[numjobs] = thisjob; /* Add this job */
1041 startjob = numjobs; /* Will start it */
1045 /* Start up a new salvage job on a partition in job slot "startjob" */
1046 if (startjob != -1) {
1048 Log("Starting salvage of file system partition %s\n",
1049 jobs[startjob]->partP->name);
1051 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1052 * commands and pass the child job number via the data path.
1054 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1055 jobs[startjob]->jobnumb);
1056 jobs[startjob]->pid = pid;
1061 jobs[startjob]->pid = pid;
1067 for (fd =0; fd < 16; fd++) close(fd);
1068 open("/", 0); dup2(0, 1); dup2(0, 2);
1069 #ifndef AFS_NT40_ENV
1071 openlog("salvager", LOG_PID, useSyslogFacility);
1075 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1076 logFile = fopen(logFileName, "w");
1078 if (!logFile) logFile = stdout;
1080 SalvageFileSys1(jobs[startjob]->partP, 0);
1085 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1087 /* If waited for all jobs to complete, now collect log files and return */
1088 #ifndef AFS_NT40_ENV
1089 if ( ! useSyslog ) /* if syslogging - no need to collect */
1092 for (i=0; i<jobcount; i++) {
1093 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1094 if ((passLog = fopen(logFileName, "r"))) {
1095 while(fgets(buf, sizeof(buf), passLog)) {
1096 fputs(buf, logFile);
1100 (void)unlink(logFileName);
1108 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1110 if (!canfork || debug || Fork() == 0) {
1111 SalvageFileSys1(partP, singleVolumeNumber);
1112 if (canfork && !debug) {
1118 Wait("SalvageFileSys");
1121 char *get_DevName(char *pbuffer, char *wpath)
1123 char pbuf[128], *ptr;
1124 strcpy(pbuf, pbuffer);
1125 ptr = (char *)strrchr(pbuf, '/');
1128 strcpy(wpath, pbuf);
1131 ptr = (char *)strrchr(pbuffer, '/');
1133 strcpy(pbuffer, ptr+1);
1139 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1142 char inodeListPath[256];
1143 static char tmpDevName[100];
1144 static char wpath[100];
1145 struct VolumeSummary *vsp, *esp;
1148 fileSysPartition = partP;
1149 fileSysDevice = fileSysPartition->device;
1150 fileSysPathName = VPartitionPath(fileSysPartition);
1153 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1154 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1155 name = partP->devName;
1157 fileSysPath = fileSysPathName;
1158 strcpy(tmpDevName, partP->devName);
1159 name = get_DevName(tmpDevName, wpath);
1160 fileSysDeviceName = name;
1161 filesysfulldev = wpath;
1164 VLockPartition(partP->name);
1165 if (singleVolumeNumber || ForceSalvage)
1168 ForceSalvage = UseTheForceLuke(fileSysPath);
1170 if (singleVolumeNumber) {
1171 if (!VConnectFS()) {
1172 Abort("Couldn't connect to file server\n");
1174 AskOffline(singleVolumeNumber);
1177 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1179 Log("***Forced salvage of all volumes on this partition***\n");
1184 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1191 assert((dirp = opendir(fileSysPath)) != NULL);
1192 while ((dp = readdir(dirp))) {
1193 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1194 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1196 Log("Removing old salvager temp files %s\n", dp->d_name);
1197 strcpy(npath, fileSysPath);
1199 strcat(npath, dp->d_name);
1205 tdir = (tmpdir ? tmpdir : fileSysPath);
1207 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1208 (void) strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
1210 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1212 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1213 unlink(inodeListPath);
1217 /* Using nt_unlink here since we're really using the delete on close
1218 * semantics of unlink. In most places in the salvager, we really do
1219 * mean to unlink the file at that point. Those places have been
1220 * modified to actually do that so that the NT crt can be used there.
1222 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1223 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1225 inodeFd = open(inodeListPath, O_RDONLY);
1226 unlink(inodeListPath);
1229 Abort("Temporary file %s is missing...\n",
1231 if (ListInodeOption) {
1235 /* enumerate volumes in the partition.
1236 figure out sets of read-only + rw volumes.
1237 salvage each set, read-only volumes first, then read-write.
1238 Fix up inodes on last volume in set (whether it is read-write
1241 GetVolumeSummary(singleVolumeNumber);
1243 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1244 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1245 for (j=i; j < nVolumesInInodeFile
1246 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1247 VolumeId vid = inodeSummary[j].volumeId;
1248 struct VolumeSummary *tsp;
1249 /* Scan volume list (from partition root directory) looking for the
1250 current rw volume number in the volume list from the inode scan.
1251 If there is one here that is not in the inode volume list,
1253 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1255 DeleteExtraVolumeHeaderFile(vsp);
1257 /* Now match up the volume summary info from the root directory with the
1258 entry in the volume list obtained from scanning inodes */
1259 inodeSummary[j].volSummary = NULL;
1260 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1261 if (tsp->header.id == vid) {
1262 inodeSummary[j].volSummary = tsp;
1268 /* Salvage the group of volumes (several read-only + 1 read/write)
1269 * starting with the current read-only volume we're looking at.
1271 SalvageVolumeGroup(&inodeSummary[i], j-i);
1274 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1275 for ( ; vsp<esp; vsp++) {
1277 DeleteExtraVolumeHeaderFile(vsp);
1280 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1281 RemoveTheForce(fileSysPath);
1283 if (!Testing && singleVolumeNumber) {
1284 AskOnline(singleVolumeNumber, fileSysPartition->name);
1286 /* Step through the volumeSummary list and set all volumes on-line.
1287 * The volumes were taken off-line in GetVolumeSummary.
1289 for (j=0; j<nVolumes; j++) {
1290 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1295 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1296 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1299 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1302 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1304 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1305 vsp->fileName, (Testing? "would have been ":""));
1307 unlink(vsp->fileName);
1311 CompareInodes(const void *_p1, const void *_p2)
1313 register const struct ViceInodeInfo *p1 = _p1;
1314 register const struct ViceInodeInfo *p2 = _p2;
1315 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1316 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1317 VolumeId p1rwid, p2rwid;
1318 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1319 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1320 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1321 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1322 if (p1rwid < p2rwid)
1324 if (p1rwid > p2rwid)
1326 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1327 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1328 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1329 return (p1->u.special.type < p2->u.special.type? -1: 1);
1330 if (p1->u.vnode.volumeId == p1rwid)
1332 if (p2->u.vnode.volumeId == p2rwid)
1334 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1336 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1337 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1338 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1340 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1342 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1344 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1346 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1348 /* The following tests are reversed, so that the most desirable
1349 of several similar inodes comes first */
1350 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1351 #ifdef AFS_3DISPARES
1352 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1353 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1356 #ifdef AFS_SGI_EXMAG
1357 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1358 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1363 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1364 #ifdef AFS_3DISPARES
1365 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1366 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1369 #ifdef AFS_SGI_EXMAG
1370 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1371 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1376 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1377 #ifdef AFS_3DISPARES
1378 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1379 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1382 #ifdef AFS_SGI_EXMAG
1383 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1384 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1389 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1390 #ifdef AFS_3DISPARES
1391 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1392 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1395 #ifdef AFS_SGI_EXMAG
1396 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1397 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1405 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1406 register struct InodeSummary * summary)
1408 int volume = ip->u.vnode.volumeId;
1409 int rwvolume = volume;
1410 register n, nSpecial;
1411 register Unique maxunique;
1414 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1416 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1418 rwvolume = ip->u.special.parentId;
1419 /* This isn't quite right, as there could (in error) be different
1420 parent inodes in different special vnodes */
1423 if (maxunique < ip->u.vnode.vnodeUniquifier)
1424 maxunique = ip->u.vnode.vnodeUniquifier;
1428 summary->volumeId = volume;
1429 summary->RWvolumeId = rwvolume;
1430 summary->nInodes =n;
1431 summary->nSpecialInodes = nSpecial;
1432 summary->maxUniquifier = maxunique;
1435 int OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber)
1437 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1438 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1439 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1444 * Collect list of inodes in file named by path. If a truly fatal error,
1445 * unlink the file and abort. For lessor errors, return -1. The file will
1446 * be unlinked by the caller.
1448 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1452 struct ViceInodeInfo *ip;
1453 struct InodeSummary summary;
1454 char summaryFileName[50];
1457 char *dev = fileSysPath;
1458 char *wpath = fileSysPath;
1460 char *dev = fileSysDeviceName;
1461 char *wpath = filesysfulldev;
1463 char *part = fileSysPath;
1466 /* This file used to come from vfsck; cobble it up ourselves now... */
1467 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1469 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1474 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1476 if (forceSal && !ForceSalvage) {
1477 Log("***Forced salvage of all volumes on this partition***\n");
1480 inodeFd = open(path, O_RDWR);
1481 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1483 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1485 tdir = (tmpdir ? tmpdir : part);
1487 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1488 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1490 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1492 summaryFile = fopen(summaryFileName, "a+");
1493 if (summaryFile == NULL) {
1496 Abort("Unable to create inode summary file\n");
1498 if (!canfork || debug || Fork() == 0) {
1500 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1502 fclose(summaryFile); close(inodeFd);
1503 unlink(summaryFileName);
1504 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1505 RemoveTheForce(fileSysPath);
1506 Log("%s vice inodes on %s; not salvaged\n",
1507 singleVolumeNumber? "No applicable": "No", dev);
1510 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1512 fclose(summaryFile); close(inodeFd);
1514 unlink(summaryFileName);
1515 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1517 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1518 fclose(summaryFile); close(inodeFd);
1520 unlink(summaryFileName);
1521 Abort("Unable to read inode table; %s not salvaged\n", dev);
1523 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1524 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1525 write(inodeFd, ip, status.st_size) != status.st_size) {
1526 fclose(summaryFile); close(inodeFd);
1528 unlink(summaryFileName);
1529 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1533 CountVolumeInodes(ip, nInodes, &summary);
1534 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1535 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1536 fclose(summaryFile); close(inodeFd);
1539 summary.index += (summary.nInodes);
1540 nInodes -= summary.nInodes;
1541 ip += summary.nInodes;
1543 /* Following fflush is not fclose, because if it was debug mode would not work */
1544 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1545 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1546 fclose(summaryFile); close(inodeFd);
1549 if (canfork && !debug) {
1555 if (Wait("Inode summary") == -1) {
1556 fclose(summaryFile); close(inodeFd);
1558 unlink(summaryFileName);
1559 Exit(1); /* salvage of this partition aborted */
1562 assert(fstat(fileno(summaryFile), &status) != -1);
1563 if ( status.st_size != 0 ) {
1565 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1566 assert(inodeSummary != NULL);
1567 /* For GNU we need to do lseek to get the file pointer moved. */
1568 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1569 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1570 assert(ret == status.st_size);
1572 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1573 fclose(summaryFile);
1575 unlink(summaryFileName);
1579 /* Comparison routine for volume sort.
1580 This is setup so that a read-write volume comes immediately before
1581 any read-only clones of that volume */
1582 int CompareVolumes(const void *_p1, const void *_p2)
1584 register const struct VolumeSummary *p1 = _p1;
1585 register const struct VolumeSummary *p2 = _p2;
1586 if (p1->header.parent != p2->header.parent)
1587 return p1->header.parent < p2->header.parent? -1: 1;
1588 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1590 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1592 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1595 void GetVolumeSummary(VolumeId singleVolumeNumber)
1598 afs_int32 nvols = 0;
1599 struct VolumeSummary *vsp, vs;
1600 struct VolumeDiskHeader diskHeader;
1603 /* Get headers from volume directory */
1604 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1605 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1606 if (!singleVolumeNumber) {
1607 while ((dp = readdir(dirp))) {
1608 char *p = dp->d_name;
1609 p = strrchr(dp->d_name, '.');
1610 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1612 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1613 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1614 == sizeof (diskHeader) &&
1615 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1616 DiskToVolumeHeader(&vs.header, &diskHeader);
1623 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1627 if (!nvols) nvols = 1;
1628 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1630 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1631 assert(volumeSummaryp != NULL);
1634 vsp = volumeSummaryp;
1635 while ((dp = readdir(dirp))) {
1636 char *p = dp->d_name;
1637 p = strrchr(dp->d_name, '.');
1638 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1641 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1642 || read(fd, &diskHeader, sizeof (diskHeader))
1643 != sizeof (diskHeader)
1644 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1649 if (!singleVolumeNumber) {
1650 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1651 dp->d_name, (Testing? "it would have been ":""));
1657 char nameShouldBe[64];
1658 DiskToVolumeHeader(&vsp->header, &diskHeader);
1659 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1660 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1663 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1664 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1665 if (singleVolumeNumber)
1666 AskOffline(vsp->header.id);
1667 if (strcmp(nameShouldBe, dp->d_name)) {
1668 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 ":""));
1673 vsp->fileName = ToString(dp->d_name);
1683 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1686 /* Find the link table. This should be associated with the RW volume or, if
1687 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1689 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1690 struct ViceInodeInfo *allInodes)
1693 struct ViceInodeInfo *ip;
1695 for (i=0; i<nVols; i++) {
1696 ip = allInodes + isp[i].index;
1697 for (j=0; j<isp[i].nSpecialInodes; j++) {
1698 if (ip[j].u.special.type == VI_LINKTABLE)
1699 return ip[j].inodeNumber;
1705 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1707 struct versionStamp version;
1710 if (!VALID_INO(ino))
1711 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1712 isp->volumeId, INODESPECIAL,
1713 VI_LINKTABLE, isp->RWvolumeId);
1714 if (!VALID_INO(ino))
1715 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1716 isp->RWvolumeId, errno);
1717 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1718 fdP = IH_OPEN(VGLinkH);
1720 Abort("Can't open link table for volume %u (error = %d)\n",
1721 isp->RWvolumeId, errno);
1723 if (FDH_TRUNC(fdP, 0)<0)
1724 Abort("Can't truncate link table for volume %u (error = %d)\n",
1725 isp->RWvolumeId, errno);
1727 version.magic = LINKTABLEMAGIC;
1728 version.version = LINKTABLEVERSION;
1730 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1732 Abort("Can't truncate link table for volume %u (error = %d)\n",
1733 isp->RWvolumeId, errno);
1735 FDH_REALLYCLOSE(fdP);
1737 /* If the volume summary exits (i.e., the V*.vol header file exists),
1738 * then set this inode there as well.
1740 if (isp->volSummary)
1741 isp->volSummary->header.linkTable = ino;
1747 void *nt_SVG(void *arg)
1749 SVGParms_t *parms = (SVGParms_t*)arg;
1750 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1754 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1757 pthread_attr_t tattr;
1761 /* Initialize per volume global variables, even if later code does so */
1765 memset(&VolInfo, 0, sizeof(VolInfo));
1767 parms.svgp_inodeSummaryp = isp;
1768 parms.svgp_count = nVols;
1769 code = pthread_attr_init(&tattr);
1771 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1775 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1777 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1781 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1783 Log("Failed to create thread to salvage volume group %u\n",
1787 (void) pthread_join(tid, NULL);
1789 #endif /* AFS_NT40_ENV */
1791 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1793 struct ViceInodeInfo *inodes,*allInodes,*ip;
1794 int i, totalInodes, size, salvageTo;
1798 int dec_VGLinkH = 0;
1800 FdHandle_t *fdP = NULL;
1803 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1804 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1805 if (!ForceSalvage && QuickCheck(isp, nVols))
1808 if (ShowMounts && !haveRWvolume)
1810 if (canfork && !debug && Fork() != 0) {
1811 (void) Wait("Salvage volume group");
1814 for (i = 0, totalInodes = 0; i<nVols; i++)
1815 totalInodes += isp[i].nInodes;
1816 size = totalInodes * sizeof (struct ViceInodeInfo);
1817 inodes = (struct ViceInodeInfo *) malloc(size);
1818 allInodes = inodes - isp->index; /* this would the base of all the inodes
1819 for the partition, if all the inodes
1820 had been read into memory */
1821 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1);
1822 assert(read(inodeFd,inodes,size) == size);
1824 /* Don't try to salvage a read write volume if there isn't one on this
1826 salvageTo = haveRWvolume? 0:1;
1828 #ifdef AFS_NAMEI_ENV
1829 ino = FindLinkHandle(isp, nVols, allInodes);
1830 if (VALID_INO(ino)) {
1831 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1832 fdP = IH_OPEN(VGLinkH);
1834 if (!VALID_INO(ino) || fdP == NULL) {
1835 Log("%s link table for volume %u.\n",
1836 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1838 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1841 CreateLinkTable(isp, ino);
1845 FDH_REALLYCLOSE(fdP);
1847 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1850 /* Salvage in reverse order--read/write volume last; this way any
1851 Inodes not referenced by the time we salvage the read/write volume
1852 can be picked up by the read/write volume */
1853 /* ACTUALLY, that's not done right now--the inodes just vanish */
1854 for (i = nVols-1; i>=salvageTo; i--) {
1856 struct InodeSummary *lisp = &isp[i];
1857 #ifdef AFS_NAMEI_ENV
1858 /* If only the RO is present on this partition, the link table
1859 * shows up as a RW volume special file. Need to make sure the
1860 * salvager doesn't try to salvage the non-existent RW.
1862 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1863 /* If this only special inode is the link table, continue */
1864 if (inodes->u.special.type == VI_LINKTABLE) {
1870 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1871 lisp->volumeId, (Testing?"(READONLY mode)":""));
1872 /* Check inodes twice. The second time do things seriously. This
1873 way the whole RO volume can be deleted, below, if anything goes wrong */
1874 for (check = 1; check>=0; check--) {
1876 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1877 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1878 if (rw && deleteMe) {
1879 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1880 volume won't be called */
1886 if (rw && check == 1)
1888 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1889 MaybeZapVolume(lisp,"Vnode index", 0, check);
1895 /* Fix actual inode counts */
1897 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1898 static int TraceBadLinkCounts = 0;
1899 #ifdef AFS_NAMEI_ENV
1900 if (VGLinkH->ih_ino == ip->inodeNumber) {
1901 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1902 VGLinkH_p1 = ip->u.param[0];
1903 continue; /* Deal with this last. */
1906 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1907 TraceBadLinkCounts--; /* Limit reports, per volume */
1908 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1909 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1910 ip->byteCount, ip->u.param[0], ip->u.param[1],
1911 ip->u.param[2], ip->u.param[3]);
1913 while (ip->linkCount > 0) {
1914 /* below used to assert, not break */
1916 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1917 Log ("idec failed. inode %s errno %d\n",
1918 PrintInode(NULL, ip->inodeNumber), errno);
1924 while (ip->linkCount < 0) {
1925 /* these used to be asserts */
1927 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1928 Log ("iinc failed. inode %s errno %d\n",
1929 PrintInode(NULL, ip->inodeNumber) ,errno);
1936 #ifdef AFS_NAMEI_ENV
1937 while (dec_VGLinkH > 0) {
1938 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1939 Log("idec failed on link table, errno = %d\n", errno);
1943 while (dec_VGLinkH < 0) {
1944 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1945 Log("iinc failed on link table, errno = %d\n", errno);
1952 /* Directory consistency checks on the rw volume */
1954 SalvageVolume(isp, VGLinkH);
1955 IH_RELEASE(VGLinkH);
1957 if (canfork && !debug) {
1963 int QuickCheck(register struct InodeSummary *isp, int nVols)
1965 /* Check headers BEFORE forking */
1969 for (i = 0; i<nVols; i++) {
1970 struct VolumeSummary *vs = isp[i].volSummary;
1971 VolumeDiskData volHeader;
1973 /* Don't salvage just because phantom rw volume is there... */
1974 /* (If a read-only volume exists, read/write inodes must also exist) */
1975 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1979 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1980 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1981 == sizeof(volHeader)
1982 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1983 && volHeader.dontSalvage == DONT_SALVAGE
1984 && volHeader.needsSalvaged == 0
1985 && volHeader.destroyMe == 0) {
1986 if (volHeader.inUse == 1) {
1987 volHeader.inUse = 0;
1988 volHeader.inService = 1;
1990 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1991 != sizeof(volHeader)) {
2008 /* SalvageVolumeHeaderFile
2010 * Salvage the top level V*.vol header file. Make sure the special files
2011 * exist and that there are no duplicates.
2013 * Calls SalvageHeader for each possible type of volume special file.
2016 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2017 register struct ViceInodeInfo *inodes,
2018 int RW, int check, int *deleteMe)
2022 register struct ViceInodeInfo *ip;
2023 int allinodesobsolete = 1;
2024 struct VolumeDiskHeader diskHeader;
2028 memset(&tempHeader, 0, sizeof(tempHeader));
2029 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2030 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2031 tempHeader.id = isp->volumeId;
2032 tempHeader.parent = isp->RWvolumeId;
2033 /* Check for duplicates (inodes are sorted by type field) */
2034 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2035 ip = &inodes[isp->index+i];
2036 if (ip->u.special.type == (ip+1)->u.special.type) {
2037 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2041 for (i = 0; i<isp->nSpecialInodes; i++) {
2042 ip = &inodes[isp->index+i];
2043 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2045 Log("Rubbish header inode\n");
2048 Log("Rubbish header inode; deleted\n");
2050 else if (!stuff[ip->u.special.type-1].obsolete) {
2051 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2052 if (!check && ip->u.special.type != VI_LINKTABLE)
2053 ip->linkCount--; /* Keep the inode around */
2054 allinodesobsolete = 0;
2058 if (allinodesobsolete) {
2065 VGLinkH_cnt ++; /* one for every header. */
2067 if (!RW && !check && isp->volSummary) {
2068 ClearROInUseBit(isp->volSummary);
2072 for (i = 0; i< MAXINODETYPE; i++) {
2073 if (stuff[i].inodeType == VI_LINKTABLE) {
2074 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2075 * And we may have recreated the link table earlier, so set the
2076 * RW header as well.
2078 if (VALID_INO(VGLinkH->ih_ino)) {
2079 *stuff[i].inode = VGLinkH->ih_ino;
2083 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2087 if (isp->volSummary == NULL) {
2089 sprintf(name, VFORMAT, isp->volumeId);
2091 Log("No header file for volume %u\n", isp->volumeId);
2094 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2095 isp->volumeId, (Testing?"it would have been ":""),
2096 fileSysPathName, name);
2097 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2098 assert(headerFd != -1);
2099 isp->volSummary = (struct VolumeSummary *)
2100 malloc(sizeof(struct VolumeSummary));
2101 isp->volSummary->fileName = ToString(name);
2105 /* hack: these two fields are obsolete... */
2106 isp->volSummary->header.volumeAcl = 0;
2107 isp->volSummary->header.volumeMountTable = 0;
2109 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2110 /* We often remove the name before calling us, so we make a fake one up */
2111 if (isp->volSummary->fileName) {
2112 strcpy(name, isp->volSummary->fileName);
2114 sprintf(name, VFORMAT, isp->volumeId);
2115 isp->volSummary->fileName = ToString(name);
2118 Log("Header file %s is damaged or no longer valid%s\n",
2119 name, (check ? "" : "; repairing"));
2123 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2124 assert(headerFd != -1);
2128 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2130 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2132 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2133 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2134 != sizeof(struct VolumeDiskHeader)) {
2135 Log("Couldn't rewrite volume header file!\n");
2142 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2143 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2147 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2148 int check, int *deleteMe)
2151 VolumeDiskData volumeInfo;
2152 struct versionStamp fileHeader;
2161 #ifndef AFS_NAMEI_ENV
2162 if ( sp->inodeType == VI_LINKTABLE)
2165 if (*(sp->inode) == 0) {
2167 Log("Missing inode in volume header (%s)\n", sp->description);
2170 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2171 sp->description, (Testing ? "it would have recreated it": "recreating"));
2173 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2174 isp->volumeId, INODESPECIAL,
2175 sp->inodeType, isp->RWvolumeId);
2176 if (!VALID_INO(*(sp->inode)))
2177 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2178 sp->description, errno);
2183 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2184 fdP = IH_OPEN(specH);
2185 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2186 /* bail out early and destroy the volume */
2187 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2189 if (deleteMe) *deleteMe = 1;
2194 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2195 sp->description, errno);
2198 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2199 || header.fileHeader.magic != sp->stamp.magic)) {
2201 Log("Part of the header (%s) is corrupted\n", sp->description);
2202 FDH_REALLYCLOSE(fdP);
2206 Log("Part of the header (%s) is corrupted; recreating\n",
2210 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2213 FDH_REALLYCLOSE(fdP);
2217 if (recreate && !Testing) {
2219 Abort("Internal error: recreating volume header (%s) in check mode\n",
2221 code = FDH_TRUNC(fdP, 0);
2223 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2224 sp->description, errno);
2226 /* The following code should be moved into vutil.c */
2227 if (sp->inodeType == VI_VOLINFO) {
2229 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2230 header.volumeInfo.stamp = sp->stamp;
2231 header.volumeInfo.id = isp->volumeId;
2232 header.volumeInfo.parentId = isp->RWvolumeId;
2233 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2234 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2235 header.volumeInfo.inService = 0;
2236 header.volumeInfo.blessed = 0;
2237 /* The + 1000 is a hack in case there are any files out in venus caches */
2238 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2239 header.volumeInfo.type =
2240 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2241 header.volumeInfo.needsCallback = 0;
2242 gettimeofday(&tp,0);
2243 header.volumeInfo.creationDate = tp.tv_sec;
2244 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2245 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2247 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2248 sizeof(header.volumeInfo));
2249 if (code != sizeof(header.volumeInfo)) {
2251 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2252 sp->description, errno);
2253 Abort("Unable to write entire volume header file (%s)\n",
2258 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2259 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2261 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2262 if (code != sizeof(sp->stamp)) {
2264 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2265 sp->description, errno);
2266 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2271 FDH_REALLYCLOSE(fdP);
2273 if (sp->inodeType == VI_VOLINFO) {
2274 VolInfo = header.volumeInfo;
2277 if (VolInfo.updateDate) {
2278 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2279 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2280 (Testing?"it would have been ":""), update);
2282 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2283 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2292 int SalvageVnodes(register struct InodeSummary *rwIsp,
2293 register struct InodeSummary * thisIsp,
2294 register struct ViceInodeInfo * inodes, int check)
2296 int ilarge, ismall, ioffset, RW, nInodes;
2297 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2298 if (Showmode) return 0;
2299 RW = (rwIsp == thisIsp);
2300 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2301 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2302 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2303 if (check && ismall == -1)
2305 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2306 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2307 return (ilarge==0 && ismall==0 ? 0: -1);
2310 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2311 register struct ViceInodeInfo *ip,
2312 int nInodes, struct VolumeSummary *volSummary, int check)
2314 VolumeId volumeNumber;
2315 char buf[SIZEOF_LARGEDISKVNODE];
2316 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2318 StreamHandle_t *file;
2319 struct VnodeClassInfo *vcp;
2321 afs_fsize_t vnodeLength;
2322 int vnodeIndex, nVnodes;
2323 afs_ino_str_t stmp1, stmp2;
2327 volumeNumber = volSummary->header.id;
2328 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2329 fdP = IH_OPEN(handle);
2330 assert(fdP != NULL);
2331 file = FDH_FDOPEN(fdP, "r+");
2332 assert(file != NULL);
2333 vcp = &VnodeClassInfo[class];
2334 size = OS_SIZE(fdP->fd_fd);
2336 nVnodes = (size / vcp->diskSize) - 1;
2338 assert((nVnodes+1) * vcp->diskSize == size);
2339 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2344 for (vnodeIndex = 0;
2345 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2346 nVnodes--, vnodeIndex++) {
2347 if (vnode->type != vNull) {
2348 int vnodeChanged = 0;
2349 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2350 /* Log programs that belong to root (potentially suid root);
2351 don't bother for read-only or backup volumes */
2352 #ifdef notdef /* This is done elsewhere */
2353 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2354 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2355 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2356 vnode->owner, vnode->modeBits);
2358 if (VNDISK_GET_INO(vnode) == 0) {
2360 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2361 memset(vnode, 0, vcp->diskSize);
2366 if (vcp->magic != vnode->vnodeMagic) {
2367 /* bad magic #, probably partially created vnode */
2368 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2369 memset(vnode, 0, vcp->diskSize);
2373 /* ****** Should do a bit more salvage here: e.g. make sure
2374 vnode type matches what it should be given the index */
2375 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2376 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2377 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2378 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2385 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2386 /* The following doesn't work, because the version number
2387 is not maintained correctly by the file server */
2388 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2389 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2391 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2398 /* For RW volume, look for vnode with matching inode number;
2399 if no such match, take the first determined by our sort
2401 register struct ViceInodeInfo *lip = ip;
2402 register lnInodes = nInodes;
2403 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2404 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2413 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2414 /* "Matching" inode */
2418 vu = vnode->uniquifier;
2419 iu = ip->u.vnode.vnodeUniquifier;
2420 vd = vnode->dataVersion;
2421 id = ip->u.vnode.inodeDataVersion;
2423 * Because of the possibility of the uniquifier overflows (> 4M)
2424 * we compare them modulo the low 22-bits; we shouldn't worry
2425 * about mismatching since they shouldn't to many old
2426 * uniquifiers of the same vnode...
2428 if (IUnique(vu) != IUnique(iu)) {
2430 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2431 vnodeNumber, IUnique(vu), IUnique(iu));
2434 vnode->uniquifier = iu;
2435 #ifdef AFS_3DISPARES
2436 vnode->dataVersion = (id >= vd ?
2437 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2438 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2440 #if defined(AFS_SGI_EXMAG)
2441 vnode->dataVersion = (id >= vd ?
2442 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2443 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2445 vnode->dataVersion = (id>vd ? id:vd);
2446 #endif /* AFS_SGI_EXMAG */
2447 #endif /* AFS_3DISPARES */
2451 /* don't bother checking for vd > id any more, since
2452 partial file transfers always result in this state,
2453 and you can't do much else anyway (you've already
2454 found the best data you can) */
2455 #ifdef AFS_3DISPARES
2456 if (!vnodeIsDirectory(vnodeNumber) &&
2457 ((vd < id && (id-vd) < 1887437) ||
2458 ((vd > id && (vd-id) > 1887437)))) {
2460 #if defined(AFS_SGI_EXMAG)
2461 if (!vnodeIsDirectory(vnodeNumber) &&
2462 ((vd < id && (id-vd) < 15099494) ||
2463 ((vd > id && (vd-id) > 15099494)))) {
2465 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2466 #endif /* AFS_SGI_EXMAG */
2468 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2469 vnode->dataVersion = id;
2474 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2477 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2479 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2480 PrintInode(stmp2, ip->inodeNumber),
2483 VNDISK_SET_INO(vnode, ip->inodeNumber);
2488 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2490 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2491 PrintInode(stmp2, ip->inodeNumber),
2494 VNDISK_SET_INO(vnode, ip->inodeNumber);
2497 VNDISK_GET_LEN(vnodeLength, vnode);
2498 if (ip->byteCount != vnodeLength) {
2500 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2501 vnodeNumber, vnodeLength, ip->byteCount);
2505 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2506 vnodeNumber, vnodeLength, ip->byteCount);
2507 VNDISK_SET_LEN(vnode, ip->byteCount);
2511 ip->linkCount--; /* Keep the inode around */
2515 else { /* no matching inode */
2516 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2517 /* No matching inode--get rid of the vnode */
2519 if (VNDISK_GET_INO(vnode)) {
2521 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2522 vnodeNumber, vnode->uniquifier,
2523 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2526 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2531 if (VNDISK_GET_INO(vnode)) {
2533 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2534 vnodeNumber, vnode->uniquifier,
2535 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2536 ctime((time_t *)&(vnode->serverModifyTime)));
2539 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)));
2541 memset(vnode, 0, vcp->diskSize);
2544 /* Should not reach here becuase we checked for
2545 * (inodeNumber == 0) above. And where we zero the vnode,
2546 * we also goto vnodeDone.
2550 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2554 } /* VNDISK_GET_INO(vnode) != 0 */
2556 assert(!(vnodeChanged && check));
2557 if (vnodeChanged && !Testing) {
2558 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2559 (char*)vnode, vcp->diskSize)
2561 VolumeChanged = 1; /* For break call back */
2572 struct VnodeEssence *CheckVnodeNumber(VnodeId vnodeNumber)
2575 struct VnodeInfo *vip;
2578 class = vnodeIdToClass(vnodeNumber);
2579 vip = &vnodeInfo[class];
2580 offset = vnodeIdToBitNumber(vnodeNumber);
2581 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2584 void CopyOnWrite(register struct DirSummary *dir)
2586 /* Copy the directory unconditionally if we are going to change it:
2587 * not just if was cloned.
2589 struct VnodeDiskObject vnode;
2590 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2591 Inode oldinode, newinode;
2594 if (dir->copied || Testing)
2596 DFlush(); /* Well justified paranoia... */
2598 code = IH_IREAD(vnodeInfo[vLarge].handle,
2599 vnodeIndexOffset(vcp, dir->vnodeNumber),
2600 (char*)&vnode, sizeof (vnode));
2601 assert(code == sizeof(vnode));
2602 oldinode = VNDISK_GET_INO(&vnode);
2603 /* Increment the version number by a whole lot to avoid problems with
2604 * clients that were promised new version numbers--but the file server
2605 * crashed before the versions were written to disk.
2607 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2608 dir->rwVid, dir->vnodeNumber,
2609 vnode.uniquifier, vnode.dataVersion += 200);
2610 assert(VALID_INO(newinode));
2611 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2613 VNDISK_SET_INO(&vnode, newinode);
2614 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2615 vnodeIndexOffset(vcp, dir->vnodeNumber),
2616 (char*)&vnode, sizeof (vnode));
2617 assert(code == sizeof (vnode));
2619 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2620 fileSysDevice, newinode);
2621 /* Don't delete the original inode right away, because the directory is
2622 * still being scanned.
2628 * This function should either successfully create a new dir, or give up
2629 * and leave things the way they were. In particular, if it fails to write
2630 * the new dir properly, it should return w/o changing the reference to the
2633 void CopyAndSalvage(register struct DirSummary *dir)
2635 struct VnodeDiskObject vnode;
2636 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2637 Inode oldinode, newinode;
2639 register afs_int32 code;
2640 afs_int32 parentUnique= 1;
2641 struct VnodeEssence *vnodeEssence;
2645 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2646 code = IH_IREAD(vnodeInfo[vLarge].handle,
2647 vnodeIndexOffset(vcp, dir->vnodeNumber),
2648 (char*)&vnode, sizeof (vnode));
2649 assert(code == sizeof (vnode));
2650 oldinode = VNDISK_GET_INO(&vnode);
2651 /* Increment the version number by a whole lot to avoid problems with
2652 * clients that were promised new version numbers--but the file server
2653 * crashed before the versions were written to disk.
2655 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2656 dir->rwVid, dir->vnodeNumber,
2658 vnode.dataVersion += 200);
2659 assert(VALID_INO(newinode));
2660 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2662 /* Assign . and .. vnode numbers from dir and vnode.parent.
2663 * The uniquifier for . is in the vnode.
2664 * The uniquifier for .. might be set to a bogus value of 1 and
2665 * the salvager will later clean it up.
2667 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2668 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2670 code = DirSalvage(&dir->dirHandle, &newdir,
2671 dir->vnodeNumber, vnode.uniquifier,
2672 (vnode.parent?vnode.parent:dir->vnodeNumber),
2674 if (code == 0) code = DFlush();
2676 /* didn't really build the new directory properly, let's just give up. */
2677 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2679 Log("Directory salvage returned code %d, continuing.\n", code);
2682 Log("Checking the results of the directory salvage...\n");
2683 if (!DirOK(&newdir)) {
2684 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2685 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2690 VNDISK_SET_INO(&vnode, newinode);
2691 VNDISK_SET_LEN(&vnode, Length(&newdir));
2692 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2693 vnodeIndexOffset(vcp, dir->vnodeNumber),
2694 (char*)&vnode, sizeof (vnode));
2695 assert(code == sizeof (vnode));
2697 nt_sync(fileSysDevice);
2699 sync(); /* this is slow, but hopefully rarely called. We don't have
2700 * an open FD on the file itself to fsync.
2703 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2705 dir->dirHandle = newdir;
2708 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2711 struct VnodeEssence *vnodeEssence;
2712 afs_int32 dirOrphaned, todelete;
2714 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2716 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2717 if (vnodeEssence == NULL) {
2719 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2720 dir->vnodeNumber, dir->name?dir->name:"??",
2721 name, vnodeNumber, unique);
2725 assert(Delete(&dir->dirHandle, name) == 0);
2731 #ifndef AFS_NAMEI_ENV
2732 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2733 * mount inode for the partition. If this inode were deleted, it would crash
2736 if (vnodeEssence->InodeNumber == 0) {
2737 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2738 dir->vnodeNumber, (dir->name?dir->name:"??"),
2739 name, vnodeNumber, unique,
2740 (Testing?"-- would have deleted":" -- deleted"));
2743 assert(Delete(&dir->dirHandle, name) == 0);
2750 if (!(vnodeNumber & 1) && !Showmode &&
2751 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2752 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2753 dir->vnodeNumber, (dir->name?dir->name:"??"),
2754 name, vnodeNumber, unique,
2755 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2759 assert(Delete(&dir->dirHandle, name) == 0);
2765 /* Check if the Uniquifiers match. If not, change the directory entry
2766 * so its unique matches the vnode unique. Delete if the unique is zero
2767 * or if the directory is orphaned.
2769 if (!vnodeEssence->unique ||
2770 (vnodeEssence->unique) != unique) {
2771 if (!vnodeEssence->unique &&
2772 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2773 /* This is an orphaned directory. Don't delete the . or ..
2774 * entry. Otherwise, it will get created in the next
2775 * salvage and deleted again here. So Just skip it.
2780 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2783 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2784 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2785 name, vnodeNumber, unique, vnodeEssence->unique,
2786 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2790 fid.Vnode = vnodeNumber;
2791 fid.Unique = vnodeEssence->unique;
2793 assert(Delete(&dir->dirHandle, name) == 0);
2795 assert(Create(&dir->dirHandle, name, &fid) == 0);
2797 if (todelete) return; /* no need to continue */
2800 if (strcmp(name,".") == 0) {
2801 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2803 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2804 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2807 assert(Delete(&dir->dirHandle, ".") == 0);
2808 fid.Vnode = dir->vnodeNumber;
2809 fid.Unique = dir->unique;
2810 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2813 vnodeNumber = fid.Vnode; /* Get the new Essence */
2814 unique = fid.Unique;
2815 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2819 else if (strcmp(name,"..") == 0) {
2822 struct VnodeEssence *dotdot;
2823 pa.Vnode = dir->parent;
2824 dotdot = CheckVnodeNumber(pa.Vnode);
2825 assert (dotdot != NULL); /* XXX Should not be assert */
2826 pa.Unique = dotdot->unique;
2829 pa.Vnode = dir->vnodeNumber;
2830 pa.Unique = dir->unique;
2832 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2833 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2834 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2837 assert(Delete(&dir->dirHandle, "..") == 0);
2838 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2841 vnodeNumber = pa.Vnode; /* Get the new Essence */
2843 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2845 dir->haveDotDot = 1;
2846 } else if (strncmp(name,".__afs",6) == 0) {
2848 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2849 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2853 assert(Delete(&dir->dirHandle, name) == 0);
2855 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2856 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2860 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2861 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2862 vnodeEssence->owner, vnodeEssence->group,
2863 vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber,
2865 if (ShowMounts && (vnodeEssence->type == vSymlink) &&
2866 !(vnodeEssence->modeBits & 0111)) {
2872 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2873 vnodeEssence->InodeNumber);
2875 assert(fdP != NULL);
2876 size = FDH_SIZE(fdP);
2878 memset(buf, 0, 1024);
2879 if (size > 1024) size = 1024;
2880 code = FDH_READ(fdP, buf, size);
2881 assert(code == size);
2882 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2883 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2884 dir->name?dir->name:"??", name, buf);
2885 FDH_REALLYCLOSE(fdP);
2888 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2889 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2890 vnodeEssence->owner, vnodeEssence->group,
2891 vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber,
2893 if (vnodeIdToClass(vnodeNumber) == vLarge &&
2894 vnodeEssence->name == NULL) {
2896 if (n = (char*)malloc(strlen(name)+1))
2898 vnodeEssence->name = n;
2901 /* The directory entry points to the vnode. Check to see if the
2902 * vnode points back to the directory. If not, then let the
2903 * directory claim it (else it might end up orphaned). Vnodes
2904 * already claimed by another directory are deleted from this
2905 * directory: hardlinks to the same vnode are not allowed
2906 * from different directories.
2908 if (vnodeEssence->parent != dir->vnodeNumber) {
2909 if (!vnodeEssence->claimed && !dirOrphaned) {
2910 /* Vnode does not point back to this directory.
2911 * Orphaned dirs cannot claim a file (it may belong to
2912 * another non-orphaned dir).
2915 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2916 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2917 vnodeNumber, unique, (Testing?"would have been ":""),
2918 vnodeEssence->parent, dir->vnodeNumber);
2920 vnodeEssence->parent = dir->vnodeNumber;
2921 vnodeEssence->changed = 1;
2923 /* Vnode was claimed by another directory */
2926 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2927 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2928 vnodeEssence->parent, vnodeNumber, unique,
2929 (Testing?"would have been ":""));
2931 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2932 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2933 vnodeEssence->parent, vnodeNumber, unique,
2934 (Testing?"would have been ":""));
2939 assert(Delete(&dir->dirHandle, name) == 0);
2944 /* This directory claims the vnode */
2945 vnodeEssence->claimed = 1;
2947 vnodeEssence->count--;
2950 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2953 register struct VnodeInfo *vip = &vnodeInfo[class];
2954 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2955 char buf[SIZEOF_LARGEDISKVNODE];
2956 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2958 StreamHandle_t *file;
2963 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2964 fdP = IH_OPEN(vip->handle);
2965 assert(fdP != NULL);
2966 file = FDH_FDOPEN(fdP, "r+");
2967 assert(file != NULL);
2968 size = OS_SIZE(fdP->fd_fd);
2970 vip->nVnodes = (size / vcp->diskSize) - 1;
2971 if (vip->nVnodes > 0) {
2972 assert((vip->nVnodes+1)*vcp->diskSize == size);
2973 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2974 assert((vip->vnodes = (struct VnodeEssence *)
2975 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
2976 if (class == vLarge) {
2977 assert((vip->inodes = (Inode *)
2978 calloc(vip->nVnodes, sizeof (Inode))) != NULL);
2989 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2990 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2991 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2992 nVnodes--, vnodeIndex++) {
2993 if (vnode->type != vNull) {
2994 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2995 afs_fsize_t vnodeLength;
2996 vip->nAllocatedVnodes++;
2997 vep->count = vnode->linkCount;
2998 VNDISK_GET_LEN(vnodeLength, vnode);
2999 vep->blockCount = nBlocks(vnodeLength);
3000 vip->volumeBlockCount += vep->blockCount;
3001 vep->parent = vnode->parent;
3002 vep->unique = vnode->uniquifier;
3003 if (*maxu < vnode->uniquifier)
3004 *maxu = vnode->uniquifier;
3005 vep->modeBits = vnode->modeBits;
3006 vep->InodeNumber = VNDISK_GET_INO(vnode);
3007 vep->type = vnode->type;
3008 vep->author = vnode->author;
3009 vep->owner = vnode->owner;
3010 vep->group = vnode->group;
3011 if (vnode->type == vDirectory) {
3012 assert(class == vLarge);
3013 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3021 static char *GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3023 struct VnodeEssence *parentvp;
3029 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3031 strcat(path, vp->name);
3037 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3038 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3040 static int IsVnodeOrphaned(VnodeId vnode)
3042 struct VnodeEssence *vep;
3044 if (vnode == 0) return(1); /* Vnode zero does not exist */
3045 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3046 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3047 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3049 return( IsVnodeOrphaned(vep->parent) );
3052 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3053 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3056 static struct DirSummary dir;
3057 static struct DirHandle dirHandle;
3058 struct VnodeEssence *parent;
3059 static char path[MAXPATHLEN];
3062 if (dirVnodeInfo->vnodes[i].salvaged)
3063 return; /* already salvaged */
3066 dirVnodeInfo->vnodes[i].salvaged = 1;
3068 if (dirVnodeInfo->inodes[i] == 0)
3069 return; /* Not allocated to a directory */
3071 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3072 if (parent && parent->salvaged == 0)
3073 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3074 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3075 rootdir, rootdirfound);
3076 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3077 dir.unique = dirVnodeInfo->vnodes[i].unique;
3080 dir.parent = dirVnodeInfo->vnodes[i].parent;
3081 dir.haveDot = dir.haveDotDot = 0;
3082 dir.ds_linkH = alinkH;
3083 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3085 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3088 Log("Directory bad, vnode %d; %s...\n",
3089 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3092 CopyAndSalvage(&dir);
3096 dirHandle = dir.dirHandle;
3098 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3101 /* If enumeration failed for random reasons, we will probably delete
3102 * too much stuff, so we guard against this instead.
3104 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3107 /* Delete the old directory if it was copied in order to salvage.
3108 * CopyOnWrite has written the new inode # to the disk, but we still
3109 * have the old one in our local structure here. Thus, we idec the
3113 if (dir.copied && !Testing) {
3114 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3116 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3119 /* Remember rootdir DirSummary _after_ it has been judged */
3120 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3121 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3128 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3130 /* This routine, for now, will only be called for read-write volumes */
3132 int BlocksInVolume = 0, FilesInVolume = 0;
3133 register VnodeClass class;
3134 struct DirSummary rootdir, oldrootdir;
3135 struct VnodeInfo *dirVnodeInfo;
3136 struct VnodeDiskObject vnode;
3137 VolumeDiskData volHeader;
3139 int orphaned, rootdirfound = 0;
3140 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3141 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3142 struct VnodeEssence *vep;
3147 VnodeId LFVnode, ThisVnode;
3148 Unique LFUnique, ThisUnique;
3151 vid = rwIsp->volSummary->header.id;
3152 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3153 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3154 assert(nBytes == sizeof(volHeader));
3155 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3156 assert (volHeader.destroyMe != DESTROY_ME);
3157 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3159 DistilVnodeEssence(vid, vLarge,
3160 rwIsp->volSummary->header.largeVnodeIndex,
3162 DistilVnodeEssence(vid, vSmall,
3163 rwIsp->volSummary->header.smallVnodeIndex,
3166 dirVnodeInfo = &vnodeInfo[vLarge];
3167 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3168 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3169 &rootdir, &rootdirfound);
3176 /* Parse each vnode looking for orphaned vnodes and
3177 * connect them to the tree as orphaned (if requested).
3179 oldrootdir = rootdir;
3180 for (class=0; class < nVNODECLASSES; class++) {
3181 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3182 vep = &(vnodeInfo[class].vnodes[v]);
3183 ThisVnode = bitNumberToVnodeNumber(v, class);
3184 ThisUnique = vep->unique;
3186 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3187 continue; /* Ignore unused, claimed, and root vnodes */
3189 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3190 * entry in this vnode had incremented the parent link count (In
3191 * JudgeEntry()). We need to go to the parent and decrement that
3192 * link count. But if the parent's unique is zero, then the parent
3193 * link count was not incremented in JudgeEntry().
3195 if (class == vLarge) { /* directory vnode */
3196 pv = vnodeIdToBitNumber(vep->parent);
3197 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3198 vnodeInfo[vLarge].vnodes[pv].count++;
3202 continue; /* If no rootdir, can't attach orphaned files */
3204 /* Here we attach orphaned files and directories into the
3205 * root directory, LVVnode, making sure link counts stay correct.
3207 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3208 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3209 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3211 /* Update this orphaned vnode's info. Its parent info and
3212 * link count (do for orphaned directories and files).
3214 vep->parent = LFVnode; /* Parent is the root dir */
3215 vep->unique = LFUnique;
3218 vep->count--; /* Inc link count (root dir will pt to it) */
3220 /* If this orphaned vnode is a directory, change '..'.
3221 * The name of the orphaned dir/file is unknown, so we
3222 * build a unique name. No need to CopyOnWrite the directory
3223 * since it is not connected to tree in BK or RO volume and
3224 * won't be visible there.
3226 if (class == vLarge) {
3230 /* Remove and recreate the ".." entry in this orphaned directory */
3231 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3233 pa.Unique = LFUnique;
3234 assert(Delete(&dh, "..") == 0);
3235 assert(Create(&dh, "..", &pa) == 0);
3237 /* The original parent's link count was decremented above.
3238 * Here we increment the new parent's link count.
3240 pv = vnodeIdToBitNumber(LFVnode);
3241 vnodeInfo[vLarge].vnodes[pv].count--;
3245 /* Go to the root dir and add this entry. The link count of the
3246 * root dir was incremented when ".." was created. Try 10 times.
3248 for (j=0; j<10; j++) {
3249 pa.Vnode = ThisVnode;
3250 pa.Unique = ThisUnique;
3252 sprintf(npath, "%s.%d.%d",
3253 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3254 ThisVnode, ThisUnique);
3256 CopyOnWrite(&rootdir);
3257 code = Create(&rootdir.dirHandle, npath, &pa);
3260 ThisUnique += 50; /* Try creating a different file */
3263 Log("Attaching orphaned %s to volume's root dir as %s\n",
3264 ((class == vLarge)?"directory":"file"), npath);
3266 } /* for each vnode in the class */
3267 } /* for each class of vnode */
3269 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3271 if (!oldrootdir.copied && rootdir.copied) {
3272 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3274 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3277 DFlush(); /* Flush the changes */
3278 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3279 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3280 orphans = ORPH_IGNORE;
3283 /* Write out all changed vnodes. Orphaned files and directories
3284 * will get removed here also (if requested).
3286 for (class = 0; class < nVNODECLASSES; class++){
3287 int nVnodes = vnodeInfo[class].nVnodes;
3288 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3289 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3290 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3291 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3292 for (i = 0; i<nVnodes; i++) {
3293 register struct VnodeEssence *vnp = &vnodes[i];
3294 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3296 /* If the vnode is good but is unclaimed (not listed in
3297 * any directory entries), then it is orphaned.
3300 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3301 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3305 if (vnp->changed || vnp->count) {
3308 nBytes = IH_IREAD(vnodeInfo[class].handle,
3309 vnodeIndexOffset(vcp, vnodeNumber),
3310 (char*)&vnode, sizeof (vnode));
3311 assert(nBytes == sizeof(vnode));
3313 vnode.parent = vnp->parent;
3314 oldCount = vnode.linkCount;
3315 vnode.linkCount = vnode.linkCount - vnp->count;
3318 orphaned = IsVnodeOrphaned(vnodeNumber);
3320 if (!vnp->todelete) {
3321 /* Orphans should have already been attached (if requested) */
3322 assert(orphans != ORPH_ATTACH);
3323 oblocks += vnp->blockCount;
3326 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3327 BlocksInVolume -= vnp->blockCount;
3329 if (VNDISK_GET_INO(&vnode)) {
3330 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3333 memset(&vnode, 0, sizeof(vnode));
3335 } else if (vnp->count) {
3337 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3338 vnodeNumber, oldCount,
3339 (Testing?"would have changed to":"now"), vnode.linkCount);
3343 vnode.dataVersion++;
3345 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3346 vnodeIndexOffset(vcp, vnodeNumber),
3347 (char*)&vnode, sizeof (vnode));
3348 assert(nBytes == sizeof(vnode));
3354 if (!Showmode && ofiles) {
3355 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3356 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3360 for (class = 0; class < nVNODECLASSES; class++) {
3361 register struct VnodeInfo *vip = &vnodeInfo[class];
3362 for (i=0; i<vip->nVnodes; i++)
3363 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3364 if (vip->vnodes) free(vip->vnodes);
3365 if (vip->inodes) free(vip->inodes);
3368 /* Set correct resource utilization statistics */
3369 volHeader.filecount = FilesInVolume;
3370 volHeader.diskused = BlocksInVolume;
3372 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3373 if (volHeader.uniquifier < (maxunique + 1)) {
3374 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3375 /* Plus 2,000 in case there are workstations out there with
3376 * cached vnodes that have since been deleted
3378 volHeader.uniquifier = (maxunique + 1 + 2000);
3381 /* Turn off the inUse bit; the volume's been salvaged! */
3382 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3383 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3384 volHeader.inService = 1; /* allow service again */
3385 volHeader.needsCallback = (VolumeChanged != 0);
3386 volHeader.dontSalvage = DONT_SALVAGE;
3389 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3390 assert(nBytes == sizeof(volHeader));
3393 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3394 (Testing?"It would have ":""), volHeader.name,
3395 volHeader.id, FilesInVolume, BlocksInVolume);
3397 IH_RELEASE(vnodeInfo[vSmall].handle);
3398 IH_RELEASE(vnodeInfo[vLarge].handle);
3403 void ClearROInUseBit(struct VolumeSummary *summary)
3405 IHandle_t *h = summary->volumeInfoHandle;
3408 VolumeDiskData volHeader;
3410 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3411 assert(nBytes == sizeof(volHeader));
3412 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3413 volHeader.inUse = 0;
3414 volHeader.needsSalvaged = 0;
3415 volHeader.inService = 1;
3416 volHeader.dontSalvage = DONT_SALVAGE;
3418 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3419 assert(nBytes == sizeof(volHeader));
3424 * Possible delete the volume.
3426 * deleteMe - Always do so, only a partial volume.
3428 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3429 int deleteMe, int check)
3431 if (readOnly(isp) || deleteMe) {
3432 if (isp->volSummary && isp->volSummary->fileName) {
3434 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);
3435 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3437 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3438 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3441 unlink(isp->volSummary->fileName);
3445 Log("%s salvage was unsuccessful: read-write volume %u\n",
3446 message, isp->volumeId);
3447 Abort("Salvage of volume %u aborted\n",
3453 void AskOffline(VolumeId volumeId)
3455 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3456 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3457 Abort("Salvage aborted\n");
3461 void AskOnline(VolumeId volumeId, char *partition)
3463 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3464 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3465 volumeId, partition);
3469 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3471 /* Volume parameter is passed in case iopen is upgraded in future to
3472 * require a volume Id to be passed
3475 IHandle_t *srcH, *destH;
3476 FdHandle_t *srcFdP, *destFdP;
3479 IH_INIT(srcH, device, rwvolume, inode1);
3480 srcFdP = IH_OPEN(srcH);
3481 assert(srcFdP != NULL);
3482 IH_INIT(destH, device, rwvolume, inode2);
3483 destFdP = IH_OPEN(destH);
3485 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3486 assert(FDH_WRITE(destFdP, buf, n) == n);
3488 FDH_REALLYCLOSE(srcFdP);
3489 FDH_REALLYCLOSE(destFdP);
3495 void PrintInodeList(void)
3497 register struct ViceInodeInfo *ip;
3498 struct ViceInodeInfo *buf;
3502 assert(fstat(inodeFd, &status) == 0);
3503 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3504 assert(buf != NULL);
3505 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3506 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3507 for(ip = buf; nInodes--; ip++) {
3508 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3509 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3510 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3515 void PrintInodeSummary(void)
3518 struct InodeSummary *isp;
3520 for (i=0; i<nVolumesInInodeFile; i++) {
3521 isp = &inodeSummary[i];
3522 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3523 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3524 isp->nSpecialInodes, isp->maxUniquifier);
3528 void PrintVolumeSummary(void)
3531 struct VolumeSummary *vsp;
3533 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3534 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3542 assert(0); /* Fork is never executed in the NT code path */
3553 if (ShowLog) showlog();
3555 if (main_thread != pthread_self())
3556 pthread_exit((void*)code);
3564 int Wait(char *prog)
3568 pid = wait(&status);
3570 if (WCOREDUMP(status))
3571 Log("\"%s\" core dumped!\n", prog);
3572 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3577 static char *TimeStamp(time_t clock, int precision)
3580 static char timestamp[20];
3581 lt = localtime(&clock);
3583 (void) strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3585 (void) strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3589 void CheckLogFile(void)
3591 char oldSlvgLog[AFSDIR_PATH_MAX];
3593 #ifndef AFS_NT40_ENV
3600 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3601 strcat(oldSlvgLog, ".old");
3603 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3604 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3606 if (!logFile) { /* still nothing, use stdout */
3611 #ifndef AFS_NAMEI_ENV
3612 AFS_DEBUG_IOPS_LOG(logFile);
3617 #ifndef AFS_NT40_ENV
3618 void TimeStampLogFile(void)
3620 char stampSlvgLog[AFSDIR_PATH_MAX];
3625 lt = localtime(&now);
3626 sprintf(stampSlvgLog, "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3627 AFSDIR_SERVER_SLVGLOG_FILEPATH,
3628 lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday,
3629 lt->tm_hour, lt->tm_min, lt->tm_sec);
3631 /* try to link the logfile to a timestamped filename */
3632 /* if it fails, oh well, nothing we can do */
3633 link(AFSDIR_SERVER_SLVGLOG_FILEPATH, stampSlvgLog);
3641 #ifndef AFS_NT40_ENV
3643 printf("Can't show log since using syslog.\n");
3652 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3655 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3658 while (fgets(line, sizeof(line), logFile))
3664 void Log(const char *format, ...)
3669 va_start(args, format);
3670 #ifndef AFS_NT40_ENV
3674 (void) vsnprintf(tmp, sizeof tmp, format, args);
3675 syslog(LOG_INFO, "%s", tmp);
3679 gettimeofday(&now, 0);
3680 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3681 vfprintf(logFile, format, args);
3687 void Abort(const char *format, ...)
3691 va_start(args, format);
3692 #ifndef AFS_NT40_ENV
3696 (void) vsnprintf(tmp, sizeof tmp, format, args);
3697 syslog(LOG_INFO, "%s", tmp);
3701 vfprintf(logFile, format, args);
3703 if (ShowLog) showlog();
3712 char *ToString(char *s)
3715 p = (char *) malloc(strlen(s)+1);
3722 /* Remove the FORCESALVAGE file */
3723 void RemoveTheForce(char *path)
3725 if (!Testing && ForceSalvage) {
3726 if (chdir(path) == 0)
3727 unlink("FORCESALVAGE");
3731 #ifndef AFS_AIX32_ENV
3733 * UseTheForceLuke - see if we can use the force
3735 int UseTheForceLuke(char *path)
3739 assert(chdir(path) != -1);
3741 return (stat("FORCESALVAGE", &force) == 0);
3745 * UseTheForceLuke - see if we can use the force
3748 * The VRMIX fsck will not muck with the filesystem it is supposedly
3749 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3750 * muck directly with the root inode, which is within the normal
3752 * ListViceInodes() has a side effect of setting ForceSalvage if
3753 * it detects a need, based on root inode examination.
3755 int UseTheForceLuke(char *path)
3758 return 0; /* sorry OB1 */
3763 /* NT support routines */
3765 static char execpathname[MAX_PATH];
3766 int nt_SalvagePartition(char *partName, int jobn)
3771 if (!*execpathname) {
3772 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3773 if (!n || n == 1023)
3776 job.cj_magic = SALVAGER_MAGIC;
3777 job.cj_number = jobn;
3778 (void) strcpy(job.cj_part, partName);
3779 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3784 int nt_SetupPartitionSalvage(void *datap, int len)
3786 childJob_t *jobp = (childJob_t*)datap;
3787 char logname[AFSDIR_PATH_MAX];
3789 if (len != sizeof(childJob_t))
3791 if (jobp->cj_magic != SALVAGER_MAGIC)
3796 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3798 logFile = fopen(logname, "w");
3799 if (!logFile) logFile = stdout;
3805 #endif /* AFS_NT40_ENV */