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/afsutil.h>
171 #include <afs/fileutil.h>
172 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
180 #include <afs/afssyscalls.h>
184 #include "partition.h"
186 #include "viceinode.h"
188 #include "volinodes.h" /* header magic number, etc. stuff */
194 extern void *calloc();
196 extern char *vol_DevName();
197 static char *TimeStamp(time_t clock, int precision);
199 #define ORPH_IGNORE 0
200 #define ORPH_REMOVE 1
201 #define ORPH_ATTACH 2
204 int debug; /* -d flag */
205 int Testing=0; /* -n flag */
206 int ListInodeOption; /* -i flag */
207 int ShowRootFiles; /* -r flag */
208 int RebuildDirs; /* -sal flag */
209 int Parallel = 4; /* -para X flag */
210 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
211 int forceR = 0; /* -b flag */
212 int ShowLog = 0; /* -showlog flag */
213 int ShowSuid = 0; /* -showsuid flag */
214 int ShowMounts = 0; /* -showmounts flag */
215 int orphans = ORPH_IGNORE; /* -orphans option */
219 int useSyslog = 0; /* -syslog flag */
220 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
223 #define MAXPARALLEL 32
225 int OKToZap; /* -o flag */
226 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
227 in the volume header */
229 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
231 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
233 Device fileSysDevice; /* The device number of the current
234 partition being salvaged */
238 char *fileSysPath; /* The path of the mounted partition currently
239 being salvaged, i.e. the directory
240 containing the volume headers */
242 char *fileSysPathName; /* NT needs this to make name pretty in log. */
243 IHandle_t *VGLinkH; /* Link handle for current volume group. */
244 int VGLinkH_cnt; /* # of references to lnk handle. */
245 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
247 char *fileSysDeviceName; /* The block device where the file system
248 being salvaged was mounted */
249 char *filesysfulldev;
251 int VolumeChanged; /* Set by any routine which would change the volume in
252 a way which would require callback is to be broken if the
253 volume was put back on line by an active file server */
255 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
257 struct InodeSummary { /* Inode summary file--an entry for each
258 volume in the inode file for a partition */
259 VolId volumeId; /* Volume id */
260 VolId RWvolumeId; /* RW volume associated */
261 int index; /* index into inode file (0, 1, 2 ...) */
262 int nInodes; /* Number of inodes for this volume */
263 int nSpecialInodes; /* Number of special inodes, i.e. volume
264 header, index, etc. These are all
265 marked (viceinode.h) and will all be sorted
266 to the beginning of the information for
267 this volume. Read-only volumes should
268 ONLY have special inodes (all the other
269 inodes look as if they belong to the
270 original RW volume). */
271 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
272 This is only useful for RW volumes and is used
273 to compute a new volume uniquifier in the event
274 that the header needs to be recreated. The inode
275 uniquifier may be a truncated version of vnode
276 uniquifier (AFS_3DISPARES). The real maxUniquifer
277 is from the vnodes and later calcuated from it */
278 struct VolumeSummary *volSummary;
279 /* Either a pointer to the original volume
280 header summary, or constructed summary
283 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
284 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
285 int inodeFd; /* File descriptor for inode file */
288 struct VolumeSummary { /* Volume summary an entry for each
289 volume in a volume directory.
290 Assumption: one volume directory per
292 char *fileName; /* File name on the partition for the volume
294 struct VolumeHeader header;
295 /* volume number, rw volume number, inode
296 numbers of each major component of
298 IHandle_t *volumeInfoHandle;
299 byte wouldNeedCallback; /* set if the file server should issue
300 call backs for all the files in this volume when
301 the volume goes back on line */
305 IHandle_t *handle; /* Inode containing this index */
306 int nVnodes; /* Total number of vnodes in index */
307 int nAllocatedVnodes; /* Total number actually used */
308 int volumeBlockCount; /* Total number of blocks used by volume */
309 Inode *inodes; /* Directory only */
310 struct VnodeEssence {
311 short count; /* Number of references to vnode; MUST BE SIGNED */
312 unsigned claimed:1; /* Set when a parent directory containing an entry
313 referencing this vnode is found. The claim
314 is that the parent in "parent" can point to
315 this vnode, and no other */
316 unsigned changed:1; /* Set if any parameters (other than the count)
317 in the vnode change. It is determined if the
318 link count has changed by noting whether it is
319 0 after scanning all directories */
320 unsigned salvaged:1;/* Set if this directory vnode has already been salvaged. */
321 unsigned todelete:1;/* Set if this vnode is to be deleted (should not be claimed) */
322 afs_uint32 blockCount;
323 /* Number of blocks (1K) used by this vnode,
325 VnodeId parent; /* parent in vnode */
326 Unique unique; /* Must match entry! */
327 char *name; /* Name of directory entry */
328 int modeBits; /* File mode bits */
329 Inode InodeNumber; /* file's inode */
330 int type; /* File type */
331 int author; /* File author */
332 int owner; /* File owner */
333 int group; /* File group */
335 } vnodeInfo[nVNODECLASSES];
338 struct DirHandle dirHandle;
341 unsigned haveDot, haveDotDot;
343 int copied; /* If the copy-on-write stuff has been applied */
351 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
352 int nVolumes; /* Number of volumes (read-write and read-only)
356 /* For NT, we can fork the per partition salvagers to gain the required
357 * safety against Aborts. But there's too many complex data structures at
358 * the per volume salvager layer to easilty copy the data across.
359 * childJobNumber is resset from -1 to the job number if this is a
360 * per partition child of the main salvager. This information is passed
361 * out-of-band in the extra data area setup for the now unused parent/child
364 #define SALVAGER_MAGIC 0x00BBaaDD
365 #define NOT_CHILD -1 /* job numbers start at 0 */
366 /* If new options need to be passed to child, add them here. */
373 /* Child job this process is running. */
374 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD};
376 int nt_SalvagePartition(char *partName, int jobn);
377 int nt_SetupPartitionSalvage(void *datap, int len);
380 struct InodeSummary *svgp_inodeSummaryp;
390 /* Forward declarations */
391 void Log(), Abort(), Exit();
393 int Wait(char *prog);
394 char * ToString(char *s);
395 void AskOffline(VolumeId volumeId);
396 void AskOnline(VolumeId volumeId, char *partition);
397 void CheckLogFile(void);
398 void ClearROInUseBit(struct VolumeSummary *summary);
399 void CopyAndSalvage(register struct DirSummary *dir);
400 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
401 void CopyOnWrite(register struct DirSummary *dir);
402 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
403 register struct InodeSummary * summary);
404 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
405 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
407 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
408 void GetVolumeSummary(VolumeId singleVolumeNumber);
409 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
411 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
414 void ObtainSalvageLock(void);
415 void PrintInodeList(void);
416 void PrintInodeSummary(void);
417 void PrintVolumeSummary(void);
418 int QuickCheck(register struct InodeSummary *isp, int nVols);
419 void RemoveTheForce(char *path);
420 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
421 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
423 void SalvageFileSysParallel(struct DiskPartition *partP);
424 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
425 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
426 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
427 int check, int *deleteMe);
428 int SalvageIndex(Inode ino, VnodeClass class, int RW,
429 register struct ViceInodeInfo *ip,
430 int nInodes, struct VolumeSummary *volSummary, int check);
431 int SalvageVnodes(register struct InodeSummary *rwIsp,
432 register struct InodeSummary * thisIsp,
433 register struct ViceInodeInfo * inodes, int check);
434 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
435 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
437 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
439 #define SalvageVolumeGroup DoSalvageVolumeGroup
441 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
442 register struct ViceInodeInfo *inodes,
443 int RW, int check, int *deleteMe);
445 int UseTheForceLuke(char *path);
447 static int IsVnodeOrphaned(VnodeId vnode);
449 /* Uniquifier stored in the Inode */
450 static Unique IUnique(u)
454 return(u & 0x3fffff);
456 #if defined(AFS_SGI_EXMAG)
457 return(u & SGI_UNIQMASK);
460 #endif /* AFS_SGI_EXMAG */
464 static int BadError(aerror)
465 register int aerror; {
466 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
468 return 0; /* otherwise may be transient, e.g. EMFILE */
474 struct cmd_syndesc *as;
476 register struct cmd_item *ti;
477 char pname[100], *temp;
478 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0, i;
479 struct DiskPartition *partP;
481 #ifdef AFS_SGI_VNODE_GLUE
482 if (afs_init_kernel_config(-1) <0) {
483 printf("Can't determine NUMA configuration, not starting salvager.\n");
489 for (i = 0; i < CMD_MAXPARMS; i++) {
490 if (as->parms[i].items) {
496 printf("Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!\n");
499 #endif /* FAST_RESTART */
500 if (ti = as->parms[0].items) { /* -partition */
502 strncpy(pname, ti->data, 100);
504 if (ti = as->parms[1].items) { /* -volumeid */
506 printf("You must also specify '-partition' option with the '-volumeid' option\n");
510 vid = atoi(ti->data);
512 if (as->parms[2].items) /* -debug */
514 if (as->parms[3].items) /* -nowrite */
516 if (as->parms[4].items) /* -inodes */
518 if (as->parms[5].items) /* -force */
520 if (as->parms[6].items) /* -oktozap */
522 if (as->parms[7].items) /* -rootinodes */
524 if (as->parms[8].items) /* -RebuildDirs */
526 if (as->parms[9].items) /* -ForceReads */
528 if (ti = as->parms[10].items) {/* -Parallel # */
530 if (strncmp(temp,"all",3) == 0) {
534 if (strlen(temp) != 0) {
535 Parallel = atoi(temp);
536 if (Parallel < 1) Parallel = 1;
537 if (Parallel > MAXPARALLEL) {
538 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
539 Parallel = MAXPARALLEL;
543 if (ti = as->parms[11].items) {/* -tmpdir */
547 dirp = opendir(tmpdir);
549 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
554 if (ti = as->parms[12].items) /* -showlog */
556 if (ti = as->parms[13].items) { /* -log */
561 if (ti = as->parms[14].items) { /* -showmounts */
566 if (ti = as->parms[15].items) { /* -orphans */
568 orphans = ORPH_IGNORE;
569 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
570 orphans = ORPH_REMOVE;
571 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
572 orphans = ORPH_ATTACH;
575 #ifndef AFS_NT40_ENV /* ignore options on NT */
576 if ( ti = as->parms[16].items) { /* -syslog */
580 if ( ti = as->parms[17].items) { /* -syslogfacility */
581 useSyslogFacility = atoi(ti->data);
587 if (ti = as->parms[18].items) { /* -DontSalvage */
588 printf("Exiting immediately without salvage. Look into the FileLog");
589 printf(" to find volumes which really need to be salvaged!\n");
592 #endif /* FAST_RESTART */
594 /* Note: if seemvol we initialize this as a standard volume utility: this has the
595 implication that the file server may be running; negotations have to be made with
596 the file server in this case to take the read write volume and associated read-only
597 volumes off line before salvaging */
600 if (afs_winsockInit()<0) {
601 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
602 AFSDIR_SALVAGER_FILE, 0);
603 Log("Failed to initailize winsock, exiting.\n");
608 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
611 if (myjob.cj_number != NOT_CHILD) {
614 (void) strcpy(pname, myjob.cj_part);
619 for (partP = DiskPartitionList; partP; partP = partP->next) {
620 SalvageFileSysParallel(partP);
622 SalvageFileSysParallel(0);
625 partP = VGetPartition(pname, 0);
627 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
632 SalvageFileSys(partP, 0);
634 /* Salvage individual volume */
636 Log("salvage: invalid volume id specified; salvage aborted\n");
639 SalvageFileSys (partP, vid);
647 #include "AFS_component_version_number.c"
651 char *save_args[MAX_ARGS];
653 pthread_t main_thread;
659 struct cmd_syndesc *ts;
661 char commandLine[150];
664 extern char cml_version_number[];
668 * The following signal action for AIX is necessary so that in case of a
669 * crash (i.e. core is generated) we can include the user's data section
670 * in the core dump. Unfortunately, by default, only a partial core is
671 * generated which, in many cases, isn't too useful.
673 struct sigaction nsa;
675 sigemptyset(&nsa.sa_mask);
676 nsa.sa_handler = SIG_DFL;
677 nsa.sa_flags = SA_FULLDUMP;
678 sigaction(SIGABRT, &nsa, NULL);
679 sigaction(SIGSEGV, &nsa, NULL);
682 /* Initialize directory paths */
683 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
685 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
687 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
691 main_thread = pthread_self();
692 if (spawnDatap && spawnDataLen) {
693 /* This is a child per partition salvager. Don't setup log or
694 * try to lock the salvager lock.
696 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
701 for (commandLine[0] = '\0', i=0; i<argc; i++) {
702 if (i > 0) strcat(commandLine, " ");
703 strcat(commandLine, argv[i]);
706 /* All entries to the log will be appended. Useful if there are
707 * multiple salvagers appending to the log.
712 #ifdef AFS_LINUX20_ENV
713 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
715 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
721 if (geteuid() != 0) {
722 printf("Salvager must be run as root.\n");
728 /* bad for normal help flag processing, but can do nada */
730 fprintf(logFile, "%s\n", cml_version_number);
731 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
733 /* Get and hold a lock for the duration of the salvage to make sure
734 * that no other salvage runs at the same time. The routine
735 * VInitVolumePackage (called below) makes sure that a file server or
736 * other volume utilities don't interfere with the salvage.
743 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
744 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
745 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
746 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
747 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
748 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
749 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
750 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
751 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
752 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
753 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
754 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
755 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
756 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
757 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
758 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
759 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
761 /* note - syslog isn't avail on NT, but if we make it conditional, have
762 to deal with screwy offsets for cmd params */
763 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
764 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
767 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");
768 #endif /* FAST_RESTART */
769 err = cmd_Dispatch(argc, argv);
773 /* Get the salvage lock if not already held. Hold until process exits. */
774 void ObtainSalvageLock(void)
779 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
780 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
782 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
784 "salvager: There appears to be another salvager running! Aborted.\n");
788 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
789 assert(salvageLock >= 0);
790 #ifdef AFS_DARWIN_ENV
791 if (flock(salvageLock, LOCK_EX) == -1) {
793 if (lockf(salvageLock, F_LOCK, 0) == -1) {
796 "salvager: There appears to be another salvager running! Aborted.\n");
803 #ifdef AFS_SGI_XFS_IOPS_ENV
804 /* Check if the given partition is mounted. For XFS, the root inode is not a
805 * constant. So we check the hard way.
807 int IsPartitionMounted(char *part)
810 struct mntent *mntent;
812 assert(mntfp = setmntent(MOUNTED, "r"));
813 while (mntent = getmntent(mntfp)) {
814 if (!strcmp(part, mntent->mnt_dir))
819 return mntent ? 1 : 1;
822 /* Check if the given inode is the root of the filesystem. */
823 #ifndef AFS_SGI_XFS_IOPS_ENV
824 int IsRootInode(status)
827 /* The root inode is not a fixed value in XFS partitions. So we need to see if
828 * the partition is in the list of mounted partitions. This only affects the
829 * SalvageFileSys path, so we check there.
831 return (status->st_ino == ROOTINODE);
836 #ifndef AFS_NAMEI_ENV
837 /* We don't want to salvage big files filesystems, since we can't put volumes on
840 int CheckIfBigFilesFS(mountPoint, devName)
844 struct superblock fs;
847 if (strncmp(devName, "/dev/", 5)) {
848 (void) sprintf(name, "/dev/%s", devName);
851 (void) strcpy(name, devName);
854 if (ReadSuper(&fs, name)<0) {
855 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
858 if (IsBigFilesFileSystem(&fs)) {
859 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
868 #define HDSTR "\\Device\\Harddisk"
869 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
870 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
877 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
879 if (strncmp(res, HDSTR, HDLEN)) {
882 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
883 res, HDSTR, p1->devName);
887 d1 = atoi(&res[HDLEN]);
889 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
891 if (strncmp(res, HDSTR, HDLEN)) {
894 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
895 res, HDSTR, p2->devName);
899 d2 = atoi(&res[HDLEN]);
904 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
907 /* This assumes that two partitions with the same device number divided by
908 * PartsPerDisk are on the same disk.
910 void SalvageFileSysParallel(struct DiskPartition *partP)
913 struct DiskPartition *partP;
914 int pid; /* Pid for this job */
915 int jobnumb; /* Log file job number */
916 struct job *nextjob; /* Next partition on disk to salvage */
918 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
919 struct job *thisjob = 0;
920 static int numjobs = 0;
921 static int jobcount = 0;
927 char logFileName[256];
931 /* We have a partition to salvage. Copy it into thisjob */
932 thisjob = (struct job *) malloc(sizeof(struct job));
934 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
937 memset(thisjob, 0, sizeof(struct job));
938 thisjob->partP = partP;
939 thisjob->jobnumb = jobcount;
942 else if (jobcount == 0) {
943 /* We are asking to wait for all jobs (partp == 0), yet we never
946 Log("No file system partitions named %s* found; not salvaged\n",
947 VICE_PARTITION_PREFIX);
951 if (debug || Parallel == 1) {
953 SalvageFileSys(thisjob->partP, 0);
960 /* Check to see if thisjob is for a disk that we are already
961 * salvaging. If it is, link it in as the next job to do. The
962 * jobs array has 1 entry per disk being salvages. numjobs is
963 * the total number of disks currently being salvaged. In
964 * order to keep thejobs array compact, when a disk is
965 * completed, the hightest element in the jobs array is moved
966 * down to now open slot.
968 for (j=0; j<numjobs; j++) {
969 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
970 /* On same disk, add it to this list and return */
971 thisjob->nextjob = jobs[j]->nextjob;
972 jobs[j]->nextjob = thisjob;
979 /* Loop until we start thisjob or until all existing jobs are finished */
980 while ( thisjob || (!partP && (numjobs > 0)) ) {
981 startjob = -1; /* No new job to start */
983 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
984 /* Either the max jobs are running or we have to wait for all
985 * the jobs to finish. In either case, we wait for at least one
986 * job to finish. When it's done, clean up after it.
988 pid = wait(&wstatus);
990 for (j=0; j<numjobs; j++) { /* Find which job it is */
991 if (pid == jobs[j]->pid) break;
994 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
995 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
998 numjobs--; /* job no longer running */
999 oldjob = jobs[j]; /* remember */
1000 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1001 free(oldjob); /* free the old job */
1003 /* If there is another partition on the disk to salvage, then
1004 * say we will start it (startjob). If not, then put thisjob there
1005 * and say we will start it.
1007 if (jobs[j]) { /* Another partitions to salvage */
1008 startjob = j; /* Will start it */
1009 } else { /* There is not another partition to salvage */
1011 jobs[j] = thisjob; /* Add thisjob */
1013 startjob = j; /* Will start it */
1015 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1016 startjob = -1; /* Don't start it - already running */
1020 /* We don't have to wait for a job to complete */
1022 jobs[numjobs] = thisjob; /* Add this job */
1024 startjob = numjobs; /* Will start it */
1028 /* Start up a new salvage job on a partition in job slot "startjob" */
1029 if (startjob != -1) {
1031 Log("Starting salvage of file system partition %s\n",
1032 jobs[startjob]->partP->name);
1034 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1035 * commands and pass the child job number via the data path.
1037 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1038 jobs[startjob]->jobnumb);
1039 jobs[startjob]->pid = pid;
1044 jobs[startjob]->pid = pid;
1050 for (fd =0; fd < 16; fd++) close(fd);
1051 open("/", 0); dup2(0, 1); dup2(0, 2);
1052 #ifndef AFS_NT40_ENV
1054 openlog(NULL, LOG_PID, useSyslogFacility);
1058 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1059 logFile = fopen(logFileName, "w");
1061 if (!logFile) logFile = stdout;
1063 SalvageFileSys1(jobs[startjob]->partP, 0);
1068 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1070 /* If waited for all jobs to complete, now collect log files and return */
1071 #ifndef AFS_NT40_ENV
1072 if ( ! useSyslog ) /* if syslogging - no need to collect */
1075 for (i=0; i<jobcount; i++) {
1076 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1077 if (passLog = fopen(logFileName, "r")) {
1078 while(fgets(buf, sizeof(buf), passLog)) {
1079 fputs(buf, logFile);
1083 (void)unlink(logFileName);
1091 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1093 if (!canfork || debug || Fork() == 0) {
1094 SalvageFileSys1(partP, singleVolumeNumber);
1095 if (canfork && !debug) {
1101 Wait("SalvageFileSys");
1104 char *get_DevName(pbuffer, wpath)
1105 char *wpath, *pbuffer;
1107 char pbuf[128], *ptr;
1108 strcpy(pbuf, pbuffer);
1109 ptr = (char *)strrchr(pbuf, '/');
1112 strcpy(wpath, pbuf);
1115 ptr = (char *)strrchr(pbuffer, '/');
1117 strcpy(pbuffer, ptr+1);
1123 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1126 char inodeListPath[50];
1127 static char tmpDevName[100];
1128 static char wpath[100];
1129 struct VolumeSummary *vsp, *esp;
1132 fileSysPartition = partP;
1133 fileSysDevice = fileSysPartition->device;
1134 fileSysPathName = VPartitionPath(fileSysPartition);
1137 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1138 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1139 name = partP->devName;
1141 fileSysPath = fileSysPathName;
1142 strcpy(tmpDevName, partP->devName);
1143 name = get_DevName(tmpDevName, wpath);
1144 fileSysDeviceName = name;
1145 filesysfulldev = wpath;
1148 VLockPartition(partP->name);
1149 if (singleVolumeNumber || ForceSalvage)
1152 ForceSalvage = UseTheForceLuke(fileSysPath);
1154 if (singleVolumeNumber) {
1155 if (!VConnectFS()) {
1156 Abort("Couldn't connect to file server\n");
1158 AskOffline(singleVolumeNumber);
1161 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1163 Log("***Forced salvage of all volumes on this partition***\n");
1168 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1175 assert((dirp = opendir(fileSysPath)) != NULL);
1176 while (dp = readdir(dirp)) {
1177 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1178 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1180 Log("Removing old salvager temp files %s\n", dp->d_name);
1181 strcpy(npath, fileSysPath);
1183 strcat(npath, dp->d_name);
1189 tdir = (tmpdir ? tmpdir : fileSysPath);
1191 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1192 (void) strcpy(inodeListPath, _tempnam(tdir, "salvage.inodes."));
1194 sprintf(inodeListPath, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1196 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1197 unlink(inodeListPath);
1201 /* Using nt_unlink here since we're really using the delete on close
1202 * semantics of unlink. In most places in the salvager, we really do
1203 * mean to unlink the file at that point. Those places have been
1204 * modified to actually do that so that the NT crt can be used there.
1206 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1207 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1209 inodeFd = open(inodeListPath, O_RDONLY);
1210 unlink(inodeListPath);
1213 Abort("Temporary file %s is missing...\n",
1215 if (ListInodeOption) {
1219 /* enumerate volumes in the partition.
1220 figure out sets of read-only + rw volumes.
1221 salvage each set, read-only volumes first, then read-write.
1222 Fix up inodes on last volume in set (whether it is read-write
1225 GetVolumeSummary(singleVolumeNumber);
1227 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1228 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1229 for (j=i; j < nVolumesInInodeFile
1230 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1231 VolumeId vid = inodeSummary[j].volumeId;
1232 struct VolumeSummary *tsp;
1233 /* Scan volume list (from partition root directory) looking for the
1234 current rw volume number in the volume list from the inode scan.
1235 If there is one here that is not in the inode volume list,
1237 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1239 DeleteExtraVolumeHeaderFile(vsp);
1241 /* Now match up the volume summary info from the root directory with the
1242 entry in the volume list obtained from scanning inodes */
1243 inodeSummary[j].volSummary = NULL;
1244 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1245 if (tsp->header.id == vid) {
1246 inodeSummary[j].volSummary = tsp;
1252 /* Salvage the group of volumes (several read-only + 1 read/write)
1253 * starting with the current read-only volume we're looking at.
1255 SalvageVolumeGroup(&inodeSummary[i], j-i);
1258 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1259 for ( ; vsp<esp; vsp++) {
1261 DeleteExtraVolumeHeaderFile(vsp);
1264 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1265 RemoveTheForce(fileSysPath);
1267 if (!Testing && singleVolumeNumber) {
1268 AskOnline(singleVolumeNumber, fileSysPartition->name);
1270 /* Step through the volumeSummary list and set all volumes on-line.
1271 * The volumes were taken off-line in GetVolumeSummary.
1273 for (j=0; j<nVolumes; j++) {
1274 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1279 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1280 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1283 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1286 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1288 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1289 vsp->fileName, (Testing? "would have been ":""));
1291 unlink(vsp->fileName);
1295 CompareInodes(_p1,_p2)
1296 const void *_p1,*_p2;
1298 register const struct ViceInodeInfo *p1 = _p1;
1299 register const struct ViceInodeInfo *p2 = _p2;
1300 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1301 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1302 VolumeId p1rwid, p2rwid;
1303 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1304 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1305 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1306 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1307 if (p1rwid < p2rwid)
1309 if (p1rwid > p2rwid)
1311 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1312 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1313 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1314 return (p1->u.special.type < p2->u.special.type? -1: 1);
1315 if (p1->u.vnode.volumeId == p1rwid)
1317 if (p2->u.vnode.volumeId == p2rwid)
1319 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1321 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1322 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1323 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1325 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1327 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1329 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1331 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1333 /* The following tests are reversed, so that the most desirable
1334 of several similar inodes comes first */
1335 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1336 #ifdef AFS_3DISPARES
1337 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1338 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1341 #ifdef AFS_SGI_EXMAG
1342 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1343 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1348 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1349 #ifdef AFS_3DISPARES
1350 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1351 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1354 #ifdef AFS_SGI_EXMAG
1355 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1356 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1361 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1362 #ifdef AFS_3DISPARES
1363 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1364 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1367 #ifdef AFS_SGI_EXMAG
1368 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1369 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1374 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1375 #ifdef AFS_3DISPARES
1376 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1377 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1380 #ifdef AFS_SGI_EXMAG
1381 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1382 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1390 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1391 register struct InodeSummary * summary)
1393 int volume = ip->u.vnode.volumeId;
1394 int rwvolume = volume;
1395 register n, nSpecial;
1396 register Unique maxunique;
1399 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1401 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1403 rwvolume = ip->u.special.parentId;
1404 /* This isn't quite right, as there could (in error) be different
1405 parent inodes in different special vnodes */
1408 if (maxunique < ip->u.vnode.vnodeUniquifier)
1409 maxunique = ip->u.vnode.vnodeUniquifier;
1413 summary->volumeId = volume;
1414 summary->RWvolumeId = rwvolume;
1415 summary->nInodes =n;
1416 summary->nSpecialInodes = nSpecial;
1417 summary->maxUniquifier = maxunique;
1420 int OnlyOneVolume(inodeinfo, singleVolumeNumber)
1421 struct ViceInodeInfo *inodeinfo;
1422 VolumeId singleVolumeNumber;
1424 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1425 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1426 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1431 * Collect list of inodes in file named by path. If a truly fatal error,
1432 * unlink the file and abort. For lessor errors, return -1. The file will
1433 * be unlinked by the caller.
1435 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1439 struct ViceInodeInfo *ip;
1440 struct InodeSummary summary;
1441 char summaryFileName[50];
1444 char *dev = fileSysPath;
1445 char *wpath = fileSysPath;
1447 char *dev = fileSysDeviceName;
1448 char *wpath = filesysfulldev;
1450 char *part = fileSysPath;
1453 /* This file used to come from vfsck; cobble it up ourselves now... */
1454 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1456 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1461 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1463 if (forceSal && !ForceSalvage) {
1464 Log("***Forced salvage of all volumes on this partition***\n");
1467 inodeFd = open(path, O_RDWR);
1468 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1470 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1472 tdir = (tmpdir ? tmpdir : part);
1474 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1475 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1477 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1479 summaryFile = fopen(summaryFileName, "a+");
1480 if (summaryFile == NULL) {
1483 Abort("Unable to create inode summary file\n");
1485 if (!canfork || debug || Fork() == 0) {
1487 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1489 fclose(summaryFile); close(inodeFd);
1490 unlink(summaryFileName);
1491 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1492 RemoveTheForce(fileSysPath);
1493 Log("%s vice inodes on %s; not salvaged\n",
1494 singleVolumeNumber? "No applicable": "No", dev);
1497 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1499 fclose(summaryFile); close(inodeFd);
1501 unlink(summaryFileName);
1502 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1504 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1505 fclose(summaryFile); close(inodeFd);
1507 unlink(summaryFileName);
1508 Abort("Unable to read inode table; %s not salvaged\n", dev);
1510 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1511 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1512 write(inodeFd, ip, status.st_size) != status.st_size) {
1513 fclose(summaryFile); close(inodeFd);
1515 unlink(summaryFileName);
1516 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1520 CountVolumeInodes(ip, nInodes, &summary);
1521 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1522 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1523 fclose(summaryFile); close(inodeFd);
1526 summary.index += (summary.nInodes);
1527 nInodes -= summary.nInodes;
1528 ip += summary.nInodes;
1530 /* Following fflush is not fclose, because if it was debug mode would not work */
1531 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1532 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1533 fclose(summaryFile); close(inodeFd);
1536 if (canfork && !debug) {
1542 if (Wait("Inode summary") == -1) {
1543 fclose(summaryFile); close(inodeFd);
1545 unlink(summaryFileName);
1546 Exit(1); /* salvage of this partition aborted */
1549 assert(fstat(fileno(summaryFile), &status) != -1);
1550 if ( status.st_size != 0 ) {
1552 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1553 assert(inodeSummary != NULL);
1554 /* For GNU we need to do lseek to get the file pointer moved. */
1555 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1556 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1557 assert(ret == status.st_size);
1559 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1560 fclose(summaryFile);
1562 unlink(summaryFileName);
1566 /* Comparison routine for volume sort.
1567 This is setup so that a read-write volume comes immediately before
1568 any read-only clones of that volume */
1569 CompareVolumes(_p1,_p2)
1570 const void *_p1,*_p2;
1572 register const struct VolumeSummary *p1 = _p1;
1573 register const struct VolumeSummary *p2 = _p2;
1574 if (p1->header.parent != p2->header.parent)
1575 return p1->header.parent < p2->header.parent? -1: 1;
1576 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1578 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1580 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1583 void GetVolumeSummary(VolumeId singleVolumeNumber)
1586 afs_int32 nvols = 0;
1587 struct VolumeSummary *vsp, vs;
1588 struct VolumeDiskHeader diskHeader;
1591 /* Get headers from volume directory */
1592 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1593 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1594 if (!singleVolumeNumber) {
1595 while (dp = readdir(dirp)) {
1596 char *p = dp->d_name;
1597 p = strrchr(dp->d_name, '.');
1598 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1600 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1601 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1602 == sizeof (diskHeader) &&
1603 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1604 DiskToVolumeHeader(&vs.header, &diskHeader);
1611 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1615 if (!nvols) nvols = 1;
1616 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1618 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1619 assert(volumeSummaryp != NULL);
1622 vsp = volumeSummaryp;
1623 while (dp = readdir(dirp)) {
1624 char *p = dp->d_name;
1625 p = strrchr(dp->d_name, '.');
1626 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1629 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1630 || read(fd, &diskHeader, sizeof (diskHeader))
1631 != sizeof (diskHeader)
1632 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1637 if (!singleVolumeNumber) {
1638 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1639 dp->d_name, (Testing? "it would have been ":""));
1645 char nameShouldBe[64];
1646 DiskToVolumeHeader(&vsp->header, &diskHeader);
1647 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1648 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1651 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1652 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1653 if (singleVolumeNumber)
1654 AskOffline(vsp->header.id);
1655 if (strcmp(nameShouldBe, dp->d_name)) {
1656 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 ":""));
1661 vsp->fileName = ToString(dp->d_name);
1671 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1674 /* Find the link table. This should be associated with the RW volume or, if
1675 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1677 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1678 struct ViceInodeInfo *allInodes)
1681 struct ViceInodeInfo *ip;
1683 for (i=0; i<nVols; i++) {
1684 ip = allInodes + isp[i].index;
1685 for (j=0; j<isp[i].nSpecialInodes; j++) {
1686 if (ip[j].u.special.type == VI_LINKTABLE)
1687 return ip[j].inodeNumber;
1693 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1695 struct versionStamp version;
1698 if (!VALID_INO(ino))
1699 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1700 isp->volumeId, INODESPECIAL,
1701 VI_LINKTABLE, isp->RWvolumeId);
1702 if (!VALID_INO(ino))
1703 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1704 isp->RWvolumeId, errno);
1705 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1706 fdP = IH_OPEN(VGLinkH);
1708 Abort("Can't open link table for volume %u (error = %d)\n",
1709 isp->RWvolumeId, errno);
1711 if (FDH_TRUNC(fdP, 0)<0)
1712 Abort("Can't truncate link table for volume %u (error = %d)\n",
1713 isp->RWvolumeId, errno);
1715 version.magic = LINKTABLEMAGIC;
1716 version.version = LINKTABLEVERSION;
1718 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1720 Abort("Can't truncate link table for volume %u (error = %d)\n",
1721 isp->RWvolumeId, errno);
1723 FDH_REALLYCLOSE(fdP);
1725 /* If the volume summary exits (i.e., the V*.vol header file exists),
1726 * then set this inode there as well.
1728 if (isp->volSummary)
1729 isp->volSummary->header.linkTable = ino;
1735 void *nt_SVG(void *arg)
1737 SVGParms_t *parms = (SVGParms_t*)arg;
1738 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1742 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1745 pthread_attr_t tattr;
1749 /* Initialize per volume global variables, even if later code does so */
1753 memset(&VolInfo, 0, sizeof(VolInfo));
1755 parms.svgp_inodeSummaryp = isp;
1756 parms.svgp_count = nVols;
1757 code = pthread_attr_init(&tattr);
1759 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1763 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1765 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1769 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1771 Log("Failed to create thread to salvage volume group %u\n",
1775 (void) pthread_join(tid, NULL);
1777 #endif /* AFS_NT40_ENV */
1779 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1781 struct ViceInodeInfo *inodes,*allInodes,*ip;
1782 int i, totalInodes, size, salvageTo;
1786 int dec_VGLinkH = 0;
1788 FdHandle_t *fdP = NULL;
1791 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1792 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1793 if (!ForceSalvage && QuickCheck(isp, nVols))
1796 if (ShowMounts && !haveRWvolume)
1798 if (canfork && !debug && Fork() != 0) {
1799 (void) Wait("Salvage volume group");
1802 for (i = 0, totalInodes = 0; i<nVols; i++)
1803 totalInodes += isp[i].nInodes;
1804 size = totalInodes * sizeof (struct ViceInodeInfo);
1805 inodes = (struct ViceInodeInfo *) malloc(size);
1806 allInodes = inodes - isp->index; /* this would the base of all the inodes
1807 for the partition, if all the inodes
1808 had been read into memory */
1809 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1)
1810 assert(read(inodeFd,inodes,size) == size)
1812 /* Don't try to salvage a read write volume if there isn't one on this
1814 salvageTo = haveRWvolume? 0:1;
1816 #ifdef AFS_NAMEI_ENV
1817 ino = FindLinkHandle(isp, nVols, allInodes);
1818 if (VALID_INO(ino)) {
1819 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1820 fdP = IH_OPEN(VGLinkH);
1822 if (!VALID_INO(ino) || fdP == NULL) {
1823 Log("%s link table for volume %u.\n",
1824 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1826 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1829 CreateLinkTable(isp, ino);
1833 FDH_REALLYCLOSE(fdP);
1835 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1838 /* Salvage in reverse order--read/write volume last; this way any
1839 Inodes not referenced by the time we salvage the read/write volume
1840 can be picked up by the read/write volume */
1841 /* ACTUALLY, that's not done right now--the inodes just vanish */
1842 for (i = nVols-1; i>=salvageTo; i--) {
1844 struct InodeSummary *lisp = &isp[i];
1845 #ifdef AFS_NAMEI_ENV
1846 /* If only the RO is present on this partition, the link table
1847 * shows up as a RW volume special file. Need to make sure the
1848 * salvager doesn't try to salvage the non-existent RW.
1850 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1851 /* If this only special inode is the link table, continue */
1852 if (inodes->u.special.type == VI_LINKTABLE) {
1858 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1859 lisp->volumeId, (Testing?"(READONLY mode)":""));
1860 /* Check inodes twice. The second time do things seriously. This
1861 way the whole RO volume can be deleted, below, if anything goes wrong */
1862 for (check = 1; check>=0; check--) {
1864 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1865 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1866 if (rw && deleteMe) {
1867 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1868 volume won't be called */
1874 if (rw && check == 1)
1876 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1877 MaybeZapVolume(lisp,"Vnode index", 0, check);
1883 /* Fix actual inode counts */
1885 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1886 static int TraceBadLinkCounts = 0;
1887 #ifdef AFS_NAMEI_ENV
1888 if (VGLinkH->ih_ino == ip->inodeNumber) {
1889 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1890 VGLinkH_p1 = ip->u.param[0];
1891 continue; /* Deal with this last. */
1894 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1895 TraceBadLinkCounts--; /* Limit reports, per volume */
1896 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1897 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1898 ip->byteCount, ip->u.param[0], ip->u.param[1],
1899 ip->u.param[2], ip->u.param[3]);
1901 while (ip->linkCount > 0) {
1902 /* below used to assert, not break */
1904 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1905 Log ("idec failed. inode %s errno %d\n",
1906 PrintInode(NULL, ip->inodeNumber), errno);
1912 while (ip->linkCount < 0) {
1913 /* these used to be asserts */
1915 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1916 Log ("iinc failed. inode %s errno %d\n",
1917 PrintInode(NULL, ip->inodeNumber) ,errno);
1924 #ifdef AFS_NAMEI_ENV
1925 while (dec_VGLinkH > 0) {
1926 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1927 Log("idec failed on link table, errno = %d\n", errno);
1931 while (dec_VGLinkH < 0) {
1932 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1933 Log("iinc failed on link table, errno = %d\n", errno);
1940 /* Directory consistency checks on the rw volume */
1942 SalvageVolume(isp, VGLinkH);
1943 IH_RELEASE(VGLinkH);
1945 if (canfork && !debug) {
1951 int QuickCheck(register struct InodeSummary *isp, int nVols)
1953 /* Check headers BEFORE forking */
1957 for (i = 0; i<nVols; i++) {
1958 struct VolumeSummary *vs = isp[i].volSummary;
1959 VolumeDiskData volHeader;
1961 /* Don't salvage just because phantom rw volume is there... */
1962 /* (If a read-only volume exists, read/write inodes must also exist) */
1963 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1967 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1968 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1969 == sizeof(volHeader)
1970 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1971 && volHeader.dontSalvage == DONT_SALVAGE
1972 && volHeader.needsSalvaged == 0
1973 && volHeader.destroyMe == 0) {
1974 if (volHeader.inUse == 1) {
1975 volHeader.inUse = 0;
1976 volHeader.inService = 1;
1978 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1979 != sizeof(volHeader)) {
1996 /* SalvageVolumeHeaderFile
1998 * Salvage the top level V*.vol header file. Make sure the special files
1999 * exist and that there are no duplicates.
2001 * Calls SalvageHeader for each possible type of volume special file.
2004 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2005 register struct ViceInodeInfo *inodes,
2006 int RW, int check, int *deleteMe)
2010 register struct ViceInodeInfo *ip;
2011 int allinodesobsolete = 1;
2012 struct VolumeDiskHeader diskHeader;
2016 memset(&tempHeader, 0, sizeof(tempHeader));
2017 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2018 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2019 tempHeader.id = isp->volumeId;
2020 tempHeader.parent = isp->RWvolumeId;
2021 /* Check for duplicates (inodes are sorted by type field) */
2022 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2023 ip = &inodes[isp->index+i];
2024 if (ip->u.special.type == (ip+1)->u.special.type) {
2025 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2029 for (i = 0; i<isp->nSpecialInodes; i++) {
2030 ip = &inodes[isp->index+i];
2031 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2033 Log("Rubbish header inode\n");
2036 Log("Rubbish header inode; deleted\n");
2038 else if (!stuff[ip->u.special.type-1].obsolete) {
2039 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2040 if (!check && ip->u.special.type != VI_LINKTABLE)
2041 ip->linkCount--; /* Keep the inode around */
2042 allinodesobsolete = 0;
2046 if (allinodesobsolete) {
2053 VGLinkH_cnt ++; /* one for every header. */
2055 if (!RW && !check && isp->volSummary) {
2056 ClearROInUseBit(isp->volSummary);
2060 for (i = 0; i< MAXINODETYPE; i++) {
2061 if (stuff[i].inodeType == VI_LINKTABLE) {
2062 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2063 * And we may have recreated the link table earlier, so set the
2064 * RW header as well.
2066 if (VALID_INO(VGLinkH->ih_ino)) {
2067 *stuff[i].inode = VGLinkH->ih_ino;
2071 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2075 if (isp->volSummary == NULL) {
2077 sprintf(name, VFORMAT, isp->volumeId);
2079 Log("No header file for volume %u\n", isp->volumeId);
2082 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2083 isp->volumeId, (Testing?"it would have been ":""),
2084 fileSysPathName, name);
2085 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2086 assert(headerFd != -1)
2087 isp->volSummary = (struct VolumeSummary *)
2088 malloc(sizeof(struct VolumeSummary));
2089 isp->volSummary->fileName = ToString(name);
2093 /* hack: these two fields are obsolete... */
2094 isp->volSummary->header.volumeAcl = 0;
2095 isp->volSummary->header.volumeMountTable = 0;
2097 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2098 /* We often remove the name before calling us, so we make a fake one up */
2099 if (isp->volSummary->fileName) {
2100 strcpy(name, isp->volSummary->fileName);
2102 sprintf(name, VFORMAT, isp->volumeId);
2103 isp->volSummary->fileName = ToString(name);
2106 Log("Header file %s is damaged or no longer valid%s\n",
2107 name, (check ? "" : "; repairing"));
2111 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2112 assert(headerFd != -1)
2116 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2118 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2120 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2121 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2122 != sizeof(struct VolumeDiskHeader)) {
2123 Log("Couldn't rewrite volume header file!\n");
2130 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2131 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2135 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2136 int check, int *deleteMe)
2139 VolumeDiskData volumeInfo;
2140 struct versionStamp fileHeader;
2149 #ifndef AFS_NAMEI_ENV
2150 if ( sp->inodeType == VI_LINKTABLE)
2153 if (*(sp->inode) == 0) {
2155 Log("Missing inode in volume header (%s)\n", sp->description);
2158 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2159 sp->description, (Testing ? "it would have recreated it": "recreating"));
2161 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2162 isp->volumeId, INODESPECIAL,
2163 sp->inodeType, isp->RWvolumeId);
2164 if (!VALID_INO(*(sp->inode)))
2165 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2166 sp->description, errno);
2171 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2172 fdP = IH_OPEN(specH);
2173 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2174 /* bail out early and destroy the volume */
2175 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2177 if (deleteMe) *deleteMe = 1;
2182 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2183 sp->description, errno);
2186 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2187 || header.fileHeader.magic != sp->stamp.magic)) {
2189 Log("Part of the header (%s) is corrupted\n", sp->description);
2190 FDH_REALLYCLOSE(fdP);
2194 Log("Part of the header (%s) is corrupted; recreating\n",
2198 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2201 FDH_REALLYCLOSE(fdP);
2205 if (recreate && !Testing) {
2207 Abort("Internal error: recreating volume header (%s) in check mode\n",
2209 code = FDH_TRUNC(fdP, 0);
2211 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2212 sp->description, errno);
2214 /* The following code should be moved into vutil.c */
2215 if (sp->inodeType == VI_VOLINFO) {
2217 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2218 header.volumeInfo.stamp = sp->stamp;
2219 header.volumeInfo.id = isp->volumeId;
2220 header.volumeInfo.parentId = isp->RWvolumeId;
2221 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2222 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2223 header.volumeInfo.inService = 0;
2224 header.volumeInfo.blessed = 0;
2225 /* The + 1000 is a hack in case there are any files out in venus caches */
2226 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2227 header.volumeInfo.type =
2228 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2229 header.volumeInfo.needsCallback = 0;
2230 gettimeofday(&tp,0);
2231 header.volumeInfo.creationDate = tp.tv_sec;
2232 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2233 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2235 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2236 sizeof(header.volumeInfo));
2237 if (code != sizeof(header.volumeInfo)) {
2239 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2240 sp->description, errno);
2241 Abort("Unable to write entire volume header file (%s)\n",
2246 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2247 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2249 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2250 if (code != sizeof(sp->stamp)) {
2252 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2253 sp->description, errno);
2254 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2259 FDH_REALLYCLOSE(fdP);
2261 if (sp->inodeType == VI_VOLINFO) {
2262 VolInfo = header.volumeInfo;
2265 if (VolInfo.updateDate) {
2266 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2267 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2268 (Testing?"it would have been ":""), update);
2270 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2271 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2280 int SalvageVnodes(register struct InodeSummary *rwIsp,
2281 register struct InodeSummary * thisIsp,
2282 register struct ViceInodeInfo * inodes, int check)
2284 int ilarge, ismall, ioffset, RW, nInodes;
2285 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2286 if (Showmode) return 0;
2287 RW = (rwIsp == thisIsp);
2288 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2289 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2290 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2291 if (check && ismall == -1)
2293 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2294 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2295 return (ilarge==0 && ismall==0 ? 0: -1);
2298 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2299 register struct ViceInodeInfo *ip,
2300 int nInodes, struct VolumeSummary *volSummary, int check)
2302 VolumeId volumeNumber;
2303 char buf[SIZEOF_LARGEDISKVNODE];
2304 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2306 StreamHandle_t *file;
2307 struct VnodeClassInfo *vcp;
2309 int vnodeIndex, nVnodes;
2310 afs_ino_str_t stmp1, stmp2;
2314 volumeNumber = volSummary->header.id;
2315 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2316 fdP = IH_OPEN(handle);
2317 assert(fdP != NULL);
2318 file = FDH_FDOPEN(fdP, "r+");
2319 assert(file != NULL)
2320 vcp = &VnodeClassInfo[class];
2321 size = OS_SIZE(fdP->fd_fd);
2323 nVnodes = (size / vcp->diskSize) - 1;
2325 assert((nVnodes+1) * vcp->diskSize == size)
2326 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2331 for (vnodeIndex = 0;
2332 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2333 nVnodes--, vnodeIndex++) {
2334 if (vnode->type != vNull) {
2335 int vnodeChanged = 0;
2336 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2337 /* Log programs that belong to root (potentially suid root);
2338 don't bother for read-only or backup volumes */
2339 #ifdef notdef /* This is done elsewhere */
2340 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2341 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2342 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2343 vnode->owner, vnode->modeBits);
2345 if (VNDISK_GET_INO(vnode) == 0) {
2347 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2348 memset(vnode, 0, vcp->diskSize);
2353 if (vcp->magic != vnode->vnodeMagic) {
2354 /* bad magic #, probably partially created vnode */
2355 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2356 memset(vnode, 0, vcp->diskSize);
2360 /* ****** Should do a bit more salvage here: e.g. make sure
2361 vnode type matches what it should be given the index */
2362 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2363 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2364 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2365 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2372 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2373 /* The following doesn't work, because the version number
2374 is not maintained correctly by the file server */
2375 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2376 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2378 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2385 /* For RW volume, look for vnode with matching inode number;
2386 if no such match, take the first determined by our sort
2388 register struct ViceInodeInfo *lip = ip;
2389 register lnInodes = nInodes;
2390 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2391 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2400 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2401 /* "Matching" inode */
2405 vu = vnode->uniquifier;
2406 iu = ip->u.vnode.vnodeUniquifier;
2407 vd = vnode->dataVersion;
2408 id = ip->u.vnode.inodeDataVersion;
2410 * Because of the possibility of the uniquifier overflows (> 4M)
2411 * we compare them modulo the low 22-bits; we shouldn't worry
2412 * about mismatching since they shouldn't to many old
2413 * uniquifiers of the same vnode...
2415 if (IUnique(vu) != IUnique(iu)) {
2417 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2418 vnodeNumber, IUnique(vu), IUnique(iu));
2421 vnode->uniquifier = iu;
2422 #ifdef AFS_3DISPARES
2423 vnode->dataVersion = (id >= vd ?
2424 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2425 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2427 #if defined(AFS_SGI_EXMAG)
2428 vnode->dataVersion = (id >= vd ?
2429 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2430 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2432 vnode->dataVersion = (id>vd ? id:vd);
2433 #endif /* AFS_SGI_EXMAG */
2434 #endif /* AFS_3DISPARES */
2438 /* don't bother checking for vd > id any more, since
2439 partial file transfers always result in this state,
2440 and you can't do much else anyway (you've already
2441 found the best data you can) */
2442 #ifdef AFS_3DISPARES
2443 if (!vnodeIsDirectory(vnodeNumber) &&
2444 ((vd < id && (id-vd) < 1887437) ||
2445 ((vd > id && (vd-id) > 1887437)))) {
2447 #if defined(AFS_SGI_EXMAG)
2448 if (!vnodeIsDirectory(vnodeNumber) &&
2449 ((vd < id && (id-vd) < 15099494) ||
2450 ((vd > id && (vd-id) > 15099494)))) {
2452 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2453 #endif /* AFS_SGI_EXMAG */
2455 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2456 vnode->dataVersion = id;
2461 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2464 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2466 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2467 PrintInode(stmp2, ip->inodeNumber),
2470 VNDISK_SET_INO(vnode, ip->inodeNumber);
2475 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2477 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2478 PrintInode(stmp2, ip->inodeNumber),
2481 VNDISK_SET_INO(vnode, ip->inodeNumber);
2484 if (ip->byteCount != vnode->length) {
2486 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2487 vnodeNumber, vnode->length, ip->byteCount);
2491 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2492 vnodeNumber, vnode->length, ip->byteCount);
2493 vnode->length = ip->byteCount;
2497 ip->linkCount--; /* Keep the inode around */
2501 else { /* no matching inode */
2502 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2503 /* No matching inode--get rid of the vnode */
2505 if (VNDISK_GET_INO(vnode)) {
2507 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2508 vnodeNumber, vnode->uniquifier,
2509 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2512 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2517 if (VNDISK_GET_INO(vnode)) {
2519 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2520 vnodeNumber, vnode->uniquifier,
2521 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2522 ctime((time_t *)&(vnode->serverModifyTime)));
2525 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime((time_t *)&(vnode->serverModifyTime)));
2527 memset(vnode, 0, vcp->diskSize);
2530 /* Should not reach here becuase we checked for
2531 * (inodeNumber == 0) above. And where we zero the vnode,
2532 * we also goto vnodeDone.
2536 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2540 } /* VNDISK_GET_INO(vnode) != 0 */
2542 assert(!(vnodeChanged && check));
2543 if (vnodeChanged && !Testing) {
2544 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2545 (char*)vnode, vcp->diskSize)
2547 VolumeChanged = 1; /* For break call back */
2558 struct VnodeEssence *CheckVnodeNumber(vnodeNumber)
2559 VnodeId vnodeNumber;
2562 struct VnodeInfo *vip;
2565 class = vnodeIdToClass(vnodeNumber);
2566 vip = &vnodeInfo[class];
2567 offset = vnodeIdToBitNumber(vnodeNumber);
2568 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2572 void CopyOnWrite(register struct DirSummary *dir)
2574 /* Copy the directory unconditionally if we are going to change it:
2575 * not just if was cloned.
2577 struct VnodeDiskObject vnode;
2578 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2579 Inode oldinode, newinode;
2582 if (dir->copied || Testing)
2584 DFlush(); /* Well justified paranoia... */
2586 code = IH_IREAD(vnodeInfo[vLarge].handle,
2587 vnodeIndexOffset(vcp, dir->vnodeNumber),
2588 (char*)&vnode, sizeof (vnode));
2589 assert(code == sizeof(vnode));
2590 oldinode = VNDISK_GET_INO(&vnode);
2591 /* Increment the version number by a whole lot to avoid problems with
2592 * clients that were promised new version numbers--but the file server
2593 * crashed before the versions were written to disk.
2595 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2596 dir->rwVid, dir->vnodeNumber,
2597 vnode.uniquifier, vnode.dataVersion += 200);
2598 assert(VALID_INO(newinode));
2599 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2601 VNDISK_SET_INO(&vnode, newinode);
2602 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2603 vnodeIndexOffset(vcp, dir->vnodeNumber),
2604 (char*)&vnode, sizeof (vnode));
2605 assert(code == sizeof (vnode));
2607 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2608 fileSysDevice, newinode);
2609 /* Don't delete the original inode right away, because the directory is
2610 * still being scanned.
2615 /* This function should either successfully create a new dir, or give up and leave
2616 * things the way they were. In particular, if it fails to write the new dir properly,
2617 * it should return w/o changing the reference to the old dir.
2619 void CopyAndSalvage(register struct DirSummary *dir)
2621 struct VnodeDiskObject vnode;
2622 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2623 Inode oldinode, newinode;
2625 register afs_int32 code;
2626 afs_int32 parentUnique= 1;
2627 struct VnodeEssence *vnodeEssence;
2631 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2632 code = IH_IREAD(vnodeInfo[vLarge].handle,
2633 vnodeIndexOffset(vcp, dir->vnodeNumber),
2634 (char*)&vnode, sizeof (vnode));
2635 assert(code == sizeof (vnode));
2636 oldinode = VNDISK_GET_INO(&vnode);
2637 /* Increment the version number by a whole lot to avoid problems with
2638 * clients that were promised new version numbers--but the file server
2639 * crashed before the versions were written to disk.
2641 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2642 dir->rwVid, dir->vnodeNumber,
2644 vnode.dataVersion += 200);
2645 assert(VALID_INO(newinode));
2646 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2648 /* Assign . and .. vnode numbers from dir and vnode.parent.
2649 * The uniquifier for . is in the vnode.
2650 * The uniquifier for .. might be set to a bogus value of 1 and
2651 * the salvager will later clean it up.
2653 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2654 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2656 code = DirSalvage(&dir->dirHandle, &newdir,
2657 dir->vnodeNumber, vnode.uniquifier,
2658 (vnode.parent?vnode.parent:dir->vnodeNumber),
2660 if (code == 0) code = DFlush();
2662 /* didn't really build the new directory properly, let's just give up. */
2663 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2665 Log("Directory salvage returned code %d, continuing.\n", code);
2668 Log("Checking the results of the directory salvage...\n");
2669 if (!DirOK(&newdir)) {
2670 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2671 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2676 VNDISK_SET_INO(&vnode, newinode);
2677 vnode.length = Length(&newdir);
2678 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2679 vnodeIndexOffset(vcp, dir->vnodeNumber),
2680 (char*)&vnode, sizeof (vnode));
2681 assert(code == sizeof (vnode));
2683 nt_sync(fileSysDevice);
2685 sync(); /* this is slow, but hopefully rarely called. We don't have
2686 * an open FD on the file itself to fsync.
2689 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2691 dir->dirHandle = newdir;
2694 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2697 struct VnodeEssence *vnodeEssence;
2698 afs_int32 dirOrphaned, todelete;
2700 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2702 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2703 if (vnodeEssence == NULL) {
2705 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2706 dir->vnodeNumber, dir->name?dir->name:"??",
2707 name, vnodeNumber, unique);
2711 assert(Delete(&dir->dirHandle, name) == 0)
2717 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2718 * mount inode for the partition. If this inode were deleted, it would crash
2721 if (vnodeEssence->InodeNumber == 0) {
2722 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2723 dir->vnodeNumber, (dir->name?dir->name:"??"),
2724 name, vnodeNumber, unique,
2725 (Testing?"-- would have deleted":" -- deleted"));
2728 assert(Delete(&dir->dirHandle, name) == 0);
2734 if (!(vnodeNumber & 1) && !Showmode &&
2735 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2736 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2737 dir->vnodeNumber, (dir->name?dir->name:"??"),
2738 name, vnodeNumber, unique,
2739 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2743 assert(Delete(&dir->dirHandle, name) == 0);
2749 /* Check if the Uniquifiers match. If not, change the directory entry
2750 * so its unique matches the vnode unique. Delete if the unique is zero
2751 * or if the directory is orphaned.
2753 if (!IUnique(vnodeEssence->unique) ||
2754 (IUnique(vnodeEssence->unique) != IUnique(unique)) ) {
2755 if ( !IUnique(vnodeEssence->unique) &&
2756 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2757 /* This is an orphaned directory. Don't delete the . or ..
2758 * entry. Otherwise, it will get created in the next
2759 * salvage and deleted again here. So Just skip it.
2764 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2767 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2768 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2769 name, vnodeNumber, unique, vnodeEssence->unique,
2770 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2774 fid.Vnode = vnodeNumber;
2775 fid.Unique = vnodeEssence->unique;
2777 assert(Delete(&dir->dirHandle, name) == 0)
2779 assert(Create(&dir->dirHandle, name, &fid) == 0)
2781 if (todelete) return; /* no need to continue */
2784 if (strcmp(name,".") == 0) {
2785 if (dir->vnodeNumber != vnodeNumber || (IUnique(dir->unique) != IUnique(unique))) {
2787 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2788 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2791 assert(Delete(&dir->dirHandle, ".") == 0)
2792 fid.Vnode = dir->vnodeNumber;
2793 fid.Unique = dir->unique;
2794 assert(Create(&dir->dirHandle, ".", &fid) == 0)
2797 vnodeNumber = fid.Vnode; /* Get the new Essence */
2798 unique = fid.Unique;
2799 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2803 else if (strcmp(name,"..") == 0) {
2806 struct VnodeEssence *dotdot;
2807 pa.Vnode = dir->parent;
2808 dotdot = CheckVnodeNumber(pa.Vnode);
2809 assert (dotdot != NULL); /* XXX Should not be assert */
2810 pa.Unique = dotdot->unique;
2813 pa.Vnode = dir->vnodeNumber;
2814 pa.Unique = dir->unique;
2816 if ((pa.Vnode != vnodeNumber) || (IUnique(pa.Unique) != IUnique(unique))) {
2817 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2818 dir->vnodeNumber, IUnique(dir->unique), vnodeNumber, IUnique(unique));
2821 assert(Delete(&dir->dirHandle, "..") == 0);
2822 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2825 vnodeNumber = pa.Vnode; /* Get the new Essence */
2827 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2829 dir->haveDotDot = 1;
2830 } else if (strncmp(name,".__afs",6) == 0) {
2832 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2833 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2837 assert(Delete(&dir->dirHandle, name) == 0)
2839 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2840 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2844 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2845 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2846 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber, dir->vnodeNumber);
2847 if (ShowMounts && (vnodeEssence->type == vSymlink) && !(vnodeEssence->modeBits & 0111)) {
2853 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2854 vnodeEssence->InodeNumber);
2856 assert(fdP != NULL);
2857 size = FDH_SIZE(fdP);
2859 memset(buf, 0, 1024);
2860 if (size > 1024) size = 1024;
2861 code = FDH_READ(fdP, buf, size);
2862 assert(code == size);
2863 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2864 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2865 dir->name?dir->name:"??", name, buf);
2866 FDH_REALLYCLOSE(fdP);
2869 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2870 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2871 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2872 if (vnodeIdToClass(vnodeNumber) == vLarge && vnodeEssence->name == NULL) {
2874 if (n = (char*)malloc(strlen(name)+1))
2876 vnodeEssence->name = n;
2879 /* The directory entry points to the vnode. Check to see if the
2880 * vnode points back to the directory. If not, then let the
2881 * directory claim it (else it might end up orphaned). Vnodes
2882 * already claimed by another directory are deleted from this
2883 * directory: hardlinks to the same vnode are not allowed
2884 * from different directories.
2886 if (vnodeEssence->parent != dir->vnodeNumber) {
2887 if (!vnodeEssence->claimed && !dirOrphaned) {
2888 /* Vnode does not point back to this directory.
2889 * Orphaned dirs cannot claim a file (it may belong to
2890 * another non-orphaned dir).
2893 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2894 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2895 vnodeNumber, unique, (Testing?"would have been ":""),
2896 vnodeEssence->parent, dir->vnodeNumber);
2898 vnodeEssence->parent = dir->vnodeNumber;
2899 vnodeEssence->changed = 1;
2901 /* Vnode was claimed by another directory */
2904 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2905 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2906 vnodeEssence->parent, vnodeNumber, unique,
2907 (Testing?"would have been ":""));
2909 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2910 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2911 vnodeEssence->parent, vnodeNumber, unique,
2912 (Testing?"would have been ":""));
2917 assert(Delete(&dir->dirHandle, name) == 0);
2922 /* This directory claims the vnode */
2923 vnodeEssence->claimed = 1;
2925 vnodeEssence->count--;
2928 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2931 register struct VnodeInfo *vip = &vnodeInfo[class];
2932 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2933 char buf[SIZEOF_LARGEDISKVNODE];
2934 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2936 StreamHandle_t *file;
2941 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2942 fdP = IH_OPEN(vip->handle);
2944 file = FDH_FDOPEN(fdP, "r+");
2945 assert(file != NULL);
2946 size = OS_SIZE(fdP->fd_fd);
2948 vip->nVnodes = (size / vcp->diskSize) - 1;
2949 if (vip->nVnodes > 0) {
2950 assert((vip->nVnodes+1)*vcp->diskSize == size)
2951 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2952 assert((vip->vnodes = (struct VnodeEssence *)
2953 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL)
2954 if (class == vLarge) {
2955 assert((vip->inodes = (Inode *)
2956 calloc(vip->nVnodes, sizeof (Inode))) != NULL)
2967 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2968 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2969 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2970 nVnodes--, vnodeIndex++) {
2971 if (vnode->type != vNull) {
2972 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2973 vip->nAllocatedVnodes++;
2974 vep->count = vnode->linkCount;
2975 vep->blockCount = nBlocks(vnode->length);
2976 vip->volumeBlockCount += vep->blockCount;
2977 vep->parent = vnode->parent;
2978 vep->unique = vnode->uniquifier;
2979 if (*maxu < vnode->uniquifier)
2980 *maxu = vnode->uniquifier;
2981 vep->modeBits = vnode->modeBits;
2982 vep->InodeNumber = VNDISK_GET_INO(vnode);
2983 vep->type = vnode->type;
2984 vep->author = vnode->author;
2985 vep->owner = vnode->owner;
2986 vep->group = vnode->group;
2987 if (vnode->type == vDirectory) {
2988 assert(class == vLarge)
2989 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2997 static char *GetDirName(vnode, vp, path)
2999 struct VnodeEssence *vp;
3002 struct VnodeEssence *parentvp;
3008 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3010 strcat(path, vp->name);
3016 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3017 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3019 static int IsVnodeOrphaned(vnode)
3022 struct VnodeEssence *vep;
3024 if (vnode == 0) return(1); /* Vnode zero does not exist */
3025 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3026 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3027 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3029 return( IsVnodeOrphaned(vep->parent) );
3032 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3033 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3036 static struct DirSummary dir;
3037 static struct DirHandle dirHandle;
3038 struct VnodeEssence *parent;
3039 static char path[MAXPATHLEN];
3042 if (dirVnodeInfo->vnodes[i].salvaged)
3043 return; /* already salvaged */
3046 dirVnodeInfo->vnodes[i].salvaged = 1;
3048 if (dirVnodeInfo->inodes[i] == 0)
3049 return; /* Not allocated to a directory */
3051 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3052 if (parent && parent->salvaged == 0)
3053 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3054 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3055 rootdir, rootdirfound);
3056 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3057 dir.unique = dirVnodeInfo->vnodes[i].unique;
3060 dir.parent = dirVnodeInfo->vnodes[i].parent;
3061 dir.haveDot = dir.haveDotDot = 0;
3062 dir.ds_linkH = alinkH;
3063 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3065 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3068 Log("Directory bad, vnode %d; %s...\n",
3069 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3072 CopyAndSalvage(&dir);
3076 dirHandle = dir.dirHandle;
3078 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3081 /* If enumeration failed for random reasons, we will probably delete
3082 * too much stuff, so we guard against this instead.
3084 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3087 /* Delete the old directory if it was copied in order to salvage.
3088 * CopyOnWrite has written the new inode # to the disk, but we still
3089 * have the old one in our local structure here. Thus, we idec the
3093 if (dir.copied && !Testing) {
3094 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3096 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3099 /* Remember rootdir DirSummary _after_ it has been judged */
3100 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3101 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3108 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3110 /* This routine, for now, will only be called for read-write volumes */
3112 int BlocksInVolume = 0, FilesInVolume = 0;
3113 register VnodeClass class;
3114 struct DirSummary rootdir, oldrootdir;
3115 struct VnodeInfo *dirVnodeInfo;
3116 struct VnodeDiskObject vnode;
3117 VolumeDiskData volHeader;
3119 int orphaned, rootdirfound = 0;
3120 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3121 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3122 struct VnodeEssence *vep;
3127 VnodeId LFVnode, ThisVnode;
3128 Unique LFUnique, ThisUnique;
3131 vid = rwIsp->volSummary->header.id;
3132 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3133 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3134 assert(nBytes == sizeof(volHeader));
3135 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3136 assert (volHeader.destroyMe != DESTROY_ME);
3137 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3139 DistilVnodeEssence(vid, vLarge,
3140 rwIsp->volSummary->header.largeVnodeIndex,
3142 DistilVnodeEssence(vid, vSmall,
3143 rwIsp->volSummary->header.smallVnodeIndex,
3146 dirVnodeInfo = &vnodeInfo[vLarge];
3147 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3148 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3149 &rootdir, &rootdirfound);
3156 /* Parse each vnode looking for orphaned vnodes and
3157 * connect them to the tree as orphaned (if requested).
3159 oldrootdir = rootdir;
3160 for (class=0; class < nVNODECLASSES; class++) {
3161 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3162 vep = &(vnodeInfo[class].vnodes[v]);
3163 ThisVnode = bitNumberToVnodeNumber(v, class);
3164 ThisUnique = vep->unique;
3166 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3167 continue; /* Ignore unused, claimed, and root vnodes */
3169 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3170 * entry in this vnode had incremented the parent link count (In
3171 * JudgeEntry()). We need to go to the parent and decrement that
3172 * link count. But if the parent's unique is zero, then the parent
3173 * link count was not incremented in JudgeEntry().
3175 if (class == vLarge) { /* directory vnode */
3176 pv = vnodeIdToBitNumber(vep->parent);
3177 if (IUnique(vnodeInfo[vLarge].vnodes[pv].unique) != 0)
3178 vnodeInfo[vLarge].vnodes[pv].count++;
3182 continue; /* If no rootdir, can't attach orphaned files */
3184 /* Here we attach orphaned files and directories into the
3185 * root directory, LVVnode, making sure link counts stay correct.
3187 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3188 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3189 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3191 /* Update this orphaned vnode's info. Its parent info and
3192 * link count (do for orphaned directories and files).
3194 vep->parent = LFVnode; /* Parent is the root dir */
3195 vep->unique = LFUnique;
3198 vep->count--; /* Inc link count (root dir will pt to it) */
3200 /* If this orphaned vnode is a directory, change '..'.
3201 * The name of the orphaned dir/file is unknown, so we
3202 * build a unique name. No need to CopyOnWrite the directory
3203 * since it is not connected to tree in BK or RO volume and
3204 * won't be visible there.
3206 if (class == vLarge) {
3210 /* Remove and recreate the ".." entry in this orphaned directory */
3211 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3213 pa.Unique = LFUnique;
3214 assert(Delete(&dh, "..") == 0);
3215 assert(Create(&dh, "..", &pa) == 0);
3217 /* The original parent's link count was decremented above.
3218 * Here we increment the new parent's link count.
3220 pv = vnodeIdToBitNumber(LFVnode);
3221 vnodeInfo[vLarge].vnodes[pv].count--;
3225 /* Go to the root dir and add this entry. The link count of the
3226 * root dir was incremented when ".." was created. Try 10 times.
3228 for (j=0; j<10; j++) {
3229 pa.Vnode = ThisVnode;
3230 pa.Unique = ThisUnique;
3232 sprintf(npath, "%s.%d.%d",
3233 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3234 ThisVnode, ThisUnique);
3236 CopyOnWrite(&rootdir);
3237 code = Create(&rootdir.dirHandle, npath, &pa);
3240 ThisUnique += 50; /* Try creating a different file */
3243 Log("Attaching orphaned %s to volume's root dir as %s\n",
3244 ((class == vLarge)?"directory":"file"), npath);
3246 } /* for each vnode in the class */
3247 } /* for each class of vnode */
3249 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3251 if (!oldrootdir.copied && rootdir.copied) {
3252 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3254 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3257 DFlush(); /* Flush the changes */
3258 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3259 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3260 orphans = ORPH_IGNORE;
3263 /* Write out all changed vnodes. Orphaned files and directories
3264 * will get removed here also (if requested).
3266 for (class = 0; class < nVNODECLASSES; class++){
3267 int nVnodes = vnodeInfo[class].nVnodes;
3268 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3269 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3270 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3271 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3272 for (i = 0; i<nVnodes; i++) {
3273 register struct VnodeEssence *vnp = &vnodes[i];
3274 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3276 /* If the vnode is good but is unclaimed (not listed in
3277 * any directory entries), then it is orphaned.
3280 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3281 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3285 if (vnp->changed || vnp->count) {
3288 nBytes = IH_IREAD(vnodeInfo[class].handle,
3289 vnodeIndexOffset(vcp, vnodeNumber),
3290 (char*)&vnode, sizeof (vnode));
3291 assert(nBytes == sizeof(vnode));
3293 vnode.parent = vnp->parent;
3294 oldCount = vnode.linkCount;
3295 vnode.linkCount = vnode.linkCount - vnp->count;
3298 orphaned = IsVnodeOrphaned(vnodeNumber);
3300 if (!vnp->todelete) {
3301 /* Orphans should have already been attached (if requested) */
3302 assert(orphans != ORPH_ATTACH);
3303 oblocks += vnp->blockCount;
3306 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3307 BlocksInVolume -= vnp->blockCount;
3309 if (VNDISK_GET_INO(&vnode)) {
3310 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3313 memset(&vnode, 0, sizeof(vnode));
3315 } else if (vnp->count) {
3317 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3318 vnodeNumber, oldCount,
3319 (Testing?"would have changed to":"now"), vnode.linkCount);
3323 vnode.dataVersion++;
3325 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3326 vnodeIndexOffset(vcp, vnodeNumber),
3327 (char*)&vnode, sizeof (vnode));
3328 assert(nBytes == sizeof(vnode));
3334 if (!Showmode && ofiles) {
3335 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3336 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3340 for (class = 0; class < nVNODECLASSES; class++) {
3341 register struct VnodeInfo *vip = &vnodeInfo[class];
3342 for (i=0; i<vip->nVnodes; i++)
3343 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3344 if (vip->vnodes) free(vip->vnodes);
3345 if (vip->inodes) free(vip->inodes);
3348 /* Set correct resource utilization statistics */
3349 volHeader.filecount = FilesInVolume;
3350 volHeader.diskused = BlocksInVolume;
3352 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3353 if (volHeader.uniquifier < (maxunique + 1)) {
3354 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3355 /* Plus 2,000 in case there are workstations out there with
3356 * cached vnodes that have since been deleted
3358 volHeader.uniquifier = (maxunique + 1 + 2000);
3361 /* Turn off the inUse bit; the volume's been salvaged! */
3362 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3363 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3364 volHeader.inService = 1; /* allow service again */
3365 volHeader.needsCallback = (VolumeChanged != 0);
3366 volHeader.dontSalvage = DONT_SALVAGE;
3369 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3370 assert(nBytes == sizeof(volHeader));
3373 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3374 (Testing?"It would have ":""), volHeader.name,
3375 volHeader.id, FilesInVolume, BlocksInVolume);
3377 IH_RELEASE(vnodeInfo[vSmall].handle);
3378 IH_RELEASE(vnodeInfo[vLarge].handle);
3383 void ClearROInUseBit(struct VolumeSummary *summary)
3385 IHandle_t *h = summary->volumeInfoHandle;
3388 VolumeDiskData volHeader;
3390 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3391 assert(nBytes == sizeof(volHeader));
3392 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC)
3393 volHeader.inUse = 0;
3394 volHeader.needsSalvaged = 0;
3395 volHeader.inService = 1;
3396 volHeader.dontSalvage = DONT_SALVAGE;
3398 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3399 assert(nBytes == sizeof(volHeader));
3404 * Possible delete the volume.
3406 * deleteMe - Always do so, only a partial volume.
3408 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3412 if (readOnly(isp) || deleteMe) {
3413 if (isp->volSummary && isp->volSummary->fileName) {
3415 if (!Showmode) Log("Volume %u (is only a partial volume--probably an attempt was made to move/restore it when a machine crash occured.\n", isp->volumeId);
3416 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3418 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3419 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3422 unlink(isp->volSummary->fileName);
3426 Log("%s salvage was unsuccessful: read-write volume %u\n",
3427 message, isp->volumeId);
3428 Abort("Salvage of volume %u aborted\n",
3434 void AskOffline(VolumeId volumeId)
3436 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3437 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3438 Abort("Salvage aborted\n");
3442 void AskOnline(VolumeId volumeId, char *partition)
3444 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3445 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3446 volumeId, partition);
3450 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3452 /* Volume parameter is passed in case iopen is upgraded in future to
3453 * require a volume Id to be passed
3456 IHandle_t *srcH, *destH;
3457 FdHandle_t *srcFdP, *destFdP;
3460 IH_INIT(srcH, device, rwvolume, inode1);
3461 srcFdP = IH_OPEN(srcH);
3462 assert(srcFdP != NULL);
3463 IH_INIT(destH, device, rwvolume, inode2);
3464 destFdP = IH_OPEN(destH);
3466 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3467 assert(FDH_WRITE(destFdP, buf, n) == n);
3469 FDH_REALLYCLOSE(srcFdP);
3470 FDH_REALLYCLOSE(destFdP);
3476 void PrintInodeList(void)
3478 register struct ViceInodeInfo *ip;
3479 struct ViceInodeInfo *buf;
3483 assert(fstat(inodeFd, &status) == 0);
3484 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3485 assert(buf != NULL);
3486 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3487 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3488 for(ip = buf; nInodes--; ip++) {
3489 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3490 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3491 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3496 void PrintInodeSummary(void)
3499 struct InodeSummary *isp;
3501 for (i=0; i<nVolumesInInodeFile; i++) {
3502 isp = &inodeSummary[i];
3503 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3504 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3505 isp->nSpecialInodes, isp->maxUniquifier);
3509 void PrintVolumeSummary(void)
3512 struct VolumeSummary *vsp;
3514 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3515 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3523 assert(0); /* Fork is never executed in the NT code path */
3534 if (ShowLog) showlog();
3536 if (main_thread != pthread_self())
3537 pthread_exit((void*)code);
3545 int Wait(char *prog)
3549 pid = wait(&status);
3551 if (WCOREDUMP(status))
3552 Log("\"%s\" core dumped!\n", prog);
3553 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3558 static char *TimeStamp(time_t clock, int precision)
3561 static char timestamp[20];
3562 lt = localtime(&clock);
3564 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3566 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3570 void CheckLogFile(void)
3572 char oldSlvgLog[AFSDIR_PATH_MAX];
3574 #ifndef AFS_NT40_ENV
3581 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3582 strcat(oldSlvgLog, ".old");
3584 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3585 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3587 if (!logFile) { /* still nothing, use stdout */
3592 #ifndef AFS_NAMEI_ENV
3593 AFS_DEBUG_IOPS_LOG(logFile);
3602 #ifndef AFS_NT40_ENV
3604 printf("Can't show log since using syslog.\n");
3613 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3616 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3619 while (fgets(line, sizeof(line), logFile))
3625 void Log(a,b,c,d,e,f,g,h,i,j,k)
3626 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3630 #ifndef AFS_NT40_ENV
3633 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3637 gettimeofday(&now, 0);
3638 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3639 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3644 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3645 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3647 #ifndef AFS_NT40_ENV
3650 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3654 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3656 if (ShowLog) showlog();
3663 char * ToString(char *s)
3666 p = (char *) malloc(strlen(s)+1);
3673 /* Remove the FORCESALVAGE file */
3674 void RemoveTheForce(char *path)
3676 if (!Testing && ForceSalvage) {
3677 if (chdir(path) == 0)
3678 unlink("FORCESALVAGE");
3682 #ifndef AFS_AIX32_ENV
3684 * UseTheForceLuke - see if we can use the force
3686 int UseTheForceLuke(char *path)
3690 assert(chdir(path) != -1);
3692 return (stat("FORCESALVAGE", &force) == 0);
3696 * UseTheForceLuke - see if we can use the force
3699 * The VRMIX fsck will not muck with the filesystem it is supposedly
3700 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3701 * muck directly with the root inode, which is within the normal
3703 * ListViceInodes() has a side effect of setting ForceSalvage if
3704 * it detects a need, based on root inode examination.
3706 int UseTheForceLuke(char *path)
3709 return 0; /* sorry OB1 */
3714 /* NT support routines */
3716 static char execpathname[MAX_PATH];
3717 int nt_SalvagePartition(char *partName, int jobn)
3722 if (!*execpathname) {
3723 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3724 if (!n || n == 1023)
3727 job.cj_magic = SALVAGER_MAGIC;
3728 job.cj_number = jobn;
3729 (void) strcpy(job.cj_part, partName);
3730 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3735 int nt_SetupPartitionSalvage(void *datap, int len)
3737 childJob_t *jobp = (childJob_t*)datap;
3738 char logname[AFSDIR_PATH_MAX];
3740 if (len != sizeof(childJob_t))
3742 if (jobp->cj_magic != SALVAGER_MAGIC)
3747 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3749 logFile = fopen(logname, "w");
3750 if (!logFile) logFile = stdout;
3756 #endif /* AFS_NT40_ENV */