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)
114 #define WCOREDUMP(x) (x & 0200)
117 #include <afs/afsint.h>
118 #include <afs/assert.h>
119 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
120 #if defined(AFS_VFSINCL_ENV)
121 #include <sys/vnode.h>
123 #include <sys/fs/ufs_inode.h>
125 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
126 #include <ufs/ufs/dinode.h>
127 #include <ufs/ffs/fs.h>
129 #include <ufs/inode.h>
132 #else /* AFS_VFSINCL_ENV */
134 #include <ufs/inode.h>
135 #else /* AFS_OSF_ENV */
136 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
137 #include <sys/inode.h>
140 #endif /* AFS_VFSINCL_ENV */
141 #endif /* AFS_SGI_ENV */
144 #include <sys/lockf.h>
148 #include <checklist.h>
150 #if defined(AFS_SGI_ENV)
155 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
158 #include <sys/mnttab.h>
159 #include <sys/mntent.h>
164 #endif /* AFS_SGI_ENV */
165 #endif /* AFS_HPUX_ENV */
170 #include <afs/osi_inode.h>
173 #include <afs/afsutil.h>
174 #include <afs/fileutil.h>
175 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
183 #include <afs/afssyscalls.h>
187 #include "partition.h"
189 #include "viceinode.h"
191 #include "volinodes.h" /* header magic number, etc. stuff */
197 extern void *calloc();
199 extern char *vol_DevName();
200 static char *TimeStamp(time_t clock, int precision);
202 #define ORPH_IGNORE 0
203 #define ORPH_REMOVE 1
204 #define ORPH_ATTACH 2
207 int debug; /* -d flag */
208 int Testing=0; /* -n flag */
209 int ListInodeOption; /* -i flag */
210 int ShowRootFiles; /* -r flag */
211 int RebuildDirs; /* -sal flag */
212 int Parallel = 4; /* -para X flag */
213 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
214 int forceR = 0; /* -b flag */
215 int ShowLog = 0; /* -showlog flag */
216 int ShowSuid = 0; /* -showsuid flag */
217 int ShowMounts = 0; /* -showmounts flag */
218 int orphans = ORPH_IGNORE; /* -orphans option */
222 int useSyslog = 0; /* -syslog flag */
223 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
226 #define MAXPARALLEL 32
228 int OKToZap; /* -o flag */
229 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
230 in the volume header */
232 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
234 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
236 Device fileSysDevice; /* The device number of the current
237 partition being salvaged */
241 char *fileSysPath; /* The path of the mounted partition currently
242 being salvaged, i.e. the directory
243 containing the volume headers */
245 char *fileSysPathName; /* NT needs this to make name pretty in log. */
246 IHandle_t *VGLinkH; /* Link handle for current volume group. */
247 int VGLinkH_cnt; /* # of references to lnk handle. */
248 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
250 char *fileSysDeviceName; /* The block device where the file system
251 being salvaged was mounted */
252 char *filesysfulldev;
254 int VolumeChanged; /* Set by any routine which would change the volume in
255 a way which would require callback is to be broken if the
256 volume was put back on line by an active file server */
258 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
260 struct InodeSummary { /* Inode summary file--an entry for each
261 volume in the inode file for a partition */
262 VolId volumeId; /* Volume id */
263 VolId RWvolumeId; /* RW volume associated */
264 int index; /* index into inode file (0, 1, 2 ...) */
265 int nInodes; /* Number of inodes for this volume */
266 int nSpecialInodes; /* Number of special inodes, i.e. volume
267 header, index, etc. These are all
268 marked (viceinode.h) and will all be sorted
269 to the beginning of the information for
270 this volume. Read-only volumes should
271 ONLY have special inodes (all the other
272 inodes look as if they belong to the
273 original RW volume). */
274 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
275 This is only useful for RW volumes and is used
276 to compute a new volume uniquifier in the event
277 that the header needs to be recreated. The inode
278 uniquifier may be a truncated version of vnode
279 uniquifier (AFS_3DISPARES). The real maxUniquifer
280 is from the vnodes and later calcuated from it */
281 struct VolumeSummary *volSummary;
282 /* Either a pointer to the original volume
283 header summary, or constructed summary
286 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
287 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
288 int inodeFd; /* File descriptor for inode file */
291 struct VolumeSummary { /* Volume summary an entry for each
292 volume in a volume directory.
293 Assumption: one volume directory per
295 char *fileName; /* File name on the partition for the volume
297 struct VolumeHeader header;
298 /* volume number, rw volume number, inode
299 numbers of each major component of
301 IHandle_t *volumeInfoHandle;
302 byte wouldNeedCallback; /* set if the file server should issue
303 call backs for all the files in this volume when
304 the volume goes back on line */
308 IHandle_t *handle; /* Inode containing this index */
309 int nVnodes; /* Total number of vnodes in index */
310 int nAllocatedVnodes; /* Total number actually used */
311 int volumeBlockCount; /* Total number of blocks used by volume */
312 Inode *inodes; /* Directory only */
313 struct VnodeEssence {
314 short count; /* Number of references to vnode; MUST BE SIGNED */
315 unsigned claimed:1; /* Set when a parent directory containing an entry
316 referencing this vnode is found. The claim
317 is that the parent in "parent" can point to
318 this vnode, and no other */
319 unsigned changed:1; /* Set if any parameters (other than the count)
320 in the vnode change. It is determined if the
321 link count has changed by noting whether it is
322 0 after scanning all directories */
323 unsigned salvaged:1;/* Set if this directory vnode has already been salvaged. */
324 unsigned todelete:1;/* Set if this vnode is to be deleted (should not be claimed) */
325 afs_uint32 blockCount;
326 /* Number of blocks (1K) used by this vnode,
328 VnodeId parent; /* parent in vnode */
329 Unique unique; /* Must match entry! */
330 char *name; /* Name of directory entry */
331 int modeBits; /* File mode bits */
332 Inode InodeNumber; /* file's inode */
333 int type; /* File type */
334 int author; /* File author */
335 int owner; /* File owner */
336 int group; /* File group */
338 } vnodeInfo[nVNODECLASSES];
341 struct DirHandle dirHandle;
344 unsigned haveDot, haveDotDot;
346 int copied; /* If the copy-on-write stuff has been applied */
354 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
355 int nVolumes; /* Number of volumes (read-write and read-only)
359 /* For NT, we can fork the per partition salvagers to gain the required
360 * safety against Aborts. But there's too many complex data structures at
361 * the per volume salvager layer to easilty copy the data across.
362 * childJobNumber is resset from -1 to the job number if this is a
363 * per partition child of the main salvager. This information is passed
364 * out-of-band in the extra data area setup for the now unused parent/child
367 #define SALVAGER_MAGIC 0x00BBaaDD
368 #define NOT_CHILD -1 /* job numbers start at 0 */
369 /* If new options need to be passed to child, add them here. */
376 /* Child job this process is running. */
377 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD};
379 int nt_SalvagePartition(char *partName, int jobn);
380 int nt_SetupPartitionSalvage(void *datap, int len);
383 struct InodeSummary *svgp_inodeSummaryp;
393 /* Forward declarations */
394 void Log(), Abort(), Exit();
396 int Wait(char *prog);
397 char * ToString(char *s);
398 void AskOffline(VolumeId volumeId);
399 void AskOnline(VolumeId volumeId, char *partition);
400 void CheckLogFile(void);
401 void ClearROInUseBit(struct VolumeSummary *summary);
402 void CopyAndSalvage(register struct DirSummary *dir);
403 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
404 void CopyOnWrite(register struct DirSummary *dir);
405 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
406 register struct InodeSummary * summary);
407 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
408 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
410 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
411 void GetVolumeSummary(VolumeId singleVolumeNumber);
412 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
414 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
417 void ObtainSalvageLock(void);
418 void PrintInodeList(void);
419 void PrintInodeSummary(void);
420 void PrintVolumeSummary(void);
421 int QuickCheck(register struct InodeSummary *isp, int nVols);
422 void RemoveTheForce(char *path);
423 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
424 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
426 void SalvageFileSysParallel(struct DiskPartition *partP);
427 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
428 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
429 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
430 int check, int *deleteMe);
431 int SalvageIndex(Inode ino, VnodeClass class, int RW,
432 register struct ViceInodeInfo *ip,
433 int nInodes, struct VolumeSummary *volSummary, int check);
434 int SalvageVnodes(register struct InodeSummary *rwIsp,
435 register struct InodeSummary * thisIsp,
436 register struct ViceInodeInfo * inodes, int check);
437 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
438 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
440 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
442 #define SalvageVolumeGroup DoSalvageVolumeGroup
444 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
445 register struct ViceInodeInfo *inodes,
446 int RW, int check, int *deleteMe);
448 int UseTheForceLuke(char *path);
450 static int IsVnodeOrphaned(VnodeId vnode);
452 /* Uniquifier stored in the Inode */
453 static Unique IUnique(u)
457 return(u & 0x3fffff);
459 #if defined(AFS_SGI_EXMAG)
460 return(u & SGI_UNIQMASK);
463 #endif /* AFS_SGI_EXMAG */
467 static int BadError(aerror)
468 register int aerror; {
469 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
471 return 0; /* otherwise may be transient, e.g. EMFILE */
477 struct cmd_syndesc *as;
479 register struct cmd_item *ti;
480 char pname[100], *temp;
481 afs_int32 seenpart = 0, seenvol = 0, vid = 0;
482 struct DiskPartition *partP;
484 #ifdef AFS_SGI_VNODE_GLUE
485 if (afs_init_kernel_config(-1) <0) {
486 printf("Can't determine NUMA configuration, not starting salvager.\n");
491 if (ti = as->parms[0].items) { /* -partition */
493 strncpy(pname, ti->data, 100);
495 if (ti = as->parms[1].items) { /* -volumeid */
497 printf("You must also specify '-partition' option with the '-volumeid' option\n");
501 vid = atoi(ti->data);
503 if (as->parms[2].items) /* -debug */
505 if (as->parms[3].items) /* -nowrite */
507 if (as->parms[4].items) /* -inodes */
509 if (as->parms[5].items) /* -force */
511 if (as->parms[6].items) /* -oktozap */
513 if (as->parms[7].items) /* -rootinodes */
515 if (as->parms[8].items) /* -RebuildDirs */
517 if (as->parms[9].items) /* -ForceReads */
519 if (ti = as->parms[10].items) {/* -Parallel # */
521 if (strncmp(temp,"all",3) == 0) {
525 if (strlen(temp) != 0) {
526 Parallel = atoi(temp);
527 if (Parallel < 1) Parallel = 1;
528 if (Parallel > MAXPARALLEL) {
529 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
530 Parallel = MAXPARALLEL;
534 if (ti = as->parms[11].items) {/* -tmpdir */
538 dirp = opendir(tmpdir);
540 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
545 if (ti = as->parms[12].items) /* -showlog */
547 if (ti = as->parms[13].items) { /* -log */
552 if (ti = as->parms[14].items) { /* -showmounts */
557 if (ti = as->parms[15].items) { /* -orphans */
559 orphans = ORPH_IGNORE;
560 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
561 orphans = ORPH_REMOVE;
562 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
563 orphans = ORPH_ATTACH;
566 #ifndef AFS_NT40_ENV /* ignore options on NT */
567 if ( ti = as->parms[16].items) { /* -syslog */
571 if ( ti = as->parms[17].items) { /* -syslogfacility */
572 useSyslogFacility = atoi(ti->data);
578 if (ti = as->parms[18].items) { /* -DontSalvage */
579 printf("Exiting immediately without salvage. Look into the FileLog");
580 printf(" to find volumes which really need to be salvaged!\n");
583 #endif /* FAST_RESTART */
585 /* Note: if seemvol we initialize this as a standard volume utility: this has the
586 implication that the file server may be running; negotations have to be made with
587 the file server in this case to take the read write volume and associated read-only
588 volumes off line before salvaging */
591 if (afs_winsockInit()<0) {
592 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
593 AFSDIR_SALVAGER_FILE, 0);
594 Log("Failed to initailize winsock, exiting.\n");
599 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
602 if (myjob.cj_number != NOT_CHILD) {
605 (void) strcpy(pname, myjob.cj_part);
610 for (partP = DiskPartitionList; partP; partP = partP->next) {
611 SalvageFileSysParallel(partP);
613 SalvageFileSysParallel(0);
616 partP = VGetPartition(pname, 0);
618 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
623 SalvageFileSys(partP, 0);
625 /* Salvage individual volume */
627 Log("salvage: invalid volume id specified; salvage aborted\n");
630 SalvageFileSys (partP, vid);
638 #include "AFS_component_version_number.c"
642 char *save_args[MAX_ARGS];
644 pthread_t main_thread;
650 struct cmd_syndesc *ts;
652 char commandLine[150];
655 extern char cml_version_number[];
659 * The following signal action for AIX is necessary so that in case of a
660 * crash (i.e. core is generated) we can include the user's data section
661 * in the core dump. Unfortunately, by default, only a partial core is
662 * generated which, in many cases, isn't too useful.
664 struct sigaction nsa;
666 sigemptyset(&nsa.sa_mask);
667 nsa.sa_handler = SIG_DFL;
668 nsa.sa_flags = SA_FULLDUMP;
669 sigaction(SIGABRT, &nsa, NULL);
670 sigaction(SIGSEGV, &nsa, NULL);
673 /* Initialize directory paths */
674 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
676 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
678 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
682 main_thread = pthread_self();
683 if (spawnDatap && spawnDataLen) {
684 /* This is a child per partition salvager. Don't setup log or
685 * try to lock the salvager lock.
687 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
692 for (commandLine[0] = '\0', i=0; i<argc; i++) {
693 if (i > 0) strcat(commandLine, " ");
694 strcat(commandLine, argv[i]);
697 /* All entries to the log will be appended. Useful if there are
698 * multiple salvagers appending to the log.
703 #ifdef AFS_LINUX20_ENV
704 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
706 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
712 if (geteuid() != 0) {
713 printf("Salvager must be run as root.\n");
719 /* bad for normal help flag processing, but can do nada */
721 fprintf(logFile, "%s\n", cml_version_number);
722 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
724 /* Get and hold a lock for the duration of the salvage to make sure
725 * that no other salvage runs at the same time. The routine
726 * VInitVolumePackage (called below) makes sure that a file server or
727 * other volume utilities don't interfere with the salvage.
734 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
735 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
736 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
737 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
738 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
739 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
740 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
741 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
742 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
743 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
744 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
745 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
746 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
747 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
748 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
749 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
750 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
752 /* note - syslog isn't avail on NT, but if we make it conditional, have
753 to deal with screwy offsets for cmd params */
754 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
755 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
758 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");
759 #endif /* FAST_RESTART */
760 err = cmd_Dispatch(argc, argv);
764 /* Get the salvage lock if not already held. Hold until process exits. */
765 void ObtainSalvageLock(void)
770 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
771 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
773 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
775 "salvager: There appears to be another salvager running! Aborted.\n");
779 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
780 assert(salvageLock >= 0);
781 #ifdef AFS_DARWIN_ENV
782 if (flock(salvageLock, LOCK_EX) == -1) {
784 if (lockf(salvageLock, F_LOCK, 0) == -1) {
787 "salvager: There appears to be another salvager running! Aborted.\n");
794 #ifdef AFS_SGI_XFS_IOPS_ENV
795 /* Check if the given partition is mounted. For XFS, the root inode is not a
796 * constant. So we check the hard way.
798 int IsPartitionMounted(char *part)
801 struct mntent *mntent;
803 assert(mntfp = setmntent(MOUNTED, "r"));
804 while (mntent = getmntent(mntfp)) {
805 if (!strcmp(part, mntent->mnt_dir))
810 return mntent ? 1 : 1;
813 /* Check if the given inode is the root of the filesystem. */
814 #ifndef AFS_SGI_XFS_IOPS_ENV
815 int IsRootInode(status)
818 /* The root inode is not a fixed value in XFS partitions. So we need to see if
819 * the partition is in the list of mounted partitions. This only affects the
820 * SalvageFileSys path, so we check there.
822 return (status->st_ino == ROOTINODE);
827 /* We don't want to salvage big files filesystems, since we can't put volumes on
830 int CheckIfBigFilesFS(mountPoint, devName)
834 struct superblock fs;
837 if (strncmp(devName, "/dev/", 5)) {
838 (void) sprintf(name, "/dev/%s", devName);
841 (void) strcpy(name, devName);
844 if (ReadSuper(&fs, name)<0) {
845 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
848 if (IsBigFilesFileSystem(&fs)) {
849 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
857 #define HDSTR "\\Device\\Harddisk"
858 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
859 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
866 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
868 if (strncmp(res, HDSTR, HDLEN)) {
871 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
872 res, HDSTR, p1->devName);
876 d1 = atoi(&res[HDLEN]);
878 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
880 if (strncmp(res, HDSTR, HDLEN)) {
883 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
884 res, HDSTR, p2->devName);
888 d2 = atoi(&res[HDLEN]);
893 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
896 /* This assumes that two partitions with the same device number divided by
897 * PartsPerDisk are on the same disk.
899 void SalvageFileSysParallel(struct DiskPartition *partP)
902 struct DiskPartition *partP;
903 int pid; /* Pid for this job */
904 int jobnumb; /* Log file job number */
905 struct job *nextjob; /* Next partition on disk to salvage */
907 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
908 struct job *thisjob = 0;
909 static int numjobs = 0;
910 static int jobcount = 0;
916 char logFileName[256];
920 /* We have a partition to salvage. Copy it into thisjob */
921 thisjob = (struct job *) malloc(sizeof(struct job));
923 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
926 memset(thisjob, 0, sizeof(struct job));
927 thisjob->partP = partP;
928 thisjob->jobnumb = jobcount;
931 else if (jobcount == 0) {
932 /* We are asking to wait for all jobs (partp == 0), yet we never
935 Log("No file system partitions named %s* found; not salvaged\n",
936 VICE_PARTITION_PREFIX);
940 if (debug || Parallel == 1) {
942 SalvageFileSys(thisjob->partP, 0);
949 /* Check to see if thisjob is for a disk that we are already
950 * salvaging. If it is, link it in as the next job to do. The
951 * jobs array has 1 entry per disk being salvages. numjobs is
952 * the total number of disks currently being salvaged. In
953 * order to keep thejobs array compact, when a disk is
954 * completed, the hightest element in the jobs array is moved
955 * down to now open slot.
957 for (j=0; j<numjobs; j++) {
958 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
959 /* On same disk, add it to this list and return */
960 thisjob->nextjob = jobs[j]->nextjob;
961 jobs[j]->nextjob = thisjob;
968 /* Loop until we start thisjob or until all existing jobs are finished */
969 while ( thisjob || (!partP && (numjobs > 0)) ) {
970 startjob = -1; /* No new job to start */
972 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
973 /* Either the max jobs are running or we have to wait for all
974 * the jobs to finish. In either case, we wait for at least one
975 * job to finish. When it's done, clean up after it.
977 pid = wait(&wstatus);
979 for (j=0; j<numjobs; j++) { /* Find which job it is */
980 if (pid == jobs[j]->pid) break;
983 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
984 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
987 numjobs--; /* job no longer running */
988 oldjob = jobs[j]; /* remember */
989 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
990 free(oldjob); /* free the old job */
992 /* If there is another partition on the disk to salvage, then
993 * say we will start it (startjob). If not, then put thisjob there
994 * and say we will start it.
996 if (jobs[j]) { /* Another partitions to salvage */
997 startjob = j; /* Will start it */
998 } else { /* There is not another partition to salvage */
1000 jobs[j] = thisjob; /* Add thisjob */
1002 startjob = j; /* Will start it */
1004 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1005 startjob = -1; /* Don't start it - already running */
1009 /* We don't have to wait for a job to complete */
1011 jobs[numjobs] = thisjob; /* Add this job */
1013 startjob = numjobs; /* Will start it */
1017 /* Start up a new salvage job on a partition in job slot "startjob" */
1018 if (startjob != -1) {
1020 Log("Starting salvage of file system partition %s\n",
1021 jobs[startjob]->partP->name);
1023 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1024 * commands and pass the child job number via the data path.
1026 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1027 jobs[startjob]->jobnumb);
1028 jobs[startjob]->pid = pid;
1033 jobs[startjob]->pid = pid;
1039 for (fd =0; fd < 16; fd++) close(fd);
1040 open("/", 0); dup2(0, 1); dup2(0, 2);
1041 #ifndef AFS_NT40_ENV
1043 openlog(NULL, LOG_PID, useSyslogFacility);
1047 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1048 logFile = fopen(logFileName, "w");
1050 if (!logFile) logFile = stdout;
1052 SalvageFileSys1(jobs[startjob]->partP, 0);
1057 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1059 /* If waited for all jobs to complete, now collect log files and return */
1060 #ifndef AFS_NT40_ENV
1061 if ( ! useSyslog ) /* if syslogging - no need to collect */
1064 for (i=0; i<jobcount; i++) {
1065 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1066 if (passLog = fopen(logFileName, "r")) {
1067 while(fgets(buf, sizeof(buf), passLog)) {
1068 fputs(buf, logFile);
1072 (void)unlink(logFileName);
1080 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1082 if (!canfork || debug || Fork() == 0) {
1083 SalvageFileSys1(partP, singleVolumeNumber);
1084 if (canfork && !debug) {
1090 Wait("SalvageFileSys");
1093 char *get_DevName(pbuffer, wpath)
1094 char *wpath, *pbuffer;
1096 char pbuf[128], *ptr;
1097 strcpy(pbuf, pbuffer);
1098 ptr = (char *)strrchr(pbuf, '/');
1101 strcpy(wpath, pbuf);
1104 ptr = (char *)strrchr(pbuffer, '/');
1106 strcpy(pbuffer, ptr+1);
1112 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1115 char inodeListPath[50];
1116 static char tmpDevName[100];
1117 static char wpath[100];
1118 struct VolumeSummary *vsp, *esp;
1121 fileSysPartition = partP;
1122 fileSysDevice = fileSysPartition->device;
1123 fileSysPathName = VPartitionPath(fileSysPartition);
1126 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1127 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1128 name = partP->devName;
1130 fileSysPath = fileSysPathName;
1131 strcpy(tmpDevName, partP->devName);
1132 name = get_DevName(tmpDevName, wpath);
1133 fileSysDeviceName = name;
1134 filesysfulldev = wpath;
1137 VLockPartition(partP->name);
1138 if (singleVolumeNumber || ForceSalvage)
1141 ForceSalvage = UseTheForceLuke(fileSysPath);
1143 if (singleVolumeNumber) {
1144 if (!VConnectFS()) {
1145 Abort("Couldn't connect to file server\n");
1147 AskOffline(singleVolumeNumber);
1150 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1152 Log("***Forced salvage of all volumes on this partition***\n");
1157 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1164 assert((dirp = opendir(fileSysPath)) != NULL);
1165 while (dp = readdir(dirp)) {
1166 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1167 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1169 Log("Removing old salvager temp files %s\n", dp->d_name);
1170 strcpy(npath, fileSysPath);
1172 strcat(npath, dp->d_name);
1178 tdir = (tmpdir ? tmpdir : fileSysPath);
1180 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1181 (void) strcpy(inodeListPath, _tempnam(tdir, "salvage.inodes."));
1183 sprintf(inodeListPath, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1185 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1186 unlink(inodeListPath);
1190 /* Using nt_unlink here since we're really using the delete on close
1191 * semantics of unlink. In most places in the salvager, we really do
1192 * mean to unlink the file at that point. Those places have been
1193 * modified to actually do that so that the NT crt can be used there.
1195 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1196 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1198 inodeFd = open(inodeListPath, O_RDONLY);
1199 unlink(inodeListPath);
1202 Abort("Temporary file %s is missing...\n",
1204 if (ListInodeOption) {
1208 /* enumerate volumes in the partition.
1209 figure out sets of read-only + rw volumes.
1210 salvage each set, read-only volumes first, then read-write.
1211 Fix up inodes on last volume in set (whether it is read-write
1214 GetVolumeSummary(singleVolumeNumber);
1216 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1217 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1218 for (j=i; j < nVolumesInInodeFile
1219 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1220 VolumeId vid = inodeSummary[j].volumeId;
1221 struct VolumeSummary *tsp;
1222 /* Scan volume list (from partition root directory) looking for the
1223 current rw volume number in the volume list from the inode scan.
1224 If there is one here that is not in the inode volume list,
1226 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1228 DeleteExtraVolumeHeaderFile(vsp);
1230 /* Now match up the volume summary info from the root directory with the
1231 entry in the volume list obtained from scanning inodes */
1232 inodeSummary[j].volSummary = NULL;
1233 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1234 if (tsp->header.id == vid) {
1235 inodeSummary[j].volSummary = tsp;
1241 /* Salvage the group of volumes (several read-only + 1 read/write)
1242 * starting with the current read-only volume we're looking at.
1244 SalvageVolumeGroup(&inodeSummary[i], j-i);
1247 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1248 for ( ; vsp<esp; vsp++) {
1250 DeleteExtraVolumeHeaderFile(vsp);
1253 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1254 RemoveTheForce(fileSysPath);
1256 if (!Testing && singleVolumeNumber) {
1257 AskOnline(singleVolumeNumber, fileSysPartition->name);
1259 /* Step through the volumeSummary list and set all volumes on-line.
1260 * The volumes were taken off-line in GetVolumeSummary.
1262 for (j=0; j<nVolumes; j++) {
1263 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1268 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1269 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1272 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1275 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1277 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1278 vsp->fileName, (Testing? "would have been ":""));
1280 unlink(vsp->fileName);
1284 CompareInodes(_p1,_p2)
1285 const void *_p1,*_p2;
1287 register const struct ViceInodeInfo *p1 = _p1;
1288 register const struct ViceInodeInfo *p2 = _p2;
1289 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1290 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1291 VolumeId p1rwid, p2rwid;
1292 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1293 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1294 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1295 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1296 if (p1rwid < p2rwid)
1298 if (p1rwid > p2rwid)
1300 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1301 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1302 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1303 return (p1->u.special.type < p2->u.special.type? -1: 1);
1304 if (p1->u.vnode.volumeId == p1rwid)
1306 if (p2->u.vnode.volumeId == p2rwid)
1308 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1310 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1311 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1312 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1314 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1316 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1318 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1320 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1322 /* The following tests are reversed, so that the most desirable
1323 of several similar inodes comes first */
1324 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1325 #ifdef AFS_3DISPARES
1326 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1327 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1330 #ifdef AFS_SGI_EXMAG
1331 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1332 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1337 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1338 #ifdef AFS_3DISPARES
1339 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1340 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1343 #ifdef AFS_SGI_EXMAG
1344 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1345 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1350 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1351 #ifdef AFS_3DISPARES
1352 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1353 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1356 #ifdef AFS_SGI_EXMAG
1357 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1358 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1363 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1364 #ifdef AFS_3DISPARES
1365 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1366 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1369 #ifdef AFS_SGI_EXMAG
1370 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1371 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1379 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1380 register struct InodeSummary * summary)
1382 int volume = ip->u.vnode.volumeId;
1383 int rwvolume = volume;
1384 register n, nSpecial;
1385 register Unique maxunique;
1388 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1390 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1392 rwvolume = ip->u.special.parentId;
1393 /* This isn't quite right, as there could (in error) be different
1394 parent inodes in different special vnodes */
1397 if (maxunique < ip->u.vnode.vnodeUniquifier)
1398 maxunique = ip->u.vnode.vnodeUniquifier;
1402 summary->volumeId = volume;
1403 summary->RWvolumeId = rwvolume;
1404 summary->nInodes =n;
1405 summary->nSpecialInodes = nSpecial;
1406 summary->maxUniquifier = maxunique;
1409 int OnlyOneVolume(inodeinfo, singleVolumeNumber)
1410 struct ViceInodeInfo *inodeinfo;
1411 VolumeId singleVolumeNumber;
1413 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1414 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1415 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1420 * Collect list of inodes in file named by path. If a truly fatal error,
1421 * unlink the file and abort. For lessor errors, return -1. The file will
1422 * be unlinked by the caller.
1424 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1427 int summaryFd, forceSal, err;
1428 struct ViceInodeInfo *ip;
1429 struct InodeSummary summary;
1430 char summaryFileName[50];
1433 char *dev = fileSysPath;
1434 char *wpath = fileSysPath;
1436 char *dev = fileSysDeviceName;
1437 char *wpath = filesysfulldev;
1439 char *part = fileSysPath;
1442 /* This file used to come from vfsck; cobble it up ourselves now... */
1443 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1445 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1450 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1452 if (forceSal && !ForceSalvage) {
1453 Log("***Forced salvage of all volumes on this partition***\n");
1456 inodeFd = open(path, O_RDWR);
1457 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1459 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1461 tdir = (tmpdir ? tmpdir : part);
1463 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1464 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1466 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1468 summaryFile = fopen(summaryFileName, "a+");
1469 if (summaryFile == NULL) {
1472 Abort("Unable to create inode summary file\n");
1474 if (!canfork || debug || Fork() == 0) {
1476 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1478 fclose(summaryFile); close(inodeFd);
1479 unlink(summaryFileName);
1480 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1481 RemoveTheForce(fileSysPath);
1482 Log("%s vice inodes on %s; not salvaged\n",
1483 singleVolumeNumber? "No applicable": "No", dev);
1486 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1488 fclose(summaryFile); close(inodeFd);
1490 unlink(summaryFileName);
1491 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1493 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1494 fclose(summaryFile); close(inodeFd);
1496 unlink(summaryFileName);
1497 Abort("Unable to read inode table; %s not salvaged\n", dev);
1499 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1500 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1501 write(inodeFd, ip, status.st_size) != status.st_size) {
1502 fclose(summaryFile); close(inodeFd);
1504 unlink(summaryFileName);
1505 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1509 CountVolumeInodes(ip, nInodes, &summary);
1510 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1511 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1512 fclose(summaryFile); close(inodeFd);
1515 summary.index += (summary.nInodes);
1516 nInodes -= summary.nInodes;
1517 ip += summary.nInodes;
1519 /* Following fflush is not fclose, because if it was debug mode would not work */
1520 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1521 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1522 fclose(summaryFile); close(inodeFd);
1525 if (canfork && !debug) {
1531 if (Wait("Inode summary") == -1) {
1532 fclose(summaryFile); close(inodeFd);
1534 unlink(summaryFileName);
1535 Exit(1); /* salvage of this partition aborted */
1538 assert(fstat(fileno(summaryFile), &status) != -1);
1539 if ( status.st_size != 0 ) {
1541 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1542 assert(inodeSummary != NULL);
1543 /* For GNU we need to do lseek to get the file pointer moved. */
1544 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1545 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1546 assert(ret == status.st_size);
1548 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1549 fclose(summaryFile);
1551 unlink(summaryFileName);
1555 /* Comparison routine for volume sort.
1556 This is setup so that a read-write volume comes immediately before
1557 any read-only clones of that volume */
1558 CompareVolumes(_p1,_p2)
1559 const void *_p1,*_p2;
1561 register const struct VolumeSummary *p1 = _p1;
1562 register const struct VolumeSummary *p2 = _p2;
1563 if (p1->header.parent != p2->header.parent)
1564 return p1->header.parent < p2->header.parent? -1: 1;
1565 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1567 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1569 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1572 void GetVolumeSummary(VolumeId singleVolumeNumber)
1575 afs_int32 nvols = 0;
1576 struct VolumeSummary *vsp, vs;
1577 struct VolumeDiskHeader diskHeader;
1580 /* Get headers from volume directory */
1581 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1582 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1583 if (!singleVolumeNumber) {
1584 while (dp = readdir(dirp)) {
1585 char *p = dp->d_name;
1586 p = strrchr(dp->d_name, '.');
1587 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1589 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1590 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1591 == sizeof (diskHeader) &&
1592 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1593 DiskToVolumeHeader(&vs.header, &diskHeader);
1600 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1604 if (!nvols) nvols = 1;
1605 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1607 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1608 assert(volumeSummaryp != NULL);
1611 vsp = volumeSummaryp;
1612 while (dp = readdir(dirp)) {
1613 char *p = dp->d_name;
1614 p = strrchr(dp->d_name, '.');
1615 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1618 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1619 || read(fd, &diskHeader, sizeof (diskHeader))
1620 != sizeof (diskHeader)
1621 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1626 if (!singleVolumeNumber) {
1627 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1628 dp->d_name, (Testing? "it would have been ":""));
1634 char nameShouldBe[64];
1635 DiskToVolumeHeader(&vsp->header, &diskHeader);
1636 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1637 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1640 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1641 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1642 if (singleVolumeNumber)
1643 AskOffline(vsp->header.id);
1644 if (strcmp(nameShouldBe, dp->d_name)) {
1645 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 ":""));
1650 vsp->fileName = ToString(dp->d_name);
1660 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1663 /* Find the link table. This should be associated with the RW volume or, if
1664 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1666 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1667 struct ViceInodeInfo *allInodes)
1670 struct ViceInodeInfo *ip;
1672 for (i=0; i<nVols; i++) {
1673 ip = allInodes + isp[i].index;
1674 for (j=0; j<isp[i].nSpecialInodes; j++) {
1675 if (ip[j].u.special.type == VI_LINKTABLE)
1676 return ip[j].inodeNumber;
1682 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1684 struct versionStamp version;
1687 if (!VALID_INO(ino))
1688 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1689 isp->volumeId, INODESPECIAL,
1690 VI_LINKTABLE, isp->RWvolumeId);
1691 if (!VALID_INO(ino))
1692 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1693 isp->RWvolumeId, errno);
1694 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1695 fdP = IH_OPEN(VGLinkH);
1697 Abort("Can't open link table for volume %u (error = %d)\n",
1698 isp->RWvolumeId, errno);
1700 if (FDH_TRUNC(fdP, 0)<0)
1701 Abort("Can't truncate link table for volume %u (error = %d)\n",
1702 isp->RWvolumeId, errno);
1704 version.magic = LINKTABLEMAGIC;
1705 version.version = LINKTABLEVERSION;
1707 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1709 Abort("Can't truncate link table for volume %u (error = %d)\n",
1710 isp->RWvolumeId, errno);
1712 FDH_REALLYCLOSE(fdP);
1714 /* If the volume summary exits (i.e., the V*.vol header file exists),
1715 * then set this inode there as well.
1717 if (isp->volSummary)
1718 isp->volSummary->header.linkTable = ino;
1724 void *nt_SVG(void *arg)
1726 SVGParms_t *parms = (SVGParms_t*)arg;
1727 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1731 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1734 pthread_attr_t tattr;
1738 /* Initialize per volume global variables, even if later code does so */
1742 memset(&VolInfo, 0, sizeof(VolInfo));
1744 parms.svgp_inodeSummaryp = isp;
1745 parms.svgp_count = nVols;
1746 code = pthread_attr_init(&tattr);
1748 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1752 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1754 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1758 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1760 Log("Failed to create thread to salvage volume group %u\n",
1764 (void) pthread_join(tid, NULL);
1766 #endif /* AFS_NT40_ENV */
1768 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1770 struct ViceInodeInfo *inodes,*allInodes,*ip;
1771 int i, totalInodes, size, salvageTo;
1775 int dec_VGLinkH = 0;
1777 FdHandle_t *fdP = NULL;
1780 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1781 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1782 if (!ForceSalvage && QuickCheck(isp, nVols))
1785 if (ShowMounts && !haveRWvolume)
1787 if (canfork && !debug && Fork() != 0) {
1788 (void) Wait("Salvage volume group");
1791 for (i = 0, totalInodes = 0; i<nVols; i++)
1792 totalInodes += isp[i].nInodes;
1793 size = totalInodes * sizeof (struct ViceInodeInfo);
1794 inodes = (struct ViceInodeInfo *) malloc(size);
1795 allInodes = inodes - isp->index; /* this would the base of all the inodes
1796 for the partition, if all the inodes
1797 had been read into memory */
1798 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1)
1799 assert(read(inodeFd,inodes,size) == size)
1801 /* Don't try to salvage a read write volume if there isn't one on this
1803 salvageTo = haveRWvolume? 0:1;
1805 #ifdef AFS_NAMEI_ENV
1806 ino = FindLinkHandle(isp, nVols, allInodes);
1807 if (VALID_INO(ino)) {
1808 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1809 fdP = IH_OPEN(VGLinkH);
1811 if (!VALID_INO(ino) || fdP == NULL) {
1812 Log("%s link table for volume %u.\n",
1813 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1815 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1818 CreateLinkTable(isp, ino);
1822 FDH_REALLYCLOSE(fdP);
1824 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1827 /* Salvage in reverse order--read/write volume last; this way any
1828 Inodes not referenced by the time we salvage the read/write volume
1829 can be picked up by the read/write volume */
1830 /* ACTUALLY, that's not done right now--the inodes just vanish */
1831 for (i = nVols-1; i>=salvageTo; i--) {
1833 struct InodeSummary *lisp = &isp[i];
1834 #ifdef AFS_NAMEI_ENV
1835 /* If only the RO is present on this partition, the link table
1836 * shows up as a RW volume special file. Need to make sure the
1837 * salvager doesn't try to salvage the non-existent RW.
1839 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1840 /* If this only special inode is the link table, continue */
1841 if (inodes->u.special.type == VI_LINKTABLE) {
1847 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1848 lisp->volumeId, (Testing?"(READONLY mode)":""));
1849 /* Check inodes twice. The second time do things seriously. This
1850 way the whole RO volume can be deleted, below, if anything goes wrong */
1851 for (check = 1; check>=0; check--) {
1853 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1854 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1855 if (rw && deleteMe) {
1856 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1857 volume won't be called */
1863 if (rw && check == 1)
1865 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1866 MaybeZapVolume(lisp,"Vnode index", 0, check);
1872 /* Fix actual inode counts */
1874 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1875 static int TraceBadLinkCounts = 0;
1876 #ifdef AFS_NAMEI_ENV
1877 if (VGLinkH->ih_ino == ip->inodeNumber) {
1878 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1879 VGLinkH_p1 = ip->u.param[0];
1880 continue; /* Deal with this last. */
1883 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1884 TraceBadLinkCounts--; /* Limit reports, per volume */
1885 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1886 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1887 ip->byteCount, ip->u.param[0], ip->u.param[1],
1888 ip->u.param[2], ip->u.param[3]);
1890 while (ip->linkCount > 0) {
1891 /* below used to assert, not break */
1893 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1894 Log ("idec failed. inode %s errno %d\n",
1895 PrintInode(NULL, ip->inodeNumber), errno);
1901 while (ip->linkCount < 0) {
1902 /* these used to be asserts */
1904 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1905 Log ("iinc failed. inode %s errno %d\n",
1906 PrintInode(NULL, ip->inodeNumber) ,errno);
1913 #ifdef AFS_NAMEI_ENV
1914 while (dec_VGLinkH > 0) {
1915 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1916 Log("idec failed on link table, errno = %d\n", errno);
1920 while (dec_VGLinkH < 0) {
1921 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1922 Log("iinc failed on link table, errno = %d\n", errno);
1929 /* Directory consistency checks on the rw volume */
1931 SalvageVolume(isp, VGLinkH);
1932 IH_RELEASE(VGLinkH);
1934 if (canfork && !debug) {
1940 int QuickCheck(register struct InodeSummary *isp, int nVols)
1942 /* Check headers BEFORE forking */
1946 for (i = 0; i<nVols; i++) {
1947 struct VolumeSummary *vs = isp[i].volSummary;
1948 VolumeDiskData volHeader;
1950 /* Don't salvage just because phantom rw volume is there... */
1951 /* (If a read-only volume exists, read/write inodes must also exist) */
1952 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1956 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1957 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1958 == sizeof(volHeader)
1959 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1960 && volHeader.dontSalvage == DONT_SALVAGE
1961 && volHeader.needsSalvaged == 0
1962 && volHeader.destroyMe == 0) {
1963 if (volHeader.inUse == 1) {
1964 volHeader.inUse = 0;
1965 volHeader.inService = 1;
1967 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1968 != sizeof(volHeader)) {
1985 /* SalvageVolumeHeaderFile
1987 * Salvage the top level V*.vol header file. Make sure the special files
1988 * exist and that there are no duplicates.
1990 * Calls SalvageHeader for each possible type of volume special file.
1993 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1994 register struct ViceInodeInfo *inodes,
1995 int RW, int check, int *deleteMe)
1999 register struct ViceInodeInfo *ip;
2000 int allinodesobsolete = 1;
2001 struct VolumeDiskHeader diskHeader;
2005 memset(&tempHeader, 0, sizeof(tempHeader));
2006 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2007 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2008 tempHeader.id = isp->volumeId;
2009 tempHeader.parent = isp->RWvolumeId;
2010 /* Check for duplicates (inodes are sorted by type field) */
2011 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2012 ip = &inodes[isp->index+i];
2013 if (ip->u.special.type == (ip+1)->u.special.type) {
2014 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2018 for (i = 0; i<isp->nSpecialInodes; i++) {
2019 ip = &inodes[isp->index+i];
2020 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2022 Log("Rubbish header inode\n");
2025 Log("Rubbish header inode; deleted\n");
2027 else if (!stuff[ip->u.special.type-1].obsolete) {
2028 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2029 if (!check && ip->u.special.type != VI_LINKTABLE)
2030 ip->linkCount--; /* Keep the inode around */
2031 allinodesobsolete = 0;
2035 if (allinodesobsolete) {
2042 VGLinkH_cnt ++; /* one for every header. */
2044 if (!RW && !check && isp->volSummary) {
2045 ClearROInUseBit(isp->volSummary);
2049 for (i = 0; i< MAXINODETYPE; i++) {
2050 if (stuff[i].inodeType == VI_LINKTABLE) {
2051 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2052 * And we may have recreated the link table earlier, so set the
2053 * RW header as well.
2055 if (VALID_INO(VGLinkH->ih_ino)) {
2056 *stuff[i].inode = VGLinkH->ih_ino;
2060 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2064 if (isp->volSummary == NULL) {
2066 sprintf(name, VFORMAT, isp->volumeId);
2068 Log("No header file for volume %u\n", isp->volumeId);
2071 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2072 isp->volumeId, (Testing?"it would have been ":""),
2073 fileSysPathName, name);
2074 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2075 assert(headerFd != -1)
2076 isp->volSummary = (struct VolumeSummary *)
2077 malloc(sizeof(struct VolumeSummary));
2078 isp->volSummary->fileName = ToString(name);
2082 /* hack: these two fields are obsolete... */
2083 isp->volSummary->header.volumeAcl = 0;
2084 isp->volSummary->header.volumeMountTable = 0;
2086 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2087 /* We often remove the name before calling us, so we make a fake one up */
2088 if (isp->volSummary->fileName) {
2089 strcpy(name, isp->volSummary->fileName);
2091 sprintf(name, VFORMAT, isp->volumeId);
2092 isp->volSummary->fileName = ToString(name);
2095 Log("Header file %s is damaged or no longer valid%s\n",
2096 name, (check ? "" : "; repairing"));
2100 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2101 assert(headerFd != -1)
2105 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2107 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2109 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2110 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2111 != sizeof(struct VolumeDiskHeader)) {
2112 Log("Couldn't rewrite volume header file!\n");
2119 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2120 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2124 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2125 int check, int *deleteMe)
2128 VolumeDiskData volumeInfo;
2129 struct versionStamp fileHeader;
2138 #ifndef AFS_NAMEI_ENV
2139 if ( sp->inodeType == VI_LINKTABLE)
2142 if (*(sp->inode) == 0) {
2144 Log("Missing inode in volume header (%s)\n", sp->description);
2147 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2148 sp->description, (Testing ? "it would have recreated it": "recreating"));
2150 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2151 isp->volumeId, INODESPECIAL,
2152 sp->inodeType, isp->RWvolumeId);
2153 if (!VALID_INO(*(sp->inode)))
2154 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2155 sp->description, errno);
2160 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2161 fdP = IH_OPEN(specH);
2162 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2163 /* bail out early and destroy the volume */
2164 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2166 if (deleteMe) *deleteMe = 1;
2171 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2172 sp->description, errno);
2175 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2176 || header.fileHeader.magic != sp->stamp.magic)) {
2178 Log("Part of the header (%s) is corrupted\n", sp->description);
2179 FDH_REALLYCLOSE(fdP);
2183 Log("Part of the header (%s) is corrupted; recreating\n",
2187 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2190 FDH_REALLYCLOSE(fdP);
2194 if (recreate && !Testing) {
2196 Abort("Internal error: recreating volume header (%s) in check mode\n",
2198 code = FDH_TRUNC(fdP, 0);
2200 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2201 sp->description, errno);
2203 /* The following code should be moved into vutil.c */
2204 if (sp->inodeType == VI_VOLINFO) {
2206 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2207 header.volumeInfo.stamp = sp->stamp;
2208 header.volumeInfo.id = isp->volumeId;
2209 header.volumeInfo.parentId = isp->RWvolumeId;
2210 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2211 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2212 header.volumeInfo.inService = 0;
2213 header.volumeInfo.blessed = 0;
2214 /* The + 1000 is a hack in case there are any files out in venus caches */
2215 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2216 header.volumeInfo.type =
2217 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2218 header.volumeInfo.needsCallback = 0;
2219 gettimeofday(&tp,0);
2220 header.volumeInfo.creationDate = tp.tv_sec;
2221 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2222 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2224 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2225 sizeof(header.volumeInfo));
2226 if (code != sizeof(header.volumeInfo)) {
2228 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2229 sp->description, errno);
2230 Abort("Unable to write entire volume header file (%s)\n",
2235 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2236 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2238 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2239 if (code != sizeof(sp->stamp)) {
2241 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2242 sp->description, errno);
2243 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2248 FDH_REALLYCLOSE(fdP);
2250 if (sp->inodeType == VI_VOLINFO) {
2251 VolInfo = header.volumeInfo;
2254 if (VolInfo.updateDate) {
2255 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2256 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2257 (Testing?"it would have been ":""), update);
2259 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2260 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2269 int SalvageVnodes(register struct InodeSummary *rwIsp,
2270 register struct InodeSummary * thisIsp,
2271 register struct ViceInodeInfo * inodes, int check)
2273 int ilarge, ismall, ioffset, RW, nInodes;
2274 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2275 if (Showmode) return 0;
2276 RW = (rwIsp == thisIsp);
2277 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2278 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2279 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2280 if (check && ismall == -1)
2282 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2283 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2284 return (ilarge==0 && ismall==0 ? 0: -1);
2287 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2288 register struct ViceInodeInfo *ip,
2289 int nInodes, struct VolumeSummary *volSummary, int check)
2291 VolumeId volumeNumber;
2292 char buf[SIZEOF_LARGEDISKVNODE];
2293 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2295 StreamHandle_t *file;
2296 struct VnodeClassInfo *vcp;
2298 int vnodeIndex, nVnodes;
2299 afs_ino_str_t stmp1, stmp2;
2303 volumeNumber = volSummary->header.id;
2304 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2305 fdP = IH_OPEN(handle);
2306 assert(fdP != NULL);
2307 file = FDH_FDOPEN(fdP, "r+");
2308 assert(file != NULL)
2309 vcp = &VnodeClassInfo[class];
2310 size = OS_SIZE(fdP->fd_fd);
2312 nVnodes = (size / vcp->diskSize) - 1;
2314 assert((nVnodes+1) * vcp->diskSize == size)
2315 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2320 for (vnodeIndex = 0;
2321 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2322 nVnodes--, vnodeIndex++) {
2323 if (vnode->type != vNull) {
2324 int vnodeChanged = 0;
2325 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2326 /* Log programs that belong to root (potentially suid root);
2327 don't bother for read-only or backup volumes */
2328 #ifdef notdef /* This is done elsewhere */
2329 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2330 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2331 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2332 vnode->owner, vnode->modeBits);
2334 if (VNDISK_GET_INO(vnode) == 0) {
2336 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2337 memset(vnode, 0, vcp->diskSize);
2342 if (vcp->magic != vnode->vnodeMagic) {
2343 /* bad magic #, probably partially created vnode */
2344 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2345 memset(vnode, 0, vcp->diskSize);
2349 /* ****** Should do a bit more salvage here: e.g. make sure
2350 vnode type matches what it should be given the index */
2351 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2352 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2353 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2354 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2361 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2362 /* The following doesn't work, because the version number
2363 is not maintained correctly by the file server */
2364 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2365 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2367 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2374 /* For RW volume, look for vnode with matching inode number;
2375 if no such match, take the first determined by our sort
2377 register struct ViceInodeInfo *lip = ip;
2378 register lnInodes = nInodes;
2379 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2380 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2389 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2390 /* "Matching" inode */
2394 vu = vnode->uniquifier;
2395 iu = ip->u.vnode.vnodeUniquifier;
2396 vd = vnode->dataVersion;
2397 id = ip->u.vnode.inodeDataVersion;
2399 * Because of the possibility of the uniquifier overflows (> 4M)
2400 * we compare them modulo the low 22-bits; we shouldn't worry
2401 * about mismatching since they shouldn't to many old
2402 * uniquifiers of the same vnode...
2404 if (IUnique(vu) != IUnique(iu)) {
2406 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2407 vnodeNumber, IUnique(vu), IUnique(iu));
2410 vnode->uniquifier = iu;
2411 #ifdef AFS_3DISPARES
2412 vnode->dataVersion = (id >= vd ?
2413 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2414 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2416 #if defined(AFS_SGI_EXMAG)
2417 vnode->dataVersion = (id >= vd ?
2418 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2419 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2421 vnode->dataVersion = (id>vd ? id:vd);
2422 #endif /* AFS_SGI_EXMAG */
2423 #endif /* AFS_3DISPARES */
2427 /* don't bother checking for vd > id any more, since
2428 partial file transfers always result in this state,
2429 and you can't do much else anyway (you've already
2430 found the best data you can) */
2431 #ifdef AFS_3DISPARES
2432 if (!vnodeIsDirectory(vnodeNumber) &&
2433 ((vd < id && (id-vd) < 1887437) ||
2434 ((vd > id && (vd-id) > 1887437)))) {
2436 #if defined(AFS_SGI_EXMAG)
2437 if (!vnodeIsDirectory(vnodeNumber) &&
2438 ((vd < id && (id-vd) < 15099494) ||
2439 ((vd > id && (vd-id) > 15099494)))) {
2441 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2442 #endif /* AFS_SGI_EXMAG */
2444 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2445 vnode->dataVersion = id;
2450 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2453 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2454 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2455 PrintInode(stmp2, ip->inodeNumber),
2458 VNDISK_SET_INO(vnode, ip->inodeNumber);
2463 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2465 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2466 PrintInode(stmp2, ip->inodeNumber),
2469 VNDISK_SET_INO(vnode, ip->inodeNumber);
2472 if (ip->byteCount != vnode->length) {
2474 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2475 vnodeNumber, vnode->length, ip->byteCount);
2479 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2480 vnodeNumber, vnode->length, ip->byteCount);
2481 vnode->length = ip->byteCount;
2485 ip->linkCount--; /* Keep the inode around */
2489 else { /* no matching inode */
2490 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2491 /* No matching inode--get rid of the vnode */
2493 if (VNDISK_GET_INO(vnode)) {
2495 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2496 vnodeNumber, vnode->uniquifier,
2497 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2500 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2505 if (VNDISK_GET_INO(vnode)) {
2507 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2508 vnodeNumber, vnode->uniquifier,
2509 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2510 ctime((time_t *)&(vnode->serverModifyTime)));
2513 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)));
2515 memset(vnode, 0, vcp->diskSize);
2518 /* Should not reach here becuase we checked for
2519 * (inodeNumber == 0) above. And where we zero the vnode,
2520 * we also goto vnodeDone.
2524 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2528 } /* VNDISK_GET_INO(vnode) != 0 */
2530 assert(!(vnodeChanged && check));
2531 if (vnodeChanged && !Testing) {
2532 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2533 (char*)vnode, vcp->diskSize)
2535 VolumeChanged = 1; /* For break call back */
2546 struct VnodeEssence *CheckVnodeNumber(vnodeNumber)
2547 VnodeId vnodeNumber;
2550 struct VnodeInfo *vip;
2553 class = vnodeIdToClass(vnodeNumber);
2554 vip = &vnodeInfo[class];
2555 offset = vnodeIdToBitNumber(vnodeNumber);
2556 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2560 void CopyOnWrite(register struct DirSummary *dir)
2562 /* Copy the directory unconditionally if we are going to change it:
2563 * not just if was cloned.
2565 struct VnodeDiskObject vnode;
2566 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2567 Inode oldinode, newinode;
2570 if (dir->copied || Testing)
2572 DFlush(); /* Well justified paranoia... */
2574 code = IH_IREAD(vnodeInfo[vLarge].handle,
2575 vnodeIndexOffset(vcp, dir->vnodeNumber),
2576 (char*)&vnode, sizeof (vnode));
2577 assert(code == sizeof(vnode));
2578 oldinode = VNDISK_GET_INO(&vnode);
2579 /* Increment the version number by a whole lot to avoid problems with
2580 * clients that were promised new version numbers--but the file server
2581 * crashed before the versions were written to disk.
2583 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2584 dir->rwVid, dir->vnodeNumber,
2585 vnode.uniquifier, vnode.dataVersion += 200);
2586 assert(VALID_INO(newinode));
2587 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2589 VNDISK_SET_INO(&vnode, newinode);
2590 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2591 vnodeIndexOffset(vcp, dir->vnodeNumber),
2592 (char*)&vnode, sizeof (vnode));
2593 assert(code == sizeof (vnode));
2595 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2596 fileSysDevice, newinode);
2597 /* Don't delete the original inode right away, because the directory is
2598 * still being scanned.
2603 /* This function should either successfully create a new dir, or give up and leave
2604 * things the way they were. In particular, if it fails to write the new dir properly,
2605 * it should return w/o changing the reference to the old dir.
2607 void CopyAndSalvage(register struct DirSummary *dir)
2609 struct VnodeDiskObject vnode;
2610 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2611 Inode oldinode, newinode;
2613 register afs_int32 code;
2614 afs_int32 parentUnique= 1;
2615 struct VnodeEssence *vnodeEssence;
2619 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2620 code = IH_IREAD(vnodeInfo[vLarge].handle,
2621 vnodeIndexOffset(vcp, dir->vnodeNumber),
2622 (char*)&vnode, sizeof (vnode));
2623 assert(code == sizeof (vnode));
2624 oldinode = VNDISK_GET_INO(&vnode);
2625 /* Increment the version number by a whole lot to avoid problems with
2626 * clients that were promised new version numbers--but the file server
2627 * crashed before the versions were written to disk.
2629 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2630 dir->rwVid, dir->vnodeNumber,
2632 vnode.dataVersion += 200);
2633 assert(VALID_INO(newinode));
2634 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2636 /* Assign . and .. vnode numbers from dir and vnode.parent.
2637 * The uniquifier for . is in the vnode.
2638 * The uniquifier for .. might be set to a bogus value of 1 and
2639 * the salvager will later clean it up.
2641 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2642 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2644 code = DirSalvage(&dir->dirHandle, &newdir,
2645 dir->vnodeNumber, vnode.uniquifier,
2646 (vnode.parent?vnode.parent:dir->vnodeNumber),
2648 if (code == 0) code = DFlush();
2650 /* didn't really build the new directory properly, let's just give up. */
2651 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2653 Log("Directory salvage returned code %d, continuing.\n", code);
2656 Log("Checking the results of the directory salvage...\n");
2657 if (!DirOK(&newdir)) {
2658 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2659 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2664 VNDISK_SET_INO(&vnode, newinode);
2665 vnode.length = Length(&newdir);
2666 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2667 vnodeIndexOffset(vcp, dir->vnodeNumber),
2668 (char*)&vnode, sizeof (vnode));
2669 assert(code == sizeof (vnode));
2671 nt_sync(fileSysDevice);
2673 sync(); /* this is slow, but hopefully rarely called. We don't have
2674 * an open FD on the file itself to fsync.
2677 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2679 dir->dirHandle = newdir;
2682 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2685 struct VnodeEssence *vnodeEssence;
2686 afs_int32 dirOrphaned, todelete;
2688 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2690 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2691 if (vnodeEssence == NULL) {
2693 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2694 dir->vnodeNumber, dir->name?dir->name:"??",
2695 name, vnodeNumber, unique);
2699 assert(Delete(&dir->dirHandle, name) == 0)
2705 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2706 * mount inode for the partition. If this inode were deleted, it would crash
2709 if (vnodeEssence->InodeNumber == 0) {
2710 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2711 dir->vnodeNumber, (dir->name?dir->name:"??"),
2712 name, vnodeNumber, unique,
2713 (Testing?"-- would have deleted":" -- deleted"));
2716 assert(Delete(&dir->dirHandle, name) == 0);
2722 if (!(vnodeNumber & 1) && !Showmode &&
2723 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2724 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2725 dir->vnodeNumber, (dir->name?dir->name:"??"),
2726 name, vnodeNumber, unique,
2727 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2731 assert(Delete(&dir->dirHandle, name) == 0);
2737 /* Check if the Uniquifiers match. If not, change the directory entry
2738 * so its unique matches the vnode unique. Delete if the unique is zero
2739 * or if the directory is orphaned.
2741 if (!IUnique(vnodeEssence->unique) ||
2742 (IUnique(vnodeEssence->unique) != IUnique(unique)) ) {
2743 if ( !IUnique(vnodeEssence->unique) &&
2744 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2745 /* This is an orphaned directory. Don't delete the . or ..
2746 * entry. Otherwise, it will get created in the next
2747 * salvage and deleted again here. So Just skip it.
2752 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2755 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2756 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2757 name, vnodeNumber, unique, vnodeEssence->unique,
2758 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2762 fid.Vnode = vnodeNumber;
2763 fid.Unique = vnodeEssence->unique;
2765 assert(Delete(&dir->dirHandle, name) == 0)
2767 assert(Create(&dir->dirHandle, name, &fid) == 0)
2769 if (todelete) return; /* no need to continue */
2772 if (strcmp(name,".") == 0) {
2773 if (dir->vnodeNumber != vnodeNumber || (IUnique(dir->unique) != IUnique(unique))) {
2775 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2776 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2779 assert(Delete(&dir->dirHandle, ".") == 0)
2780 fid.Vnode = dir->vnodeNumber;
2781 fid.Unique = dir->unique;
2782 assert(Create(&dir->dirHandle, ".", &fid) == 0)
2785 vnodeNumber = fid.Vnode; /* Get the new Essence */
2786 unique = fid.Unique;
2787 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2791 else if (strcmp(name,"..") == 0) {
2794 struct VnodeEssence *dotdot;
2795 pa.Vnode = dir->parent;
2796 dotdot = CheckVnodeNumber(pa.Vnode);
2797 assert (dotdot != NULL); /* XXX Should not be assert */
2798 pa.Unique = dotdot->unique;
2801 pa.Vnode = dir->vnodeNumber;
2802 pa.Unique = dir->unique;
2804 if ((pa.Vnode != vnodeNumber) || (IUnique(pa.Unique) != IUnique(unique))) {
2805 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2806 dir->vnodeNumber, IUnique(dir->unique), vnodeNumber, IUnique(unique));
2809 assert(Delete(&dir->dirHandle, "..") == 0);
2810 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2813 vnodeNumber = pa.Vnode; /* Get the new Essence */
2815 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2817 dir->haveDotDot = 1;
2818 } else if (strncmp(name,".__afs",6) == 0) {
2820 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2821 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2825 assert(Delete(&dir->dirHandle, name) == 0)
2827 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2828 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2832 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2833 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2834 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber, dir->vnodeNumber);
2835 if (ShowMounts && (vnodeEssence->type == vSymlink) && !(vnodeEssence->modeBits & 0111)) {
2841 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2842 vnodeEssence->InodeNumber);
2844 assert(fdP != NULL);
2845 size = FDH_SIZE(fdP);
2847 memset(buf, 0, 1024);
2848 if (size > 1024) size = 1024;
2849 code = FDH_READ(fdP, buf, size);
2850 assert(code == size);
2851 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2852 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2853 dir->name?dir->name:"??", name, buf);
2854 FDH_REALLYCLOSE(fdP);
2857 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2858 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2859 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2860 if (vnodeIdToClass(vnodeNumber) == vLarge && vnodeEssence->name == (char *)0) {
2862 if (n = (char*)malloc(strlen(name)+1))
2864 vnodeEssence->name = n;
2867 /* The directory entry points to the vnode. Check to see if the
2868 * vnode points back to the directory. If not, then let the
2869 * directory claim it (else it might end up orphaned). Vnodes
2870 * already claimed by another directory are deleted from this
2871 * directory: hardlinks to the same vnode are not allowed
2872 * from different directories.
2874 if (vnodeEssence->parent != dir->vnodeNumber) {
2875 if (!vnodeEssence->claimed && !dirOrphaned) {
2876 /* Vnode does not point back to this directory.
2877 * Orphaned dirs cannot claim a file (it may belong to
2878 * another non-orphaned dir).
2881 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2882 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2883 vnodeNumber, unique, (Testing?"would have been ":""),
2884 vnodeEssence->parent, dir->vnodeNumber);
2886 vnodeEssence->parent = dir->vnodeNumber;
2887 vnodeEssence->changed = 1;
2889 /* Vnode was claimed by another directory */
2892 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2893 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2894 vnodeEssence->parent, vnodeNumber, unique,
2895 (Testing?"would have been ":""));
2897 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2898 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2899 vnodeEssence->parent, vnodeNumber, unique,
2900 (Testing?"would have been ":""));
2905 assert(Delete(&dir->dirHandle, name) == 0);
2910 /* This directory claims the vnode */
2911 vnodeEssence->claimed = 1;
2913 vnodeEssence->count--;
2916 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2919 register struct VnodeInfo *vip = &vnodeInfo[class];
2920 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2921 char buf[SIZEOF_LARGEDISKVNODE];
2922 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2925 StreamHandle_t *file;
2930 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2931 fdP = IH_OPEN(vip->handle);
2933 file = FDH_FDOPEN(fdP, "r+");
2934 assert(file != NULL);
2935 size = OS_SIZE(fdP->fd_fd);
2937 vip->nVnodes = (size / vcp->diskSize) - 1;
2938 if (vip->nVnodes > 0) {
2939 assert((vip->nVnodes+1)*vcp->diskSize == size)
2940 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2941 assert((vip->vnodes = (struct VnodeEssence *)
2942 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL)
2943 if (class == vLarge) {
2944 assert((vip->inodes = (Inode *)
2945 calloc(vip->nVnodes, sizeof (Inode))) != NULL)
2956 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2957 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2958 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2959 nVnodes--, vnodeIndex++) {
2960 if (vnode->type != vNull) {
2961 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2962 vip->nAllocatedVnodes++;
2963 vep->count = vnode->linkCount;
2964 vep->blockCount = nBlocks(vnode->length);
2965 vip->volumeBlockCount += vep->blockCount;
2966 vep->parent = vnode->parent;
2967 vep->unique = vnode->uniquifier;
2968 if (*maxu < vnode->uniquifier)
2969 *maxu = vnode->uniquifier;
2970 vep->modeBits = vnode->modeBits;
2971 vep->InodeNumber = VNDISK_GET_INO(vnode);
2972 vep->type = vnode->type;
2973 vep->author = vnode->author;
2974 vep->owner = vnode->owner;
2975 vep->group = vnode->group;
2976 if (vnode->type == vDirectory) {
2977 assert(class == vLarge)
2978 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2986 static char *GetDirName(vnode, vp, path)
2988 struct VnodeEssence *vp;
2991 struct VnodeEssence *parentvp;
2997 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
2999 strcat(path, vp->name);
3005 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3006 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3008 static int IsVnodeOrphaned(vnode)
3011 struct VnodeEssence *vep;
3013 if (vnode == 0) return(1); /* Vnode zero does not exist */
3014 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3015 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3016 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3018 return( IsVnodeOrphaned(vep->parent) );
3021 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3022 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3025 static struct DirSummary dir;
3026 static struct DirHandle dirHandle;
3027 struct VnodeEssence *parent;
3028 static char path[MAXPATHLEN];
3031 if (dirVnodeInfo->vnodes[i].salvaged)
3032 return; /* already salvaged */
3035 dirVnodeInfo->vnodes[i].salvaged = 1;
3037 if (dirVnodeInfo->inodes[i] == 0)
3038 return; /* Not allocated to a directory */
3040 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3041 if (parent && parent->salvaged == 0)
3042 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3043 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3044 rootdir, rootdirfound);
3045 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3046 dir.unique = dirVnodeInfo->vnodes[i].unique;
3049 dir.parent = dirVnodeInfo->vnodes[i].parent;
3050 dir.haveDot = dir.haveDotDot = 0;
3051 dir.ds_linkH = alinkH;
3052 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3054 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3057 Log("Directory bad, vnode %d; %s...\n",
3058 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3061 CopyAndSalvage(&dir);
3065 dirHandle = dir.dirHandle;
3067 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3070 /* If enumeration failed for random reasons, we will probably delete
3071 * too much stuff, so we guard against this instead.
3073 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3076 /* Delete the old directory if it was copied in order to salvage.
3077 * CopyOnWrite has written the new inode # to the disk, but we still
3078 * have the old one in our local structure here. Thus, we idec the
3082 if (dir.copied && !Testing) {
3083 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3085 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3088 /* Remember rootdir DirSummary _after_ it has been judged */
3089 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3090 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3097 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3099 /* This routine, for now, will only be called for read-write volumes */
3101 int BlocksInVolume = 0, FilesInVolume = 0;
3102 register VnodeClass class;
3103 struct DirSummary rootdir, oldrootdir;
3104 struct VnodeInfo *dirVnodeInfo;
3105 struct VnodeDiskObject vnode;
3106 VolumeDiskData volHeader;
3108 int orphaned, rootdirfound = 0;
3109 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3110 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3111 struct VnodeEssence *vep;
3116 VnodeId LFVnode, ThisVnode;
3117 Unique LFUnique, ThisUnique;
3120 vid = rwIsp->volSummary->header.id;
3121 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3122 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3123 assert(nBytes == sizeof(volHeader));
3124 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3125 assert (volHeader.destroyMe != DESTROY_ME);
3126 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3128 DistilVnodeEssence(vid, vLarge,
3129 rwIsp->volSummary->header.largeVnodeIndex,
3131 DistilVnodeEssence(vid, vSmall,
3132 rwIsp->volSummary->header.smallVnodeIndex,
3135 dirVnodeInfo = &vnodeInfo[vLarge];
3136 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3137 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3138 &rootdir, &rootdirfound);
3145 /* Parse each vnode looking for orphaned vnodes and
3146 * connect them to the tree as orphaned (if requested).
3148 oldrootdir = rootdir;
3149 for (class=0; class < nVNODECLASSES; class++) {
3150 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3151 vep = &(vnodeInfo[class].vnodes[v]);
3152 ThisVnode = bitNumberToVnodeNumber(v, class);
3153 ThisUnique = vep->unique;
3155 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3156 continue; /* Ignore unused, claimed, and root vnodes */
3158 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3159 * entry in this vnode had incremented the parent link count (In
3160 * JudgeEntry()). We need to go to the parent and decrement that
3161 * link count. But if the parent's unique is zero, then the parent
3162 * link count was not incremented in JudgeEntry().
3164 if (class == vLarge) { /* directory vnode */
3165 pv = vnodeIdToBitNumber(vep->parent);
3166 if (IUnique(vnodeInfo[vLarge].vnodes[pv].unique) != 0)
3167 vnodeInfo[vLarge].vnodes[pv].count++;
3171 continue; /* If no rootdir, can't attach orphaned files */
3173 /* Here we attach orphaned files and directories into the
3174 * root directory, LVVnode, making sure link counts stay correct.
3176 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3177 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3178 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3180 /* Update this orphaned vnode's info. Its parent info and
3181 * link count (do for orphaned directories and files).
3183 vep->parent = LFVnode; /* Parent is the root dir */
3184 vep->unique = LFUnique;
3187 vep->count--; /* Inc link count (root dir will pt to it) */
3189 /* If this orphaned vnode is a directory, change '..'.
3190 * The name of the orphaned dir/file is unknown, so we
3191 * build a unique name. No need to CopyOnWrite the directory
3192 * since it is not connected to tree in BK or RO volume and
3193 * won't be visible there.
3195 if (class == vLarge) {
3199 /* Remove and recreate the ".." entry in this orphaned directory */
3200 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3202 pa.Unique = LFUnique;
3203 assert(Delete(&dh, "..") == 0);
3204 assert(Create(&dh, "..", &pa) == 0);
3206 /* The original parent's link count was decremented above.
3207 * Here we increment the new parent's link count.
3209 pv = vnodeIdToBitNumber(LFVnode);
3210 vnodeInfo[vLarge].vnodes[pv].count--;
3214 /* Go to the root dir and add this entry. The link count of the
3215 * root dir was incremented when ".." was created. Try 10 times.
3217 for (j=0; j<10; j++) {
3218 pa.Vnode = ThisVnode;
3219 pa.Unique = ThisUnique;
3221 sprintf(npath, "%s.%d.%d",
3222 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3223 ThisVnode, ThisUnique);
3225 CopyOnWrite(&rootdir);
3226 code = Create(&rootdir.dirHandle, npath, &pa);
3229 ThisUnique += 50; /* Try creating a different file */
3232 Log("Attaching orphaned %s to volume's root dir as %s\n",
3233 ((class == vLarge)?"directory":"file"), npath);
3235 } /* for each vnode in the class */
3236 } /* for each class of vnode */
3238 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3240 if (!oldrootdir.copied && rootdir.copied) {
3241 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3243 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3246 DFlush(); /* Flush the changes */
3247 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3248 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3249 orphans = ORPH_IGNORE;
3252 /* Write out all changed vnodes. Orphaned files and directories
3253 * will get removed here also (if requested).
3255 for (class = 0; class < nVNODECLASSES; class++){
3256 int nVnodes = vnodeInfo[class].nVnodes;
3257 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3258 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3259 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3260 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3261 for (i = 0; i<nVnodes; i++) {
3262 register struct VnodeEssence *vnp = &vnodes[i];
3263 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3265 /* If the vnode is good but is unclaimed (not listed in
3266 * any directory entries), then it is orphaned.
3269 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3270 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3274 if (vnp->changed || vnp->count) {
3277 nBytes = IH_IREAD(vnodeInfo[class].handle,
3278 vnodeIndexOffset(vcp, vnodeNumber),
3279 (char*)&vnode, sizeof (vnode));
3280 assert(nBytes == sizeof(vnode));
3282 vnode.parent = vnp->parent;
3283 oldCount = vnode.linkCount;
3284 vnode.linkCount = vnode.linkCount - vnp->count;
3287 orphaned = IsVnodeOrphaned(vnodeNumber);
3289 if (!vnp->todelete) {
3290 /* Orphans should have already been attached (if requested) */
3291 assert(orphans != ORPH_ATTACH);
3292 oblocks += vnp->blockCount;
3295 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3296 BlocksInVolume -= vnp->blockCount;
3298 if (VNDISK_GET_INO(&vnode)) {
3299 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3302 memset(&vnode, 0, sizeof(vnode));
3304 } else if (vnp->count) {
3306 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3307 vnodeNumber, oldCount,
3308 (Testing?"would have changed to":"now"), vnode.linkCount);
3312 vnode.dataVersion++;
3314 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3315 vnodeIndexOffset(vcp, vnodeNumber),
3316 (char*)&vnode, sizeof (vnode));
3317 assert(nBytes == sizeof(vnode));
3323 if (!Showmode && ofiles) {
3324 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3325 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3329 for (class = 0; class < nVNODECLASSES; class++) {
3330 register struct VnodeInfo *vip = &vnodeInfo[class];
3331 for (i=0; i<vip->nVnodes; i++)
3332 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3333 if (vip->vnodes) free(vip->vnodes);
3334 if (vip->inodes) free(vip->inodes);
3337 /* Set correct resource utilization statistics */
3338 volHeader.filecount = FilesInVolume;
3339 volHeader.diskused = BlocksInVolume;
3341 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3342 if (volHeader.uniquifier < (maxunique + 1)) {
3343 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3344 /* Plus 2,000 in case there are workstations out there with
3345 * cached vnodes that have since been deleted
3347 volHeader.uniquifier = (maxunique + 1 + 2000);
3350 /* Turn off the inUse bit; the volume's been salvaged! */
3351 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3352 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3353 volHeader.inService = 1; /* allow service again */
3354 volHeader.needsCallback = (VolumeChanged != 0);
3355 volHeader.dontSalvage = DONT_SALVAGE;
3358 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3359 assert(nBytes == sizeof(volHeader));
3362 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3363 (Testing?"It would have ":""), volHeader.name,
3364 volHeader.id, FilesInVolume, BlocksInVolume);
3366 IH_RELEASE(vnodeInfo[vSmall].handle);
3367 IH_RELEASE(vnodeInfo[vLarge].handle);
3372 void ClearROInUseBit(struct VolumeSummary *summary)
3374 IHandle_t *h = summary->volumeInfoHandle;
3377 VolumeDiskData volHeader;
3379 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3380 assert(nBytes == sizeof(volHeader));
3381 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC)
3382 volHeader.inUse = 0;
3383 volHeader.needsSalvaged = 0;
3384 volHeader.inService = 1;
3385 volHeader.dontSalvage = DONT_SALVAGE;
3387 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3388 assert(nBytes == sizeof(volHeader));
3393 * Possible delete the volume.
3395 * deleteMe - Always do so, only a partial volume.
3397 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3401 if (readOnly(isp) || deleteMe) {
3402 if (isp->volSummary && isp->volSummary->fileName) {
3404 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);
3405 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3407 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3408 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3411 unlink(isp->volSummary->fileName);
3415 Log("%s salvage was unsuccessful: read-write volume %u\n",
3416 message, isp->volumeId);
3417 Abort("Salvage of volume %u aborted\n",
3423 void AskOffline(VolumeId volumeId)
3425 if (FSYNC_askfs(volumeId, (char *)0, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3426 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3427 Abort("Salvage aborted\n");
3431 void AskOnline(VolumeId volumeId, char *partition)
3433 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3434 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3435 volumeId, partition);
3439 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3441 /* Volume parameter is passed in case iopen is upgraded in future to
3442 * require a volume Id to be passed
3445 IHandle_t *srcH, *destH;
3446 FdHandle_t *srcFdP, *destFdP;
3449 IH_INIT(srcH, device, rwvolume, inode1);
3450 srcFdP = IH_OPEN(srcH);
3451 assert(srcFdP != NULL);
3452 IH_INIT(destH, device, rwvolume, inode2);
3453 destFdP = IH_OPEN(destH);
3455 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3456 assert(FDH_WRITE(destFdP, buf, n) == n);
3458 FDH_REALLYCLOSE(srcFdP);
3459 FDH_REALLYCLOSE(destFdP);
3465 void PrintInodeList(void)
3467 register struct ViceInodeInfo *ip;
3468 struct ViceInodeInfo *buf;
3472 assert(fstat(inodeFd, &status) == 0);
3473 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3474 assert(buf != NULL);
3475 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3476 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3477 for(ip = buf; nInodes--; ip++) {
3478 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3479 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3480 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3485 void PrintInodeSummary(void)
3488 struct InodeSummary *isp;
3490 for (i=0; i<nVolumesInInodeFile; i++) {
3491 isp = &inodeSummary[i];
3492 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3493 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3494 isp->nSpecialInodes, isp->maxUniquifier);
3498 void PrintVolumeSummary(void)
3501 struct VolumeSummary *vsp;
3503 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3504 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3512 assert(0); /* Fork is never executed in the NT code path */
3523 if (ShowLog) showlog();
3525 if (main_thread != pthread_self())
3526 pthread_exit((void*)code);
3534 int Wait(char *prog)
3538 pid = wait(&status);
3540 if (WCOREDUMP(status))
3541 Log("\"%s\" core dumped!\n", prog);
3542 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3547 static char *TimeStamp(time_t clock, int precision)
3550 static char timestamp[20];
3551 lt = localtime(&clock);
3553 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3555 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3559 void CheckLogFile(void)
3561 char oldSlvgLog[AFSDIR_PATH_MAX];
3563 #ifndef AFS_NT40_ENV
3570 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3571 strcat(oldSlvgLog, ".old");
3573 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3574 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3576 if (!logFile) { /* still nothing, use stdout */
3581 #ifndef AFS_NAMEI_ENV
3582 AFS_DEBUG_IOPS_LOG(logFile);
3591 #ifndef AFS_NT40_ENV
3593 printf("Can't show log since using syslog.\n");
3602 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3605 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3608 while (fgets(line, sizeof(line), logFile))
3614 void Log(a,b,c,d,e,f,g,h,i,j,k)
3615 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3619 #ifndef AFS_NT40_ENV
3622 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3626 gettimeofday(&now, 0);
3627 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3628 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3633 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3634 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3636 #ifndef AFS_NT40_ENV
3639 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3643 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3645 if (ShowLog) showlog();
3652 char * ToString(char *s)
3655 p = (char *) malloc(strlen(s)+1);
3662 /* Remove the FORCESALVAGE file */
3663 void RemoveTheForce(char *path)
3665 if (!Testing && ForceSalvage) {
3666 if (chdir(path) == 0)
3667 unlink("FORCESALVAGE");
3671 #ifndef AFS_AIX32_ENV
3673 * UseTheForceLuke - see if we can use the force
3675 int UseTheForceLuke(char *path)
3679 assert(chdir(path) != -1);
3681 return (stat("FORCESALVAGE", &force) == 0);
3685 * UseTheForceLuke - see if we can use the force
3688 * The VRMIX fsck will not muck with the filesystem it is supposedly
3689 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3690 * muck directly with the root inode, which is within the normal
3692 * ListViceInodes() has a side effect of setting ForceSalvage if
3693 * it detects a need, based on root inode examination.
3695 int UseTheForceLuke(char *path)
3698 return 0; /* sorry OB1 */
3703 /* NT support routines */
3705 static char execpathname[MAX_PATH];
3706 int nt_SalvagePartition(char *partName, int jobn)
3711 if (!*execpathname) {
3712 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3713 if (!n || n == 1023)
3716 job.cj_magic = SALVAGER_MAGIC;
3717 job.cj_number = jobn;
3718 (void) strcpy(job.cj_part, partName);
3719 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3724 int nt_SetupPartitionSalvage(void *datap, int len)
3726 childJob_t *jobp = (childJob_t*)datap;
3727 char logname[AFSDIR_PATH_MAX];
3729 if (len != sizeof(childJob_t))
3731 if (jobp->cj_magic != SALVAGER_MAGIC)
3736 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3738 logFile = fopen(logname, "w");
3739 if (!logFile) logFile = stdout;
3745 #endif /* AFS_NT40_ENV */