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
14 * Module: vol-salvage.c
15 * Institution: The Information Technology Center, Carnegie-Mellon University
19 Correct handling of bad "." and ".." entries.
20 Message if volume has "destroyMe" flag set--but doesn't delete yet.
21 Link count bug fixed--bug was that vnodeEssence link count was unsigned
22 14 bits. Needs to be signed.
25 Change to DirHandle stuff to make sure that cache entries are reused at the
26 right time (this parallels the file server change, but is not identical).
28 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
31 Fixed bug which was causing inode link counts to go bad (thus leaking
33 Vnodes with 0 inode pointers in RW volumes are now deleted.
34 An inode with a matching inode number to the vnode is preferred to an
35 inode with a higer data version.
36 Bug is probably fixed that was causing data version to remain wrong,
37 despite assurances from the salvager to the contrary.
40 Added limited salvaging: unless ForceSalvage is on, then the volume will
41 not be salvaged if the dontSalvage flag is set in the Volume Header.
42 The ForceSalvage flag is turned on if an individual volume is salvaged or
43 if the file FORCESALVAGE exists in the partition header of the file system
44 being salvaged. This isn't used for anything but could be set by vfsck.
45 A -f flag was also added to force salvage.
48 It now deletes obsolete volume inodes without complaining
51 Repairs rw volume headers (again).
54 Correlates volume headers & inodes correctly, thus preventing occasional deletion
55 of read-only volumes...
56 No longer forces a directory salvage for volume 144 (which may be a good volume
58 Some of the messages are cleaned up or made more explicit. One or two added.
60 A bug was fixed which forced salvage of read-only volumes without a corresponding
64 When a volume header is recreated, the new name will be "bogus.volume#"
67 Directory salvaging turned on!!!
70 Prints warning messages for setuid programs.
73 Logs missing inode numbers.
76 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.
79 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
80 Time stamps on log entries.
81 Fcntl on stdout to cause all entries to be appended.
82 Problems writing to temporary files are now all detected.
83 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
84 Some cleanup of error messages.
88 #define SalvageVersion "2.4"
90 /* Main program file. Define globals. */
93 #include <afs/param.h>
103 #include <WINNT/afsevent.h>
105 #include <sys/param.h>
106 #include <sys/file.h>
108 #include <sys/time.h>
109 #endif /* ITIMER_REAL */
111 #if defined(AFS_AIX_ENV)
112 #define WCOREDUMP(x) (x & 0200)
115 #include <afs/afsint.h>
116 #include <afs/assert.h>
117 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
118 #if defined(AFS_VFSINCL_ENV)
119 #include <sys/vnode.h>
121 #include <sys/fs/ufs_inode.h>
123 #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
124 #include <ufs/ufs/dinode.h>
125 #include <ufs/ffs/fs.h>
127 #include <ufs/inode.h>
130 #else /* AFS_VFSINCL_ENV */
132 #include <ufs/inode.h>
133 #else /* AFS_OSF_ENV */
134 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_FBSD_ENV)
135 #include <sys/inode.h>
138 #endif /* AFS_VFSINCL_ENV */
139 #endif /* AFS_SGI_ENV */
142 #include <sys/lockf.h>
146 #include <checklist.h>
148 #if defined(AFS_SGI_ENV)
153 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
156 #include <sys/mnttab.h>
157 #include <sys/mntent.h>
162 #endif /* AFS_SGI_ENV */
163 #endif /* AFS_HPUX_ENV */
168 #include <afs/osi_inode.h>
171 #include <afs/afsutil.h>
172 #include <afs/fileutil.h>
173 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
181 #include <afs/afssyscalls.h>
185 #include "partition.h"
187 #include "viceinode.h"
189 #include "volinodes.h" /* header magic number, etc. stuff */
195 extern void *calloc();
197 extern char *vol_DevName();
198 static char *TimeStamp(time_t clock, int precision);
200 #define ORPH_IGNORE 0
201 #define ORPH_REMOVE 1
202 #define ORPH_ATTACH 2
205 int debug; /* -d flag */
206 int Testing=0; /* -n flag */
207 int ListInodeOption; /* -i flag */
208 int ShowRootFiles; /* -r flag */
209 int RebuildDirs; /* -sal flag */
210 int Parallel = 4; /* -para X flag */
211 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
212 int forceR = 0; /* -b flag */
213 int ShowLog = 0; /* -showlog flag */
214 int ShowSuid = 0; /* -showsuid flag */
215 int ShowMounts = 0; /* -showmounts flag */
216 int orphans = ORPH_IGNORE; /* -orphans option */
220 int useSyslog = 0; /* -syslog flag */
221 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
224 #define MAXPARALLEL 32
226 int OKToZap; /* -o flag */
227 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
228 in the volume header */
230 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
232 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
234 Device fileSysDevice; /* The device number of the current
235 partition being salvaged */
239 char *fileSysPath; /* The path of the mounted partition currently
240 being salvaged, i.e. the directory
241 containing the volume headers */
243 char *fileSysPathName; /* NT needs this to make name pretty in log. */
244 IHandle_t *VGLinkH; /* Link handle for current volume group. */
245 int VGLinkH_cnt; /* # of references to lnk handle. */
246 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
248 char *fileSysDeviceName; /* The block device where the file system
249 being salvaged was mounted */
250 char *filesysfulldev;
252 int VolumeChanged; /* Set by any routine which would change the volume in
253 a way which would require callback is to be broken if the
254 volume was put back on line by an active file server */
256 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
258 struct InodeSummary { /* Inode summary file--an entry for each
259 volume in the inode file for a partition */
260 VolId volumeId; /* Volume id */
261 VolId RWvolumeId; /* RW volume associated */
262 int index; /* index into inode file (0, 1, 2 ...) */
263 int nInodes; /* Number of inodes for this volume */
264 int nSpecialInodes; /* Number of special inodes, i.e. volume
265 header, index, etc. These are all
266 marked (viceinode.h) and will all be sorted
267 to the beginning of the information for
268 this volume. Read-only volumes should
269 ONLY have special inodes (all the other
270 inodes look as if they belong to the
271 original RW volume). */
272 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
273 This is only useful for RW volumes and is used
274 to compute a new volume uniquifier in the event
275 that the header needs to be recreated. The inode
276 uniquifier may be a truncated version of vnode
277 uniquifier (AFS_3DISPARES). The real maxUniquifer
278 is from the vnodes and later calcuated from it */
279 struct VolumeSummary *volSummary;
280 /* Either a pointer to the original volume
281 header summary, or constructed summary
284 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
285 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
286 int inodeFd; /* File descriptor for inode file */
289 struct VolumeSummary { /* Volume summary an entry for each
290 volume in a volume directory.
291 Assumption: one volume directory per
293 char *fileName; /* File name on the partition for the volume
295 struct VolumeHeader header;
296 /* volume number, rw volume number, inode
297 numbers of each major component of
299 IHandle_t *volumeInfoHandle;
300 byte wouldNeedCallback; /* set if the file server should issue
301 call backs for all the files in this volume when
302 the volume goes back on line */
306 IHandle_t *handle; /* Inode containing this index */
307 int nVnodes; /* Total number of vnodes in index */
308 int nAllocatedVnodes; /* Total number actually used */
309 int volumeBlockCount; /* Total number of blocks used by volume */
310 Inode *inodes; /* Directory only */
311 struct VnodeEssence {
312 short count; /* Number of references to vnode; MUST BE SIGNED */
313 unsigned claimed:1; /* Set when a parent directory containing an entry
314 referencing this vnode is found. The claim
315 is that the parent in "parent" can point to
316 this vnode, and no other */
317 unsigned changed:1; /* Set if any parameters (other than the count)
318 in the vnode change. It is determined if the
319 link count has changed by noting whether it is
320 0 after scanning all directories */
321 unsigned salvaged:1;/* Set if this directory vnode has already been salvaged. */
322 unsigned todelete:1;/* Set if this vnode is to be deleted (should not be claimed) */
323 afs_uint32 blockCount;
324 /* Number of blocks (1K) used by this vnode,
326 VnodeId parent; /* parent in vnode */
327 Unique unique; /* Must match entry! */
328 char *name; /* Name of directory entry */
329 int modeBits; /* File mode bits */
330 Inode InodeNumber; /* file's inode */
331 int type; /* File type */
332 int author; /* File author */
333 int owner; /* File owner */
334 int group; /* File group */
336 } vnodeInfo[nVNODECLASSES];
339 struct DirHandle dirHandle;
342 unsigned haveDot, haveDotDot;
344 int copied; /* If the copy-on-write stuff has been applied */
352 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
353 int nVolumes; /* Number of volumes (read-write and read-only)
357 /* For NT, we can fork the per partition salvagers to gain the required
358 * safety against Aborts. But there's too many complex data structures at
359 * the per volume salvager layer to easilty copy the data across.
360 * childJobNumber is resset from -1 to the job number if this is a
361 * per partition child of the main salvager. This information is passed
362 * out-of-band in the extra data area setup for the now unused parent/child
365 #define SALVAGER_MAGIC 0x00BBaaDD
366 #define NOT_CHILD -1 /* job numbers start at 0 */
367 /* If new options need to be passed to child, add them here. */
374 /* Child job this process is running. */
375 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD};
377 int nt_SalvagePartition(char *partName, int jobn);
378 int nt_SetupPartitionSalvage(void *datap, int len);
381 struct InodeSummary *svgp_inodeSummaryp;
391 /* Forward declarations */
392 void Log(), Abort(), Exit();
394 int Wait(char *prog);
395 char * ToString(char *s);
396 void AskOffline(VolumeId volumeId);
397 void AskOnline(VolumeId volumeId, char *partition);
398 void CheckLogFile(void);
399 void ClearROInUseBit(struct VolumeSummary *summary);
400 void CopyAndSalvage(register struct DirSummary *dir);
401 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
402 void CopyOnWrite(register struct DirSummary *dir);
403 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
404 register struct InodeSummary * summary);
405 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
406 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
408 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
409 void GetVolumeSummary(VolumeId singleVolumeNumber);
410 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
412 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
415 void ObtainSalvageLock(void);
416 void PrintInodeList(void);
417 void PrintInodeSummary(void);
418 void PrintVolumeSummary(void);
419 int QuickCheck(register struct InodeSummary *isp, int nVols);
420 void RemoveTheForce(char *path);
421 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
422 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
424 void SalvageFileSysParallel(struct DiskPartition *partP);
425 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
426 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
427 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
428 int check, int *deleteMe);
429 int SalvageIndex(Inode ino, VnodeClass class, int RW,
430 register struct ViceInodeInfo *ip,
431 int nInodes, struct VolumeSummary *volSummary, int check);
432 int SalvageVnodes(register struct InodeSummary *rwIsp,
433 register struct InodeSummary * thisIsp,
434 register struct ViceInodeInfo * inodes, int check);
435 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
436 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
438 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
440 #define SalvageVolumeGroup DoSalvageVolumeGroup
442 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
443 register struct ViceInodeInfo *inodes,
444 int RW, int check, int *deleteMe);
446 int UseTheForceLuke(char *path);
448 static int IsVnodeOrphaned(VnodeId vnode);
450 /* Uniquifier stored in the Inode */
451 static Unique IUnique(u)
455 return(u & 0x3fffff);
457 #if defined(AFS_SGI_EXMAG)
458 return(u & SGI_UNIQMASK);
461 #endif /* AFS_SGI_EXMAG */
465 static int BadError(aerror)
466 register int aerror; {
467 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
469 return 0; /* otherwise may be transient, e.g. EMFILE */
475 struct cmd_syndesc *as;
477 register struct cmd_item *ti;
478 char pname[100], *temp;
479 afs_int32 seenpart = 0, seenvol = 0, vid = 0;
480 struct DiskPartition *partP;
482 #ifdef AFS_SGI_VNODE_GLUE
483 if (afs_init_kernel_config(-1) <0) {
484 printf("Can't determine NUMA configuration, not starting salvager.\n");
489 if (ti = as->parms[0].items) { /* -partition */
491 strncpy(pname, ti->data, 100);
493 if (ti = as->parms[1].items) { /* -volumeid */
495 printf("You must also specify '-partition' option with the '-volumeid' option\n");
499 vid = atoi(ti->data);
501 if (as->parms[2].items) /* -debug */
503 if (as->parms[3].items) /* -nowrite */
505 if (as->parms[4].items) /* -inodes */
507 if (as->parms[5].items) /* -force */
509 if (as->parms[6].items) /* -oktozap */
511 if (as->parms[7].items) /* -rootinodes */
513 if (as->parms[8].items) /* -RebuildDirs */
515 if (as->parms[9].items) /* -ForceReads */
517 if (ti = as->parms[10].items) {/* -Parallel # */
519 if (strncmp(temp,"all",3) == 0) {
523 if (strlen(temp) != 0) {
524 Parallel = atoi(temp);
525 if (Parallel < 1) Parallel = 1;
526 if (Parallel > MAXPARALLEL) {
527 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
528 Parallel = MAXPARALLEL;
532 if (ti = as->parms[11].items) {/* -tmpdir */
536 dirp = opendir(tmpdir);
538 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
543 if (ti = as->parms[12].items) /* -showlog */
545 if (ti = as->parms[13].items) { /* -log */
550 if (ti = as->parms[14].items) { /* -showmounts */
555 if (ti = as->parms[15].items) { /* -orphans */
557 orphans = ORPH_IGNORE;
558 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
559 orphans = ORPH_REMOVE;
560 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
561 orphans = ORPH_ATTACH;
564 #ifndef AFS_NT40_ENV /* ignore options on NT */
565 if ( ti = as->parms[16].items) { /* -syslog */
569 if ( ti = as->parms[17].items) { /* -syslogfacility */
570 useSyslogFacility = atoi(ti->data);
576 if (ti = as->parms[18].items) { /* -DontSalvage */
577 printf("Exiting immediately without salvage. Look into the FileLog");
578 printf(" to find volumes which really need to be salvaged!\n");
581 #endif /* FAST_RESTART */
583 /* Note: if seemvol we initialize this as a standard volume utility: this has the
584 implication that the file server may be running; negotations have to be made with
585 the file server in this case to take the read write volume and associated read-only
586 volumes off line before salvaging */
589 if (afs_winsockInit()<0) {
590 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
591 AFSDIR_SALVAGER_FILE, 0);
592 Log("Failed to initailize winsock, exiting.\n");
597 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
600 if (myjob.cj_number != NOT_CHILD) {
603 (void) strcpy(pname, myjob.cj_part);
608 for (partP = DiskPartitionList; partP; partP = partP->next) {
609 SalvageFileSysParallel(partP);
611 SalvageFileSysParallel(0);
614 partP = VGetPartition(pname, 0);
616 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
621 SalvageFileSys(partP, 0);
623 /* Salvage individual volume */
625 Log("salvage: invalid volume id specified; salvage aborted\n");
628 SalvageFileSys (partP, vid);
636 #include "AFS_component_version_number.c"
640 char *save_args[MAX_ARGS];
642 pthread_t main_thread;
648 struct cmd_syndesc *ts;
650 char commandLine[150];
653 extern char cml_version_number[];
657 * The following signal action for AIX is necessary so that in case of a
658 * crash (i.e. core is generated) we can include the user's data section
659 * in the core dump. Unfortunately, by default, only a partial core is
660 * generated which, in many cases, isn't too useful.
662 struct sigaction nsa;
664 sigemptyset(&nsa.sa_mask);
665 nsa.sa_handler = SIG_DFL;
666 nsa.sa_flags = SA_FULLDUMP;
667 sigaction(SIGABRT, &nsa, NULL);
668 sigaction(SIGSEGV, &nsa, NULL);
671 /* Initialize directory paths */
672 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
674 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
676 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
680 main_thread = pthread_self();
681 if (spawnDatap && spawnDataLen) {
682 /* This is a child per partition salvager. Don't setup log or
683 * try to lock the salvager lock.
685 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
690 for (commandLine[0] = '\0', i=0; i<argc; i++) {
691 if (i > 0) strcat(commandLine, " ");
692 strcat(commandLine, argv[i]);
695 /* All entries to the log will be appended. Useful if there are
696 * multiple salvagers appending to the log.
701 #ifdef AFS_LINUX20_ENV
702 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
704 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
710 if (geteuid() != 0) {
711 printf("Salvager must be run as root.\n");
717 /* bad for normal help flag processing, but can do nada */
719 fprintf(logFile, "%s\n", cml_version_number);
720 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
722 /* Get and hold a lock for the duration of the salvage to make sure
723 * that no other salvage runs at the same time. The routine
724 * VInitVolumePackage (called below) makes sure that a file server or
725 * other volume utilities don't interfere with the salvage.
732 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
733 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
734 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
735 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
736 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
737 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
738 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
739 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
740 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
741 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
742 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
743 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
744 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
745 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
746 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
747 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
748 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
750 /* note - syslog isn't avail on NT, but if we make it conditional, have
751 to deal with screwy offsets for cmd params */
752 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
753 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
756 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");
757 #endif /* FAST_RESTART */
758 err = cmd_Dispatch(argc, argv);
762 /* Get the salvage lock if not already held. Hold until process exits. */
763 void ObtainSalvageLock(void)
768 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
769 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
771 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
773 "salvager: There appears to be another salvager running! Aborted.\n");
777 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
778 assert(salvageLock >= 0);
779 #ifdef AFS_DARWIN_ENV
780 if (flock(salvageLock, LOCK_EX) == -1) {
782 if (lockf(salvageLock, F_LOCK, 0) == -1) {
785 "salvager: There appears to be another salvager running! Aborted.\n");
792 #ifdef AFS_SGI_XFS_IOPS_ENV
793 /* Check if the given partition is mounted. For XFS, the root inode is not a
794 * constant. So we check the hard way.
796 int IsPartitionMounted(char *part)
799 struct mntent *mntent;
801 assert(mntfp = setmntent(MOUNTED, "r"));
802 while (mntent = getmntent(mntfp)) {
803 if (!strcmp(part, mntent->mnt_dir))
808 return mntent ? 1 : 1;
811 /* Check if the given inode is the root of the filesystem. */
812 #ifndef AFS_SGI_XFS_IOPS_ENV
813 int IsRootInode(status)
816 /* The root inode is not a fixed value in XFS partitions. So we need to see if
817 * the partition is in the list of mounted partitions. This only affects the
818 * SalvageFileSys path, so we check there.
820 return (status->st_ino == ROOTINODE);
825 /* We don't want to salvage big files filesystems, since we can't put volumes on
828 int CheckIfBigFilesFS(mountPoint, devName)
832 struct superblock fs;
835 if (strncmp(devName, "/dev/", 5)) {
836 (void) sprintf(name, "/dev/%s", devName);
839 (void) strcpy(name, devName);
842 if (ReadSuper(&fs, name)<0) {
843 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
846 if (IsBigFilesFileSystem(&fs)) {
847 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
855 #define HDSTR "\\Device\\Harddisk"
856 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
857 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
864 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
866 if (strncmp(res, HDSTR, HDLEN)) {
869 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
870 res, HDSTR, p1->devName);
874 d1 = atoi(&res[HDLEN]);
876 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
878 if (strncmp(res, HDSTR, HDLEN)) {
881 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
882 res, HDSTR, p2->devName);
886 d2 = atoi(&res[HDLEN]);
891 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
894 /* This assumes that two partitions with the same device number divided by
895 * PartsPerDisk are on the same disk.
897 void SalvageFileSysParallel(struct DiskPartition *partP)
900 struct DiskPartition *partP;
901 int pid; /* Pid for this job */
902 int jobnumb; /* Log file job number */
903 struct job *nextjob; /* Next partition on disk to salvage */
905 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
906 struct job *thisjob = 0;
907 static int numjobs = 0;
908 static int jobcount = 0;
914 char logFileName[256];
918 /* We have a partition to salvage. Copy it into thisjob */
919 thisjob = (struct job *) malloc(sizeof(struct job));
921 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
924 bzero(thisjob, sizeof(struct job));
925 thisjob->partP = partP;
926 thisjob->jobnumb = jobcount;
929 else if (jobcount == 0) {
930 /* We are asking to wait for all jobs (partp == 0), yet we never
933 Log("No file system partitions named %s* found; not salvaged\n",
934 VICE_PARTITION_PREFIX);
938 if (debug || Parallel == 1) {
940 SalvageFileSys(thisjob->partP, 0);
947 /* Check to see if thisjob is for a disk that we are already
948 * salvaging. If it is, link it in as the next job to do. The
949 * jobs array has 1 entry per disk being salvages. numjobs is
950 * the total number of disks currently being salvaged. In
951 * order to keep thejobs array compact, when a disk is
952 * completed, the hightest element in the jobs array is moved
953 * down to now open slot.
955 for (j=0; j<numjobs; j++) {
956 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
957 /* On same disk, add it to this list and return */
958 thisjob->nextjob = jobs[j]->nextjob;
959 jobs[j]->nextjob = thisjob;
966 /* Loop until we start thisjob or until all existing jobs are finished */
967 while ( thisjob || (!partP && (numjobs > 0)) ) {
968 startjob = -1; /* No new job to start */
970 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
971 /* Either the max jobs are running or we have to wait for all
972 * the jobs to finish. In either case, we wait for at least one
973 * job to finish. When it's done, clean up after it.
975 pid = wait(&wstatus);
977 for (j=0; j<numjobs; j++) { /* Find which job it is */
978 if (pid == jobs[j]->pid) break;
981 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
982 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
985 numjobs--; /* job no longer running */
986 oldjob = jobs[j]; /* remember */
987 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
988 free(oldjob); /* free the old job */
990 /* If there is another partition on the disk to salvage, then
991 * say we will start it (startjob). If not, then put thisjob there
992 * and say we will start it.
994 if (jobs[j]) { /* Another partitions to salvage */
995 startjob = j; /* Will start it */
996 } else { /* There is not another partition to salvage */
998 jobs[j] = thisjob; /* Add thisjob */
1000 startjob = j; /* Will start it */
1002 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1003 startjob = -1; /* Don't start it - already running */
1007 /* We don't have to wait for a job to complete */
1009 jobs[numjobs] = thisjob; /* Add this job */
1011 startjob = numjobs; /* Will start it */
1015 /* Start up a new salvage job on a partition in job slot "startjob" */
1016 if (startjob != -1) {
1018 Log("Starting salvage of file system partition %s\n",
1019 jobs[startjob]->partP->name);
1021 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1022 * commands and pass the child job number via the data path.
1024 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1025 jobs[startjob]->jobnumb);
1026 jobs[startjob]->pid = pid;
1031 jobs[startjob]->pid = pid;
1037 for (fd =0; fd < 16; fd++) close(fd);
1038 open("/", 0); dup2(0, 1); dup2(0, 2);
1039 #ifndef AFS_NT40_ENV
1041 openlog(NULL, LOG_PID, useSyslogFacility);
1045 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1046 logFile = fopen(logFileName, "w");
1048 if (!logFile) logFile = stdout;
1050 SalvageFileSys1(jobs[startjob]->partP, 0);
1055 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1057 /* If waited for all jobs to complete, now collect log files and return */
1058 #ifndef AFS_NT40_ENV
1059 if ( ! useSyslog ) /* if syslogging - no need to collect */
1062 for (i=0; i<jobcount; i++) {
1063 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1064 if (passLog = fopen(logFileName, "r")) {
1065 while(fgets(buf, sizeof(buf), passLog)) {
1066 fputs(buf, logFile);
1070 (void)unlink(logFileName);
1078 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1080 if (!canfork || debug || Fork() == 0) {
1081 SalvageFileSys1(partP, singleVolumeNumber);
1082 if (canfork && !debug) {
1088 Wait("SalvageFileSys");
1091 char *get_DevName(pbuffer, wpath)
1092 char *wpath, *pbuffer;
1094 char pbuf[128], *ptr;
1095 strcpy(pbuf, pbuffer);
1096 ptr = (char *)strrchr(pbuf, '/');
1099 strcpy(wpath, pbuf);
1102 ptr = (char *)strrchr(pbuffer, '/');
1104 strcpy(pbuffer, ptr+1);
1110 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1113 char inodeListPath[50];
1114 static char tmpDevName[100];
1115 static char wpath[100];
1116 struct VolumeSummary *vsp, *esp;
1119 fileSysPartition = partP;
1120 fileSysDevice = fileSysPartition->device;
1121 fileSysPathName = VPartitionPath(fileSysPartition);
1124 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1125 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1126 name = partP->devName;
1128 fileSysPath = fileSysPathName;
1129 strcpy(tmpDevName, partP->devName);
1130 name = get_DevName(tmpDevName, wpath);
1131 fileSysDeviceName = name;
1132 filesysfulldev = wpath;
1135 VLockPartition(partP->name);
1136 if (singleVolumeNumber || ForceSalvage)
1139 ForceSalvage = UseTheForceLuke(fileSysPath);
1141 if (singleVolumeNumber) {
1142 if (!VConnectFS()) {
1143 Abort("Couldn't connect to file server\n");
1145 AskOffline(singleVolumeNumber);
1148 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1150 Log("***Forced salvage of all volumes on this partition***\n");
1155 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1162 assert((dirp = opendir(fileSysPath)) != NULL);
1163 while (dp = readdir(dirp)) {
1164 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1165 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1167 Log("Removing old salvager temp files %s\n", dp->d_name);
1168 strcpy(npath, fileSysPath);
1170 strcat(npath, dp->d_name);
1176 tdir = (tmpdir ? tmpdir : fileSysPath);
1178 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1179 (void) strcpy(inodeListPath, _tempnam(tdir, "salvage.inodes."));
1181 sprintf(inodeListPath, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1183 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1184 unlink(inodeListPath);
1188 /* Using nt_unlink here since we're really using the delete on close
1189 * semantics of unlink. In most places in the salvager, we really do
1190 * mean to unlink the file at that point. Those places have been
1191 * modified to actually do that so that the NT crt can be used there.
1193 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1194 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1196 inodeFd = open(inodeListPath, O_RDONLY);
1197 unlink(inodeListPath);
1200 Abort("Temporary file %s is missing...\n",
1202 if (ListInodeOption) {
1206 /* enumerate volumes in the partition.
1207 figure out sets of read-only + rw volumes.
1208 salvage each set, read-only volumes first, then read-write.
1209 Fix up inodes on last volume in set (whether it is read-write
1212 GetVolumeSummary(singleVolumeNumber);
1214 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1215 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1216 for (j=i; j < nVolumesInInodeFile
1217 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1218 VolumeId vid = inodeSummary[j].volumeId;
1219 struct VolumeSummary *tsp;
1220 /* Scan volume list (from partition root directory) looking for the
1221 current rw volume number in the volume list from the inode scan.
1222 If there is one here that is not in the inode volume list,
1224 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1226 DeleteExtraVolumeHeaderFile(vsp);
1228 /* Now match up the volume summary info from the root directory with the
1229 entry in the volume list obtained from scanning inodes */
1230 inodeSummary[j].volSummary = NULL;
1231 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1232 if (tsp->header.id == vid) {
1233 inodeSummary[j].volSummary = tsp;
1239 /* Salvage the group of volumes (several read-only + 1 read/write)
1240 * starting with the current read-only volume we're looking at.
1242 SalvageVolumeGroup(&inodeSummary[i], j-i);
1245 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1246 for ( ; vsp<esp; vsp++) {
1248 DeleteExtraVolumeHeaderFile(vsp);
1251 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1252 RemoveTheForce(fileSysPath);
1254 if (!Testing && singleVolumeNumber) {
1255 AskOnline(singleVolumeNumber, fileSysPartition->name);
1257 /* Step through the volumeSummary list and set all volumes on-line.
1258 * The volumes were taken off-line in GetVolumeSummary.
1260 for (j=0; j<nVolumes; j++) {
1261 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1266 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1267 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1270 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1273 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1275 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1276 vsp->fileName, (Testing? "would have been ":""));
1278 unlink(vsp->fileName);
1282 CompareInodes(_p1,_p2)
1283 const void *_p1,*_p2;
1285 register const struct ViceInodeInfo *p1 = _p1;
1286 register const struct ViceInodeInfo *p2 = _p2;
1287 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1288 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1289 VolumeId p1rwid, p2rwid;
1290 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1291 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1292 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1293 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1294 if (p1rwid < p2rwid)
1296 if (p1rwid > p2rwid)
1298 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1299 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1300 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1301 return (p1->u.special.type < p2->u.special.type? -1: 1);
1302 if (p1->u.vnode.volumeId == p1rwid)
1304 if (p2->u.vnode.volumeId == p2rwid)
1306 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1308 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1309 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1310 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1312 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1314 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1316 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1318 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1320 /* The following tests are reversed, so that the most desirable
1321 of several similar inodes comes first */
1322 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1323 #ifdef AFS_3DISPARES
1324 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1325 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1328 #ifdef AFS_SGI_EXMAG
1329 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1330 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1335 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1336 #ifdef AFS_3DISPARES
1337 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1338 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1341 #ifdef AFS_SGI_EXMAG
1342 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1343 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1348 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1349 #ifdef AFS_3DISPARES
1350 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1351 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1354 #ifdef AFS_SGI_EXMAG
1355 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1356 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1361 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1362 #ifdef AFS_3DISPARES
1363 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1364 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1367 #ifdef AFS_SGI_EXMAG
1368 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1369 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1377 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1378 register struct InodeSummary * summary)
1380 int volume = ip->u.vnode.volumeId;
1381 int rwvolume = volume;
1382 register n, nSpecial;
1383 register Unique maxunique;
1386 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1388 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1390 rwvolume = ip->u.special.parentId;
1391 /* This isn't quite right, as there could (in error) be different
1392 parent inodes in different special vnodes */
1395 if (maxunique < ip->u.vnode.vnodeUniquifier)
1396 maxunique = ip->u.vnode.vnodeUniquifier;
1400 summary->volumeId = volume;
1401 summary->RWvolumeId = rwvolume;
1402 summary->nInodes =n;
1403 summary->nSpecialInodes = nSpecial;
1404 summary->maxUniquifier = maxunique;
1407 int OnlyOneVolume(inodeinfo, singleVolumeNumber)
1408 struct ViceInodeInfo *inodeinfo;
1409 VolumeId singleVolumeNumber;
1411 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1412 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1413 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1418 * Collect list of inodes in file named by path. If a truly fatal error,
1419 * unlink the file and abort. For lessor errors, return -1. The file will
1420 * be unlinked by the caller.
1422 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1425 int summaryFd, forceSal, err;
1426 struct ViceInodeInfo *ip;
1427 struct InodeSummary summary;
1428 char summaryFileName[50];
1431 char *dev = fileSysPath;
1432 char *wpath = fileSysPath;
1434 char *dev = fileSysDeviceName;
1435 char *wpath = filesysfulldev;
1437 char *part = fileSysPath;
1440 /* This file used to come from vfsck; cobble it up ourselves now... */
1441 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1443 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1448 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1450 if (forceSal && !ForceSalvage) {
1451 Log("***Forced salvage of all volumes on this partition***\n");
1454 inodeFd = open(path, O_RDWR);
1455 if (inodeFd == -1 || fstat(inodeFd, &status) == -1) {
1457 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1459 tdir = (tmpdir ? tmpdir : part);
1461 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1462 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1464 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1466 summaryFile = fopen(summaryFileName, "a+");
1467 if (summaryFile == NULL) {
1470 Abort("Unable to create inode summary file\n");
1472 if (!canfork || debug || Fork() == 0) {
1474 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1476 fclose(summaryFile); close(inodeFd);
1477 unlink(summaryFileName);
1478 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1479 RemoveTheForce(fileSysPath);
1480 Log("%s vice inodes on %s; not salvaged\n",
1481 singleVolumeNumber? "No applicable": "No", dev);
1484 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1486 fclose(summaryFile); close(inodeFd);
1488 unlink(summaryFileName);
1489 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1491 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1492 fclose(summaryFile); close(inodeFd);
1494 unlink(summaryFileName);
1495 Abort("Unable to read inode table; %s not salvaged\n", dev);
1497 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1498 if (lseek(inodeFd, 0, SEEK_SET) == -1 ||
1499 write(inodeFd, ip, status.st_size) != status.st_size) {
1500 fclose(summaryFile); close(inodeFd);
1502 unlink(summaryFileName);
1503 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1507 CountVolumeInodes(ip, nInodes, &summary);
1508 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1509 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1510 fclose(summaryFile); close(inodeFd);
1513 summary.index += (summary.nInodes);
1514 nInodes -= summary.nInodes;
1515 ip += summary.nInodes;
1517 /* Following fflush is not fclose, because if it was debug mode would not work */
1518 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1519 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1520 fclose(summaryFile); close(inodeFd);
1523 if (canfork && !debug) {
1529 if (Wait("Inode summary") == -1) {
1530 fclose(summaryFile); close(inodeFd);
1532 unlink(summaryFileName);
1533 Exit(1); /* salvage of this partition aborted */
1536 assert(fstat(fileno(summaryFile), &status) != -1);
1537 if ( status.st_size != 0 ) {
1539 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1540 assert(inodeSummary != NULL);
1541 /* For GNU we need to do lseek to get the file pointer moved. */
1542 assert(lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1543 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1544 assert(ret == status.st_size);
1546 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1547 fclose(summaryFile);
1549 unlink(summaryFileName);
1553 /* Comparison routine for volume sort.
1554 This is setup so that a read-write volume comes immediately before
1555 any read-only clones of that volume */
1556 CompareVolumes(_p1,_p2)
1557 const void *_p1,*_p2;
1559 register const struct VolumeSummary *p1 = _p1;
1560 register const struct VolumeSummary *p2 = _p2;
1561 if (p1->header.parent != p2->header.parent)
1562 return p1->header.parent < p2->header.parent? -1: 1;
1563 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1565 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1567 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1570 void GetVolumeSummary(VolumeId singleVolumeNumber)
1573 afs_int32 nvols = 0;
1574 struct VolumeSummary *vsp, vs;
1575 struct VolumeDiskHeader diskHeader;
1578 /* Get headers from volume directory */
1579 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1580 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1581 if (!singleVolumeNumber) {
1582 while (dp = readdir(dirp)) {
1583 char *p = dp->d_name;
1584 p = strrchr(dp->d_name, '.');
1585 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1587 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1588 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1589 == sizeof (diskHeader) &&
1590 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1591 DiskToVolumeHeader(&vs.header, &diskHeader);
1598 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1602 if (!nvols) nvols = 1;
1603 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1605 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1606 assert(volumeSummaryp != NULL);
1609 vsp = volumeSummaryp;
1610 while (dp = readdir(dirp)) {
1611 char *p = dp->d_name;
1612 p = strrchr(dp->d_name, '.');
1613 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1616 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1617 || read(fd, &diskHeader, sizeof (diskHeader))
1618 != sizeof (diskHeader)
1619 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1624 if (!singleVolumeNumber) {
1625 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1626 dp->d_name, (Testing? "it would have been ":""));
1632 char nameShouldBe[64];
1633 DiskToVolumeHeader(&vsp->header, &diskHeader);
1634 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1635 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1638 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1639 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1640 if (singleVolumeNumber)
1641 AskOffline(vsp->header.id);
1642 if (strcmp(nameShouldBe, dp->d_name)) {
1643 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 ":""));
1648 vsp->fileName = ToString(dp->d_name);
1658 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1661 /* Find the link table. This should be associated with the RW volume or, if
1662 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1664 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1665 struct ViceInodeInfo *allInodes)
1668 struct ViceInodeInfo *ip;
1670 for (i=0; i<nVols; i++) {
1671 ip = allInodes + isp[i].index;
1672 for (j=0; j<isp[i].nSpecialInodes; j++) {
1673 if (ip[j].u.special.type == VI_LINKTABLE)
1674 return ip[j].inodeNumber;
1680 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1682 struct versionStamp version;
1685 if (!VALID_INO(ino))
1686 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1687 isp->volumeId, INODESPECIAL,
1688 VI_LINKTABLE, isp->RWvolumeId);
1689 if (!VALID_INO(ino))
1690 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1691 isp->RWvolumeId, errno);
1692 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1693 fdP = IH_OPEN(VGLinkH);
1695 Abort("Can't open link table for volume %u (error = %d)\n",
1696 isp->RWvolumeId, errno);
1698 if (FDH_TRUNC(fdP, 0)<0)
1699 Abort("Can't truncate link table for volume %u (error = %d)\n",
1700 isp->RWvolumeId, errno);
1702 version.magic = LINKTABLEMAGIC;
1703 version.version = LINKTABLEVERSION;
1705 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1707 Abort("Can't truncate link table for volume %u (error = %d)\n",
1708 isp->RWvolumeId, errno);
1710 FDH_REALLYCLOSE(fdP);
1712 /* If the volume summary exits (i.e., the V*.vol header file exists),
1713 * then set this inode there as well.
1715 if (isp->volSummary)
1716 isp->volSummary->header.linkTable = ino;
1722 void *nt_SVG(void *arg)
1724 SVGParms_t *parms = (SVGParms_t*)arg;
1725 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1729 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1732 pthread_attr_t tattr;
1736 /* Initialize per volume global variables, even if later code does so */
1740 memset(&VolInfo, 0, sizeof(VolInfo));
1742 parms.svgp_inodeSummaryp = isp;
1743 parms.svgp_count = nVols;
1744 code = pthread_attr_init(&tattr);
1746 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1750 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1752 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1756 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1758 Log("Failed to create thread to salvage volume group %u\n",
1762 (void) pthread_join(tid, NULL);
1764 #endif /* AFS_NT40_ENV */
1766 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1768 struct ViceInodeInfo *inodes,*allInodes,*ip;
1769 int i, totalInodes, size, salvageTo;
1773 int dec_VGLinkH = 0;
1775 FdHandle_t *fdP = NULL;
1778 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1779 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1780 if (!ForceSalvage && QuickCheck(isp, nVols))
1783 if (ShowMounts && !haveRWvolume)
1785 if (canfork && !debug && Fork() != 0) {
1786 (void) Wait("Salvage volume group");
1789 for (i = 0, totalInodes = 0; i<nVols; i++)
1790 totalInodes += isp[i].nInodes;
1791 size = totalInodes * sizeof (struct ViceInodeInfo);
1792 inodes = (struct ViceInodeInfo *) malloc(size);
1793 allInodes = inodes - isp->index; /* this would the base of all the inodes
1794 for the partition, if all the inodes
1795 had been read into memory */
1796 assert(lseek(inodeFd,isp->index*sizeof(struct ViceInodeInfo),SEEK_SET) != -1)
1797 assert(read(inodeFd,inodes,size) == size)
1799 /* Don't try to salvage a read write volume if there isn't one on this
1801 salvageTo = haveRWvolume? 0:1;
1803 #ifdef AFS_NAMEI_ENV
1804 ino = FindLinkHandle(isp, nVols, allInodes);
1805 if (VALID_INO(ino)) {
1806 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1807 fdP = IH_OPEN(VGLinkH);
1809 if (!VALID_INO(ino) || fdP == NULL) {
1810 Log("%s link table for volume %u.\n",
1811 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1813 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1816 CreateLinkTable(isp, ino);
1820 FDH_REALLYCLOSE(fdP);
1822 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1825 /* Salvage in reverse order--read/write volume last; this way any
1826 Inodes not referenced by the time we salvage the read/write volume
1827 can be picked up by the read/write volume */
1828 /* ACTUALLY, that's not done right now--the inodes just vanish */
1829 for (i = nVols-1; i>=salvageTo; i--) {
1831 struct InodeSummary *lisp = &isp[i];
1832 #ifdef AFS_NAMEI_ENV
1833 /* If only the RO is present on this partition, the link table
1834 * shows up as a RW volume special file. Need to make sure the
1835 * salvager doesn't try to salvage the non-existent RW.
1837 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1838 /* If this only special inode is the link table, continue */
1839 if (inodes->u.special.type == VI_LINKTABLE) {
1845 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1846 lisp->volumeId, (Testing?"(READONLY mode)":""));
1847 /* Check inodes twice. The second time do things seriously. This
1848 way the whole RO volume can be deleted, below, if anything goes wrong */
1849 for (check = 1; check>=0; check--) {
1851 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1852 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1853 if (rw && deleteMe) {
1854 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1855 volume won't be called */
1861 if (rw && check == 1)
1863 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1864 MaybeZapVolume(lisp,"Vnode index", 0, check);
1870 /* Fix actual inode counts */
1872 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1873 static int TraceBadLinkCounts = 0;
1874 #ifdef AFS_NAMEI_ENV
1875 if (VGLinkH->ih_ino == ip->inodeNumber) {
1876 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1877 VGLinkH_p1 = ip->u.param[0];
1878 continue; /* Deal with this last. */
1881 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1882 TraceBadLinkCounts--; /* Limit reports, per volume */
1883 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1884 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1885 ip->byteCount, ip->u.param[0], ip->u.param[1],
1886 ip->u.param[2], ip->u.param[3]);
1888 while (ip->linkCount > 0) {
1889 /* below used to assert, not break */
1891 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1892 Log ("idec failed. inode %s errno %d\n",
1893 PrintInode(NULL, ip->inodeNumber), errno);
1899 while (ip->linkCount < 0) {
1900 /* these used to be asserts */
1902 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1903 Log ("iinc failed. inode %s errno %d\n",
1904 PrintInode(NULL, ip->inodeNumber) ,errno);
1911 #ifdef AFS_NAMEI_ENV
1912 while (dec_VGLinkH > 0) {
1913 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1914 Log("idec failed on link table, errno = %d\n", errno);
1918 while (dec_VGLinkH < 0) {
1919 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1920 Log("iinc failed on link table, errno = %d\n", errno);
1927 /* Directory consistency checks on the rw volume */
1929 SalvageVolume(isp, VGLinkH);
1930 IH_RELEASE(VGLinkH);
1932 if (canfork && !debug) {
1938 int QuickCheck(register struct InodeSummary *isp, int nVols)
1940 /* Check headers BEFORE forking */
1944 for (i = 0; i<nVols; i++) {
1945 struct VolumeSummary *vs = isp[i].volSummary;
1946 VolumeDiskData volHeader;
1948 /* Don't salvage just because phantom rw volume is there... */
1949 /* (If a read-only volume exists, read/write inodes must also exist) */
1950 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
1954 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
1955 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
1956 == sizeof(volHeader)
1957 && volHeader.stamp.magic == VOLUMEINFOMAGIC
1958 && volHeader.dontSalvage == DONT_SALVAGE
1959 && volHeader.needsSalvaged == 0
1960 && volHeader.destroyMe == 0) {
1961 if (volHeader.inUse == 1) {
1962 volHeader.inUse = 0;
1963 volHeader.inService = 1;
1965 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
1966 != sizeof(volHeader)) {
1983 /* SalvageVolumeHeaderFile
1985 * Salvage the top level V*.vol header file. Make sure the special files
1986 * exist and that there are no duplicates.
1988 * Calls SalvageHeader for each possible type of volume special file.
1991 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
1992 register struct ViceInodeInfo *inodes,
1993 int RW, int check, int *deleteMe)
1997 register struct ViceInodeInfo *ip;
1998 int allinodesobsolete = 1;
1999 struct VolumeDiskHeader diskHeader;
2003 bzero(&tempHeader, sizeof(tempHeader));
2004 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2005 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2006 tempHeader.id = isp->volumeId;
2007 tempHeader.parent = isp->RWvolumeId;
2008 /* Check for duplicates (inodes are sorted by type field) */
2009 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2010 ip = &inodes[isp->index+i];
2011 if (ip->u.special.type == (ip+1)->u.special.type) {
2012 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2016 for (i = 0; i<isp->nSpecialInodes; i++) {
2017 ip = &inodes[isp->index+i];
2018 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2020 Log("Rubbish header inode\n");
2023 Log("Rubbish header inode; deleted\n");
2025 else if (!stuff[ip->u.special.type-1].obsolete) {
2026 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2027 if (!check && ip->u.special.type != VI_LINKTABLE)
2028 ip->linkCount--; /* Keep the inode around */
2029 allinodesobsolete = 0;
2033 if (allinodesobsolete) {
2040 VGLinkH_cnt ++; /* one for every header. */
2042 if (!RW && !check && isp->volSummary) {
2043 ClearROInUseBit(isp->volSummary);
2047 for (i = 0; i< MAXINODETYPE; i++) {
2048 if (stuff[i].inodeType == VI_LINKTABLE) {
2049 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2050 * And we may have recreated the link table earlier, so set the
2051 * RW header as well.
2053 if (VALID_INO(VGLinkH->ih_ino)) {
2054 *stuff[i].inode = VGLinkH->ih_ino;
2058 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2062 if (isp->volSummary == NULL) {
2064 sprintf(name, VFORMAT, isp->volumeId);
2066 Log("No header file for volume %u\n", isp->volumeId);
2069 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2070 isp->volumeId, (Testing?"it would have been ":""),
2071 fileSysPathName, name);
2072 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2073 assert(headerFd != -1)
2074 isp->volSummary = (struct VolumeSummary *)
2075 malloc(sizeof(struct VolumeSummary));
2076 isp->volSummary->fileName = ToString(name);
2080 /* hack: these two fields are obsolete... */
2081 isp->volSummary->header.volumeAcl = 0;
2082 isp->volSummary->header.volumeMountTable = 0;
2084 if (bcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2085 /* We often remove the name before calling us, so we make a fake one up */
2086 if (isp->volSummary->fileName) {
2087 strcpy(name, isp->volSummary->fileName);
2089 sprintf(name, VFORMAT, isp->volumeId);
2090 isp->volSummary->fileName = ToString(name);
2093 Log("Header file %s is damaged or no longer valid%s\n",
2094 name, (check ? "" : "; repairing"));
2098 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2099 assert(headerFd != -1)
2103 bcopy(&tempHeader,&isp->volSummary->header,sizeof(struct VolumeHeader));
2105 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2107 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2108 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2109 != sizeof(struct VolumeDiskHeader)) {
2110 Log("Couldn't rewrite volume header file!\n");
2117 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2118 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2122 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2123 int check, int *deleteMe)
2126 VolumeDiskData volumeInfo;
2127 struct versionStamp fileHeader;
2136 #ifndef AFS_NAMEI_ENV
2137 if ( sp->inodeType == VI_LINKTABLE)
2140 if (*(sp->inode) == 0) {
2142 Log("Missing inode in volume header (%s)\n", sp->description);
2145 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2146 sp->description, (Testing ? "it would have recreated it": "recreating"));
2148 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2149 isp->volumeId, INODESPECIAL,
2150 sp->inodeType, isp->RWvolumeId);
2151 if (!VALID_INO(*(sp->inode)))
2152 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2153 sp->description, errno);
2158 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2159 fdP = IH_OPEN(specH);
2160 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2161 /* bail out early and destroy the volume */
2162 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2164 if (deleteMe) *deleteMe = 1;
2169 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2170 sp->description, errno);
2173 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2174 || header.fileHeader.magic != sp->stamp.magic)) {
2176 Log("Part of the header (%s) is corrupted\n", sp->description);
2177 FDH_REALLYCLOSE(fdP);
2181 Log("Part of the header (%s) is corrupted; recreating\n",
2185 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2188 FDH_REALLYCLOSE(fdP);
2192 if (recreate && !Testing) {
2194 Abort("Internal error: recreating volume header (%s) in check mode\n",
2196 code = FDH_TRUNC(fdP, 0);
2198 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2199 sp->description, errno);
2201 /* The following code should be moved into vutil.c */
2202 if (sp->inodeType == VI_VOLINFO) {
2204 bzero(&header.volumeInfo, sizeof (header.volumeInfo));
2205 header.volumeInfo.stamp = sp->stamp;
2206 header.volumeInfo.id = isp->volumeId;
2207 header.volumeInfo.parentId = isp->RWvolumeId;
2208 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2209 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2210 header.volumeInfo.inService = 0;
2211 header.volumeInfo.blessed = 0;
2212 /* The + 1000 is a hack in case there are any files out in venus caches */
2213 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2214 header.volumeInfo.type =
2215 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2216 header.volumeInfo.needsCallback = 0;
2217 gettimeofday(&tp,0);
2218 header.volumeInfo.creationDate = tp.tv_sec;
2219 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2220 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2222 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2223 sizeof(header.volumeInfo));
2224 if (code != sizeof(header.volumeInfo)) {
2226 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2227 sp->description, errno);
2228 Abort("Unable to write entire volume header file (%s)\n",
2233 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2234 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2236 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2237 if (code != sizeof(sp->stamp)) {
2239 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2240 sp->description, errno);
2241 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2246 FDH_REALLYCLOSE(fdP);
2248 if (sp->inodeType == VI_VOLINFO) {
2249 VolInfo = header.volumeInfo;
2252 if (VolInfo.updateDate) {
2253 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2254 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2255 (Testing?"it would have been ":""), update);
2257 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2258 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2267 int SalvageVnodes(register struct InodeSummary *rwIsp,
2268 register struct InodeSummary * thisIsp,
2269 register struct ViceInodeInfo * inodes, int check)
2271 int ilarge, ismall, ioffset, RW, nInodes;
2272 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2273 if (Showmode) return 0;
2274 RW = (rwIsp == thisIsp);
2275 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2276 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2277 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2278 if (check && ismall == -1)
2280 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2281 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2282 return (ilarge==0 && ismall==0 ? 0: -1);
2285 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2286 register struct ViceInodeInfo *ip,
2287 int nInodes, struct VolumeSummary *volSummary, int check)
2289 VolumeId volumeNumber;
2290 char buf[SIZEOF_LARGEDISKVNODE];
2291 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2293 StreamHandle_t *file;
2294 struct VnodeClassInfo *vcp;
2296 int vnodeIndex, nVnodes;
2297 afs_ino_str_t stmp1, stmp2;
2301 volumeNumber = volSummary->header.id;
2302 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2303 fdP = IH_OPEN(handle);
2304 assert(fdP != NULL);
2305 file = FDH_FDOPEN(fdP, "r+");
2306 assert(file != NULL)
2307 vcp = &VnodeClassInfo[class];
2308 size = OS_SIZE(fdP->fd_fd);
2310 nVnodes = (size / vcp->diskSize) - 1;
2312 assert((nVnodes+1) * vcp->diskSize == size)
2313 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2318 for (vnodeIndex = 0;
2319 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2320 nVnodes--, vnodeIndex++) {
2321 if (vnode->type != vNull) {
2322 int vnodeChanged = 0;
2323 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2324 /* Log programs that belong to root (potentially suid root);
2325 don't bother for read-only or backup volumes */
2326 #ifdef notdef /* This is done elsewhere */
2327 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2328 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2329 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2330 vnode->owner, vnode->modeBits);
2332 if (VNDISK_GET_INO(vnode) == 0) {
2334 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2335 bzero(vnode, vcp->diskSize);
2340 if (vcp->magic != vnode->vnodeMagic) {
2341 /* bad magic #, probably partially created vnode */
2342 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2343 bzero(vnode, vcp->diskSize);
2347 /* ****** Should do a bit more salvage here: e.g. make sure
2348 vnode type matches what it should be given the index */
2349 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2350 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2351 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2352 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2359 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2360 /* The following doesn't work, because the version number
2361 is not maintained correctly by the file server */
2362 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2363 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2365 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2372 /* For RW volume, look for vnode with matching inode number;
2373 if no such match, take the first determined by our sort
2375 register struct ViceInodeInfo *lip = ip;
2376 register lnInodes = nInodes;
2377 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2378 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2387 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2388 /* "Matching" inode */
2392 vu = vnode->uniquifier;
2393 iu = ip->u.vnode.vnodeUniquifier;
2394 vd = vnode->dataVersion;
2395 id = ip->u.vnode.inodeDataVersion;
2397 * Because of the possibility of the uniquifier overflows (> 4M)
2398 * we compare them modulo the low 22-bits; we shouldn't worry
2399 * about mismatching since they shouldn't to many old
2400 * uniquifiers of the same vnode...
2402 if (IUnique(vu) != IUnique(iu)) {
2404 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2405 vnodeNumber, IUnique(vu), IUnique(iu));
2408 vnode->uniquifier = iu;
2409 #ifdef AFS_3DISPARES
2410 vnode->dataVersion = (id >= vd ?
2411 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2412 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2414 #if defined(AFS_SGI_EXMAG)
2415 vnode->dataVersion = (id >= vd ?
2416 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2417 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2419 vnode->dataVersion = (id>vd ? id:vd);
2420 #endif /* AFS_SGI_EXMAG */
2421 #endif /* AFS_3DISPARES */
2425 /* don't bother checking for vd > id any more, since
2426 partial file transfers always result in this state,
2427 and you can't do much else anyway (you've already
2428 found the best data you can) */
2429 #ifdef AFS_3DISPARES
2430 if (!vnodeIsDirectory(vnodeNumber) &&
2431 ((vd < id && (id-vd) < 1887437) ||
2432 ((vd > id && (vd-id) > 1887437)))) {
2434 #if defined(AFS_SGI_EXMAG)
2435 if (!vnodeIsDirectory(vnodeNumber) &&
2436 ((vd < id && (id-vd) < 15099494) ||
2437 ((vd > id && (vd-id) > 15099494)))) {
2439 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2440 #endif /* AFS_SGI_EXMAG */
2442 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2443 vnode->dataVersion = id;
2448 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2451 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2452 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2453 PrintInode(stmp2, ip->inodeNumber),
2456 VNDISK_SET_INO(vnode, ip->inodeNumber);
2461 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2463 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2464 PrintInode(stmp2, ip->inodeNumber),
2467 VNDISK_SET_INO(vnode, ip->inodeNumber);
2470 if (ip->byteCount != vnode->length) {
2472 if (!Showmode) Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2473 vnodeNumber, vnode->length, ip->byteCount);
2477 if (!Showmode) Log("Vnode %d: length incorrect; changed from %d to %d\n",
2478 vnodeNumber, vnode->length, ip->byteCount);
2479 vnode->length = ip->byteCount;
2483 ip->linkCount--; /* Keep the inode around */
2487 else { /* no matching inode */
2488 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2489 /* No matching inode--get rid of the vnode */
2491 if (VNDISK_GET_INO(vnode)) {
2493 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2494 vnodeNumber, vnode->uniquifier,
2495 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2498 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2503 if (VNDISK_GET_INO(vnode)) {
2505 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2506 vnodeNumber, vnode->uniquifier,
2507 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2508 ctime((time_t *)&(vnode->serverModifyTime)));
2511 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)));
2513 bzero(vnode, vcp->diskSize);
2516 /* Should not reach here becuase we checked for
2517 * (inodeNumber == 0) above. And where we zero the vnode,
2518 * we also goto vnodeDone.
2522 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2526 } /* VNDISK_GET_INO(vnode) != 0 */
2528 assert(!(vnodeChanged && check));
2529 if (vnodeChanged && !Testing) {
2530 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2531 (char*)vnode, vcp->diskSize)
2533 VolumeChanged = 1; /* For break call back */
2544 struct VnodeEssence *CheckVnodeNumber(vnodeNumber)
2545 VnodeId vnodeNumber;
2548 struct VnodeInfo *vip;
2551 class = vnodeIdToClass(vnodeNumber);
2552 vip = &vnodeInfo[class];
2553 offset = vnodeIdToBitNumber(vnodeNumber);
2554 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2558 void CopyOnWrite(register struct DirSummary *dir)
2560 /* Copy the directory unconditionally if we are going to change it:
2561 * not just if was cloned.
2563 struct VnodeDiskObject vnode;
2564 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2565 Inode oldinode, newinode;
2568 if (dir->copied || Testing)
2570 DFlush(); /* Well justified paranoia... */
2572 code = IH_IREAD(vnodeInfo[vLarge].handle,
2573 vnodeIndexOffset(vcp, dir->vnodeNumber),
2574 (char*)&vnode, sizeof (vnode));
2575 assert(code == sizeof(vnode));
2576 oldinode = VNDISK_GET_INO(&vnode);
2577 /* Increment the version number by a whole lot to avoid problems with
2578 * clients that were promised new version numbers--but the file server
2579 * crashed before the versions were written to disk.
2581 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2582 dir->rwVid, dir->vnodeNumber,
2583 vnode.uniquifier, vnode.dataVersion += 200);
2584 assert(VALID_INO(newinode));
2585 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2587 VNDISK_SET_INO(&vnode, newinode);
2588 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2589 vnodeIndexOffset(vcp, dir->vnodeNumber),
2590 (char*)&vnode, sizeof (vnode));
2591 assert(code == sizeof (vnode));
2593 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2594 fileSysDevice, newinode);
2595 /* Don't delete the original inode right away, because the directory is
2596 * still being scanned.
2601 /* This function should either successfully create a new dir, or give up and leave
2602 * things the way they were. In particular, if it fails to write the new dir properly,
2603 * it should return w/o changing the reference to the old dir.
2605 void CopyAndSalvage(register struct DirSummary *dir)
2607 struct VnodeDiskObject vnode;
2608 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2609 Inode oldinode, newinode;
2611 register afs_int32 code;
2612 afs_int32 parentUnique= 1;
2613 struct VnodeEssence *vnodeEssence;
2617 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2618 code = IH_IREAD(vnodeInfo[vLarge].handle,
2619 vnodeIndexOffset(vcp, dir->vnodeNumber),
2620 (char*)&vnode, sizeof (vnode));
2621 assert(code == sizeof (vnode));
2622 oldinode = VNDISK_GET_INO(&vnode);
2623 /* Increment the version number by a whole lot to avoid problems with
2624 * clients that were promised new version numbers--but the file server
2625 * crashed before the versions were written to disk.
2627 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2628 dir->rwVid, dir->vnodeNumber,
2630 vnode.dataVersion += 200);
2631 assert(VALID_INO(newinode));
2632 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2634 /* Assign . and .. vnode numbers from dir and vnode.parent.
2635 * The uniquifier for . is in the vnode.
2636 * The uniquifier for .. might be set to a bogus value of 1 and
2637 * the salvager will later clean it up.
2639 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2640 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2642 code = DirSalvage(&dir->dirHandle, &newdir,
2643 dir->vnodeNumber, vnode.uniquifier,
2644 (vnode.parent?vnode.parent:dir->vnodeNumber),
2646 if (code == 0) code = DFlush();
2648 /* didn't really build the new directory properly, let's just give up. */
2649 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2651 Log("Directory salvage returned code %d, continuing.\n", code);
2654 Log("Checking the results of the directory salvage...\n");
2655 if (!DirOK(&newdir)) {
2656 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2657 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2662 VNDISK_SET_INO(&vnode, newinode);
2663 vnode.length = Length(&newdir);
2664 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2665 vnodeIndexOffset(vcp, dir->vnodeNumber),
2666 (char*)&vnode, sizeof (vnode));
2667 assert(code == sizeof (vnode));
2669 nt_sync(fileSysDevice);
2671 sync(); /* this is slow, but hopefully rarely called. We don't have
2672 * an open FD on the file itself to fsync.
2675 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2677 dir->dirHandle = newdir;
2680 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2683 struct VnodeEssence *vnodeEssence;
2684 afs_int32 dirOrphaned, todelete;
2686 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2688 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2689 if (vnodeEssence == NULL) {
2691 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2692 dir->vnodeNumber, dir->name?dir->name:"??",
2693 name, vnodeNumber, unique);
2697 assert(Delete(&dir->dirHandle, name) == 0)
2703 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2704 * mount inode for the partition. If this inode were deleted, it would crash
2707 if (vnodeEssence->InodeNumber == 0) {
2708 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2709 dir->vnodeNumber, (dir->name?dir->name:"??"),
2710 name, vnodeNumber, unique,
2711 (Testing?"-- would have deleted":" -- deleted"));
2714 assert(Delete(&dir->dirHandle, name) == 0);
2720 if (!(vnodeNumber & 1) && !Showmode &&
2721 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2722 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2723 dir->vnodeNumber, (dir->name?dir->name:"??"),
2724 name, vnodeNumber, unique,
2725 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2729 assert(Delete(&dir->dirHandle, name) == 0);
2735 /* Check if the Uniquifiers match. If not, change the directory entry
2736 * so its unique matches the vnode unique. Delete if the unique is zero
2737 * or if the directory is orphaned.
2739 if (!IUnique(vnodeEssence->unique) ||
2740 (IUnique(vnodeEssence->unique) != IUnique(unique)) ) {
2741 if ( !IUnique(vnodeEssence->unique) &&
2742 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2743 /* This is an orphaned directory. Don't delete the . or ..
2744 * entry. Otherwise, it will get created in the next
2745 * salvage and deleted again here. So Just skip it.
2750 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2753 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2754 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2755 name, vnodeNumber, unique, vnodeEssence->unique,
2756 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2760 fid.Vnode = vnodeNumber;
2761 fid.Unique = vnodeEssence->unique;
2763 assert(Delete(&dir->dirHandle, name) == 0)
2765 assert(Create(&dir->dirHandle, name, &fid) == 0)
2767 if (todelete) return; /* no need to continue */
2770 if (strcmp(name,".") == 0) {
2771 if (dir->vnodeNumber != vnodeNumber || (IUnique(dir->unique) != IUnique(unique))) {
2773 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2774 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2777 assert(Delete(&dir->dirHandle, ".") == 0)
2778 fid.Vnode = dir->vnodeNumber;
2779 fid.Unique = dir->unique;
2780 assert(Create(&dir->dirHandle, ".", &fid) == 0)
2783 vnodeNumber = fid.Vnode; /* Get the new Essence */
2784 unique = fid.Unique;
2785 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2789 else if (strcmp(name,"..") == 0) {
2792 struct VnodeEssence *dotdot;
2793 pa.Vnode = dir->parent;
2794 dotdot = CheckVnodeNumber(pa.Vnode);
2795 assert (dotdot != NULL); /* XXX Should not be assert */
2796 pa.Unique = dotdot->unique;
2799 pa.Vnode = dir->vnodeNumber;
2800 pa.Unique = dir->unique;
2802 if ((pa.Vnode != vnodeNumber) || (IUnique(pa.Unique) != IUnique(unique))) {
2803 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2804 dir->vnodeNumber, IUnique(dir->unique), vnodeNumber, IUnique(unique));
2807 assert(Delete(&dir->dirHandle, "..") == 0);
2808 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2811 vnodeNumber = pa.Vnode; /* Get the new Essence */
2813 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2815 dir->haveDotDot = 1;
2816 } else if (strncmp(name,".__afs",6) == 0) {
2818 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2819 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2823 assert(Delete(&dir->dirHandle, name) == 0)
2825 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2826 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2830 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2831 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2832 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber, dir->vnodeNumber);
2833 if (ShowMounts && (vnodeEssence->type == vSymlink) && !(vnodeEssence->modeBits & 0111)) {
2839 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2840 vnodeEssence->InodeNumber);
2842 assert(fdP != NULL);
2843 size = FDH_SIZE(fdP);
2846 if (size > 1024) size = 1024;
2847 code = FDH_READ(fdP, buf, size);
2848 assert(code == size);
2849 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2850 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2851 dir->name?dir->name:"??", name, buf);
2852 FDH_REALLYCLOSE(fdP);
2855 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2856 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2857 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2858 if (vnodeIdToClass(vnodeNumber) == vLarge && vnodeEssence->name == (char *)0) {
2860 if (n = (char*)malloc(strlen(name)+1))
2862 vnodeEssence->name = n;
2865 /* The directory entry points to the vnode. Check to see if the
2866 * vnode points back to the directory. If not, then let the
2867 * directory claim it (else it might end up orphaned). Vnodes
2868 * already claimed by another directory are deleted from this
2869 * directory: hardlinks to the same vnode are not allowed
2870 * from different directories.
2872 if (vnodeEssence->parent != dir->vnodeNumber) {
2873 if (!vnodeEssence->claimed && !dirOrphaned) {
2874 /* Vnode does not point back to this directory.
2875 * Orphaned dirs cannot claim a file (it may belong to
2876 * another non-orphaned dir).
2879 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2880 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2881 vnodeNumber, unique, (Testing?"would have been ":""),
2882 vnodeEssence->parent, dir->vnodeNumber);
2884 vnodeEssence->parent = dir->vnodeNumber;
2885 vnodeEssence->changed = 1;
2887 /* Vnode was claimed by another directory */
2890 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2891 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2892 vnodeEssence->parent, vnodeNumber, unique,
2893 (Testing?"would have been ":""));
2895 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2896 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2897 vnodeEssence->parent, vnodeNumber, unique,
2898 (Testing?"would have been ":""));
2903 assert(Delete(&dir->dirHandle, name) == 0);
2908 /* This directory claims the vnode */
2909 vnodeEssence->claimed = 1;
2911 vnodeEssence->count--;
2914 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
2917 register struct VnodeInfo *vip = &vnodeInfo[class];
2918 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
2919 char buf[SIZEOF_LARGEDISKVNODE];
2920 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2923 StreamHandle_t *file;
2928 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
2929 fdP = IH_OPEN(vip->handle);
2931 file = FDH_FDOPEN(fdP, "r+");
2932 assert(file != NULL);
2933 size = OS_SIZE(fdP->fd_fd);
2935 vip->nVnodes = (size / vcp->diskSize) - 1;
2936 if (vip->nVnodes > 0) {
2937 assert((vip->nVnodes+1)*vcp->diskSize == size)
2938 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2939 assert((vip->vnodes = (struct VnodeEssence *)
2940 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL)
2941 if (class == vLarge) {
2942 assert((vip->inodes = (Inode *)
2943 calloc(vip->nVnodes, sizeof (Inode))) != NULL)
2954 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
2955 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
2956 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2957 nVnodes--, vnodeIndex++) {
2958 if (vnode->type != vNull) {
2959 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
2960 vip->nAllocatedVnodes++;
2961 vep->count = vnode->linkCount;
2962 vep->blockCount = nBlocks(vnode->length);
2963 vip->volumeBlockCount += vep->blockCount;
2964 vep->parent = vnode->parent;
2965 vep->unique = vnode->uniquifier;
2966 if (*maxu < vnode->uniquifier)
2967 *maxu = vnode->uniquifier;
2968 vep->modeBits = vnode->modeBits;
2969 vep->InodeNumber = VNDISK_GET_INO(vnode);
2970 vep->type = vnode->type;
2971 vep->author = vnode->author;
2972 vep->owner = vnode->owner;
2973 vep->group = vnode->group;
2974 if (vnode->type == vDirectory) {
2975 assert(class == vLarge)
2976 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
2984 static char *GetDirName(vnode, vp, path)
2986 struct VnodeEssence *vp;
2989 struct VnodeEssence *parentvp;
2995 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
2997 strcat(path, vp->name);
3003 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3004 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3006 static int IsVnodeOrphaned(vnode)
3009 struct VnodeEssence *vep;
3011 if (vnode == 0) return(1); /* Vnode zero does not exist */
3012 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3013 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3014 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3016 return( IsVnodeOrphaned(vep->parent) );
3019 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3020 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3023 static struct DirSummary dir;
3024 static struct DirHandle dirHandle;
3025 struct VnodeEssence *parent;
3026 static char path[MAXPATHLEN];
3029 if (dirVnodeInfo->vnodes[i].salvaged)
3030 return; /* already salvaged */
3033 dirVnodeInfo->vnodes[i].salvaged = 1;
3035 if (dirVnodeInfo->inodes[i] == 0)
3036 return; /* Not allocated to a directory */
3038 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3039 if (parent && parent->salvaged == 0)
3040 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3041 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3042 rootdir, rootdirfound);
3043 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3044 dir.unique = dirVnodeInfo->vnodes[i].unique;
3047 dir.parent = dirVnodeInfo->vnodes[i].parent;
3048 dir.haveDot = dir.haveDotDot = 0;
3049 dir.ds_linkH = alinkH;
3050 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3052 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3055 Log("Directory bad, vnode %d; %s...\n",
3056 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3059 CopyAndSalvage(&dir);
3063 dirHandle = dir.dirHandle;
3065 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3068 /* If enumeration failed for random reasons, we will probably delete
3069 * too much stuff, so we guard against this instead.
3071 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3074 /* Delete the old directory if it was copied in order to salvage.
3075 * CopyOnWrite has written the new inode # to the disk, but we still
3076 * have the old one in our local structure here. Thus, we idec the
3080 if (dir.copied && !Testing) {
3081 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3083 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3086 /* Remember rootdir DirSummary _after_ it has been judged */
3087 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3088 bcopy(&dir, rootdir, sizeof(struct DirSummary));
3095 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3097 /* This routine, for now, will only be called for read-write volumes */
3099 int BlocksInVolume = 0, FilesInVolume = 0;
3100 register VnodeClass class;
3101 struct DirSummary rootdir, oldrootdir;
3102 struct VnodeInfo *dirVnodeInfo;
3103 struct VnodeDiskObject vnode;
3104 VolumeDiskData volHeader;
3106 int orphaned, rootdirfound = 0;
3107 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3108 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3109 struct VnodeEssence *vep;
3114 VnodeId LFVnode, ThisVnode;
3115 Unique LFUnique, ThisUnique;
3118 vid = rwIsp->volSummary->header.id;
3119 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3120 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3121 assert(nBytes == sizeof(volHeader));
3122 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3123 assert (volHeader.destroyMe != DESTROY_ME);
3124 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3126 DistilVnodeEssence(vid, vLarge,
3127 rwIsp->volSummary->header.largeVnodeIndex,
3129 DistilVnodeEssence(vid, vSmall,
3130 rwIsp->volSummary->header.smallVnodeIndex,
3133 dirVnodeInfo = &vnodeInfo[vLarge];
3134 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3135 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3136 &rootdir, &rootdirfound);
3143 /* Parse each vnode looking for orphaned vnodes and
3144 * connect them to the tree as orphaned (if requested).
3146 oldrootdir = rootdir;
3147 for (class=0; class < nVNODECLASSES; class++) {
3148 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3149 vep = &(vnodeInfo[class].vnodes[v]);
3150 ThisVnode = bitNumberToVnodeNumber(v, class);
3151 ThisUnique = vep->unique;
3153 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3154 continue; /* Ignore unused, claimed, and root vnodes */
3156 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3157 * entry in this vnode had incremented the parent link count (In
3158 * JudgeEntry()). We need to go to the parent and decrement that
3159 * link count. But if the parent's unique is zero, then the parent
3160 * link count was not incremented in JudgeEntry().
3162 if (class == vLarge) { /* directory vnode */
3163 pv = vnodeIdToBitNumber(vep->parent);
3164 if (IUnique(vnodeInfo[vLarge].vnodes[pv].unique) != 0)
3165 vnodeInfo[vLarge].vnodes[pv].count++;
3169 continue; /* If no rootdir, can't attach orphaned files */
3171 /* Here we attach orphaned files and directories into the
3172 * root directory, LVVnode, making sure link counts stay correct.
3174 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3175 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3176 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3178 /* Update this orphaned vnode's info. Its parent info and
3179 * link count (do for orphaned directories and files).
3181 vep->parent = LFVnode; /* Parent is the root dir */
3182 vep->unique = LFUnique;
3185 vep->count--; /* Inc link count (root dir will pt to it) */
3187 /* If this orphaned vnode is a directory, change '..'.
3188 * The name of the orphaned dir/file is unknown, so we
3189 * build a unique name. No need to CopyOnWrite the directory
3190 * since it is not connected to tree in BK or RO volume and
3191 * won't be visible there.
3193 if (class == vLarge) {
3197 /* Remove and recreate the ".." entry in this orphaned directory */
3198 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3200 pa.Unique = LFUnique;
3201 assert(Delete(&dh, "..") == 0);
3202 assert(Create(&dh, "..", &pa) == 0);
3204 /* The original parent's link count was decremented above.
3205 * Here we increment the new parent's link count.
3207 pv = vnodeIdToBitNumber(LFVnode);
3208 vnodeInfo[vLarge].vnodes[pv].count--;
3212 /* Go to the root dir and add this entry. The link count of the
3213 * root dir was incremented when ".." was created. Try 10 times.
3215 for (j=0; j<10; j++) {
3216 pa.Vnode = ThisVnode;
3217 pa.Unique = ThisUnique;
3219 sprintf(npath, "%s.%d.%d",
3220 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3221 ThisVnode, ThisUnique);
3223 CopyOnWrite(&rootdir);
3224 code = Create(&rootdir.dirHandle, npath, &pa);
3227 ThisUnique += 50; /* Try creating a different file */
3230 Log("Attaching orphaned %s to volume's root dir as %s\n",
3231 ((class == vLarge)?"directory":"file"), npath);
3233 } /* for each vnode in the class */
3234 } /* for each class of vnode */
3236 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3238 if (!oldrootdir.copied && rootdir.copied) {
3239 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3241 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3244 DFlush(); /* Flush the changes */
3245 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3246 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3247 orphans = ORPH_IGNORE;
3250 /* Write out all changed vnodes. Orphaned files and directories
3251 * will get removed here also (if requested).
3253 for (class = 0; class < nVNODECLASSES; class++){
3254 int nVnodes = vnodeInfo[class].nVnodes;
3255 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3256 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3257 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3258 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3259 for (i = 0; i<nVnodes; i++) {
3260 register struct VnodeEssence *vnp = &vnodes[i];
3261 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3263 /* If the vnode is good but is unclaimed (not listed in
3264 * any directory entries), then it is orphaned.
3267 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3268 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3272 if (vnp->changed || vnp->count) {
3275 nBytes = IH_IREAD(vnodeInfo[class].handle,
3276 vnodeIndexOffset(vcp, vnodeNumber),
3277 (char*)&vnode, sizeof (vnode));
3278 assert(nBytes == sizeof(vnode));
3280 vnode.parent = vnp->parent;
3281 oldCount = vnode.linkCount;
3282 vnode.linkCount = vnode.linkCount - vnp->count;
3285 orphaned = IsVnodeOrphaned(vnodeNumber);
3287 if (!vnp->todelete) {
3288 /* Orphans should have already been attached (if requested) */
3289 assert(orphans != ORPH_ATTACH);
3290 oblocks += vnp->blockCount;
3293 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3294 BlocksInVolume -= vnp->blockCount;
3296 if (VNDISK_GET_INO(&vnode)) {
3297 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3300 bzero(&vnode, sizeof(vnode));
3302 } else if (vnp->count) {
3304 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3305 vnodeNumber, oldCount,
3306 (Testing?"would have changed to":"now"), vnode.linkCount);
3310 vnode.dataVersion++;
3312 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3313 vnodeIndexOffset(vcp, vnodeNumber),
3314 (char*)&vnode, sizeof (vnode));
3315 assert(nBytes == sizeof(vnode));
3321 if (!Showmode && ofiles) {
3322 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3323 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3327 for (class = 0; class < nVNODECLASSES; class++) {
3328 register struct VnodeInfo *vip = &vnodeInfo[class];
3329 for (i=0; i<vip->nVnodes; i++)
3330 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3331 if (vip->vnodes) free(vip->vnodes);
3332 if (vip->inodes) free(vip->inodes);
3335 /* Set correct resource utilization statistics */
3336 volHeader.filecount = FilesInVolume;
3337 volHeader.diskused = BlocksInVolume;
3339 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3340 if (volHeader.uniquifier < (maxunique + 1)) {
3341 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3342 /* Plus 2,000 in case there are workstations out there with
3343 * cached vnodes that have since been deleted
3345 volHeader.uniquifier = (maxunique + 1 + 2000);
3348 /* Turn off the inUse bit; the volume's been salvaged! */
3349 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3350 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3351 volHeader.inService = 1; /* allow service again */
3352 volHeader.needsCallback = (VolumeChanged != 0);
3353 volHeader.dontSalvage = DONT_SALVAGE;
3356 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3357 assert(nBytes == sizeof(volHeader));
3360 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3361 (Testing?"It would have ":""), volHeader.name,
3362 volHeader.id, FilesInVolume, BlocksInVolume);
3364 IH_RELEASE(vnodeInfo[vSmall].handle);
3365 IH_RELEASE(vnodeInfo[vLarge].handle);
3370 void ClearROInUseBit(struct VolumeSummary *summary)
3372 IHandle_t *h = summary->volumeInfoHandle;
3375 VolumeDiskData volHeader;
3377 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3378 assert(nBytes == sizeof(volHeader));
3379 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC)
3380 volHeader.inUse = 0;
3381 volHeader.needsSalvaged = 0;
3382 volHeader.inService = 1;
3383 volHeader.dontSalvage = DONT_SALVAGE;
3385 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3386 assert(nBytes == sizeof(volHeader));
3391 * Possible delete the volume.
3393 * deleteMe - Always do so, only a partial volume.
3395 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3399 if (readOnly(isp) || deleteMe) {
3400 if (isp->volSummary && isp->volSummary->fileName) {
3402 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);
3403 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3405 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3406 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3409 unlink(isp->volSummary->fileName);
3413 Log("%s salvage was unsuccessful: read-write volume %u\n",
3414 message, isp->volumeId);
3415 Abort("Salvage of volume %u aborted\n",
3421 void AskOffline(VolumeId volumeId)
3423 if (FSYNC_askfs(volumeId, (char *)0, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3424 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3425 Abort("Salvage aborted\n");
3429 void AskOnline(VolumeId volumeId, char *partition)
3431 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3432 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3433 volumeId, partition);
3437 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3439 /* Volume parameter is passed in case iopen is upgraded in future to
3440 * require a volume Id to be passed
3443 IHandle_t *srcH, *destH;
3444 FdHandle_t *srcFdP, *destFdP;
3447 IH_INIT(srcH, device, rwvolume, inode1);
3448 srcFdP = IH_OPEN(srcH);
3449 assert(srcFdP != NULL);
3450 IH_INIT(destH, device, rwvolume, inode2);
3451 destFdP = IH_OPEN(destH);
3453 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3454 assert(FDH_WRITE(destFdP, buf, n) == n);
3456 FDH_REALLYCLOSE(srcFdP);
3457 FDH_REALLYCLOSE(destFdP);
3463 void PrintInodeList(void)
3465 register struct ViceInodeInfo *ip;
3466 struct ViceInodeInfo *buf;
3470 assert(fstat(inodeFd, &status) == 0);
3471 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3472 assert(buf != NULL);
3473 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3474 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3475 for(ip = buf; nInodes--; ip++) {
3476 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3477 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3478 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3483 void PrintInodeSummary(void)
3486 struct InodeSummary *isp;
3488 for (i=0; i<nVolumesInInodeFile; i++) {
3489 isp = &inodeSummary[i];
3490 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3491 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3492 isp->nSpecialInodes, isp->maxUniquifier);
3496 void PrintVolumeSummary(void)
3499 struct VolumeSummary *vsp;
3501 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3502 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3510 assert(0); /* Fork is never executed in the NT code path */
3521 if (ShowLog) showlog();
3523 if (main_thread != pthread_self())
3524 pthread_exit((void*)code);
3532 int Wait(char *prog)
3536 pid = wait(&status);
3538 if (WCOREDUMP(status))
3539 Log("\"%s\" core dumped!\n", prog);
3540 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3545 static char *TimeStamp(time_t clock, int precision)
3548 static char timestamp[20];
3549 lt = localtime(&clock);
3551 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3553 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3557 void CheckLogFile(void)
3559 char oldSlvgLog[AFSDIR_PATH_MAX];
3561 #ifndef AFS_NT40_ENV
3568 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3569 strcat(oldSlvgLog, ".old");
3571 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3572 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3574 if (!logFile) { /* still nothing, use stdout */
3579 #ifndef AFS_NAMEI_ENV
3580 AFS_DEBUG_IOPS_LOG(logFile);
3589 #ifndef AFS_NT40_ENV
3591 printf("Can't show log since using syslog.\n");
3600 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3603 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3606 while (fgets(line, sizeof(line), logFile))
3612 void Log(a,b,c,d,e,f,g,h,i,j,k)
3613 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3617 #ifndef AFS_NT40_ENV
3620 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3624 gettimeofday(&now, 0);
3625 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3626 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3631 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3632 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3634 #ifndef AFS_NT40_ENV
3637 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3641 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3643 if (ShowLog) showlog();
3650 char * ToString(char *s)
3653 p = (char *) malloc(strlen(s)+1);
3660 /* Remove the FORCESALVAGE file */
3661 void RemoveTheForce(char *path)
3663 if (!Testing && ForceSalvage) {
3664 if (chdir(path) == 0)
3665 unlink("FORCESALVAGE");
3669 #ifndef AFS_AIX32_ENV
3671 * UseTheForceLuke - see if we can use the force
3673 int UseTheForceLuke(char *path)
3677 assert(chdir(path) != -1);
3679 return (stat("FORCESALVAGE", &force) == 0);
3683 * UseTheForceLuke - see if we can use the force
3686 * The VRMIX fsck will not muck with the filesystem it is supposedly
3687 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3688 * muck directly with the root inode, which is within the normal
3690 * ListViceInodes() has a side effect of setting ForceSalvage if
3691 * it detects a need, based on root inode examination.
3693 int UseTheForceLuke(char *path)
3696 return 0; /* sorry OB1 */
3701 /* NT support routines */
3703 static char execpathname[MAX_PATH];
3704 int nt_SalvagePartition(char *partName, int jobn)
3709 if (!*execpathname) {
3710 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3711 if (!n || n == 1023)
3714 job.cj_magic = SALVAGER_MAGIC;
3715 job.cj_number = jobn;
3716 (void) strcpy(job.cj_part, partName);
3717 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3722 int nt_SetupPartitionSalvage(void *datap, int len)
3724 childJob_t *jobp = (childJob_t*)datap;
3725 char logname[AFSDIR_PATH_MAX];
3727 if (len != sizeof(childJob_t))
3729 if (jobp->cj_magic != SALVAGER_MAGIC)
3734 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3736 logFile = fopen(logname, "w");
3737 if (!logFile) logFile = stdout;
3743 #endif /* AFS_NT40_ENV */