2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #define SalvageVersion "2.4"
88 /* Main program file. Define globals. */
91 #include <afsconfig.h>
92 #include <afs/param.h>
100 #include <sys/stat.h>
105 #include <WINNT/afsevent.h>
107 #include <sys/param.h>
108 #include <sys/file.h>
110 #include <sys/time.h>
111 #endif /* ITIMER_REAL */
113 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
114 #define WCOREDUMP(x) (x & 0200)
117 #include <afs/afsint.h>
118 #include <afs/assert.h>
119 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
120 #if defined(AFS_VFSINCL_ENV)
121 #include <sys/vnode.h>
123 #include <sys/fs/ufs_inode.h>
125 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
126 #include <ufs/ufs/dinode.h>
127 #include <ufs/ffs/fs.h>
129 #include <ufs/inode.h>
132 #else /* AFS_VFSINCL_ENV */
134 #include <ufs/inode.h>
135 #else /* AFS_OSF_ENV */
136 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
137 #include <sys/inode.h>
140 #endif /* AFS_VFSINCL_ENV */
141 #endif /* AFS_SGI_ENV */
144 #include <sys/lockf.h>
148 #include <checklist.h>
150 #if defined(AFS_SGI_ENV)
155 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
158 #include <sys/mnttab.h>
159 #include <sys/mntent.h>
164 #endif /* AFS_SGI_ENV */
165 #endif /* AFS_HPUX_ENV */
170 #include <afs/osi_inode.h>
173 #include <afs/afsutil.h>
174 #include <afs/fileutil.h>
175 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
183 #include <afs/afssyscalls.h>
187 #include "partition.h"
189 #include "viceinode.h"
191 #include "volinodes.h" /* header magic number, etc. stuff */
197 extern void *calloc();
199 extern char *vol_DevName();
200 static char *TimeStamp(time_t clock, int precision);
202 #define ORPH_IGNORE 0
203 #define ORPH_REMOVE 1
204 #define ORPH_ATTACH 2
207 int debug; /* -d flag */
208 int Testing=0; /* -n flag */
209 int ListInodeOption; /* -i flag */
210 int ShowRootFiles; /* -r flag */
211 int RebuildDirs; /* -sal flag */
212 int Parallel = 4; /* -para X flag */
213 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
214 int forceR = 0; /* -b flag */
215 int ShowLog = 0; /* -showlog flag */
216 int ShowSuid = 0; /* -showsuid flag */
217 int ShowMounts = 0; /* -showmounts flag */
218 int orphans = ORPH_IGNORE; /* -orphans option */
222 int useSyslog = 0; /* -syslog flag */
223 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
226 #define MAXPARALLEL 32
228 int OKToZap; /* -o flag */
229 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
230 in the volume header */
232 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
234 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
236 Device fileSysDevice; /* The device number of the current
237 partition being salvaged */
241 char *fileSysPath; /* The path of the mounted partition currently
242 being salvaged, i.e. the directory
243 containing the volume headers */
245 char *fileSysPathName; /* NT needs this to make name pretty in log. */
246 IHandle_t *VGLinkH; /* Link handle for current volume group. */
247 int VGLinkH_cnt; /* # of references to lnk handle. */
248 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
250 char *fileSysDeviceName; /* The block device where the file system
251 being salvaged was mounted */
252 char *filesysfulldev;
254 int VolumeChanged; /* Set by any routine which would change the volume in
255 a way which would require callback is to be broken if the
256 volume was put back on line by an active file server */
258 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
260 struct InodeSummary { /* Inode summary file--an entry for each
261 volume in the inode file for a partition */
262 VolId volumeId; /* Volume id */
263 VolId RWvolumeId; /* RW volume associated */
264 int index; /* index into inode file (0, 1, 2 ...) */
265 int nInodes; /* Number of inodes for this volume */
266 int nSpecialInodes; /* Number of special inodes, i.e. volume
267 header, index, etc. These are all
268 marked (viceinode.h) and will all be sorted
269 to the beginning of the information for
270 this volume. Read-only volumes should
271 ONLY have special inodes (all the other
272 inodes look as if they belong to the
273 original RW volume). */
274 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
275 This is only useful for RW volumes and is used
276 to compute a new volume uniquifier in the event
277 that the header needs to be recreated. The inode
278 uniquifier may be a truncated version of vnode
279 uniquifier (AFS_3DISPARES). The real maxUniquifer
280 is from the vnodes and later calcuated from it */
281 struct VolumeSummary *volSummary;
282 /* Either a pointer to the original volume
283 header summary, or constructed summary
286 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
287 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
288 int inodeFd; /* File descriptor for inode file */
291 struct VolumeSummary { /* Volume summary an entry for each
292 volume in a volume directory.
293 Assumption: one volume directory per
295 char *fileName; /* File name on the partition for the volume
297 struct VolumeHeader header;
298 /* volume number, rw volume number, inode
299 numbers of each major component of
301 IHandle_t *volumeInfoHandle;
302 byte wouldNeedCallback; /* set if the file server should issue
303 call backs for all the files in this volume when
304 the volume goes back on line */
308 IHandle_t *handle; /* Inode containing this index */
309 int nVnodes; /* Total number of vnodes in index */
310 int nAllocatedVnodes; /* Total number actually used */
311 int volumeBlockCount; /* Total number of blocks used by volume */
312 Inode *inodes; /* Directory only */
313 struct VnodeEssence {
314 short count; /* Number of references to vnode; MUST BE SIGNED */
315 unsigned claimed:1; /* Set when a parent directory containing an entry
316 referencing this vnode is found. The claim
317 is that the parent in "parent" can point to
318 this vnode, and no other */
319 unsigned changed:1; /* Set if any parameters (other than the count)
320 in the vnode change. It is determined if the
321 link count has changed by noting whether it is
322 0 after scanning all directories */
323 unsigned salvaged:1;/* Set if this directory vnode has already been salvaged. */
324 unsigned todelete:1;/* Set if this vnode is to be deleted (should not be claimed) */
325 afs_offs_t blockCount;
326 /* Number of blocks (1K) used by this vnode,
328 VnodeId parent; /* parent in vnode */
329 Unique unique; /* Must match entry! */
330 char *name; /* Name of directory entry */
331 int modeBits; /* File mode bits */
332 Inode InodeNumber; /* file's inode */
333 int type; /* File type */
334 int author; /* File author */
335 int owner; /* File owner */
336 int group; /* File group */
338 } vnodeInfo[nVNODECLASSES];
341 struct DirHandle dirHandle;
344 unsigned haveDot, haveDotDot;
346 int copied; /* If the copy-on-write stuff has been applied */
354 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
355 int nVolumes; /* Number of volumes (read-write and read-only)
359 /* For NT, we can fork the per partition salvagers to gain the required
360 * safety against Aborts. But there's too many complex data structures at
361 * the per volume salvager layer to easilty copy the data across.
362 * childJobNumber is resset from -1 to the job number if this is a
363 * per partition child of the main salvager. This information is passed
364 * out-of-band in the extra data area setup for the now unused parent/child
367 #define SALVAGER_MAGIC 0x00BBaaDD
368 #define NOT_CHILD -1 /* job numbers start at 0 */
369 /* If new options need to be passed to child, add them here. */
376 /* Child job this process is running. */
377 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD};
379 int nt_SalvagePartition(char *partName, int jobn);
380 int nt_SetupPartitionSalvage(void *datap, int len);
383 struct InodeSummary *svgp_inodeSummaryp;
393 /* Forward declarations */
394 void Log(), Abort(), Exit();
396 int Wait(char *prog);
397 char * ToString(char *s);
398 void AskOffline(VolumeId volumeId);
399 void AskOnline(VolumeId volumeId, char *partition);
400 void CheckLogFile(void);
401 void ClearROInUseBit(struct VolumeSummary *summary);
402 void CopyAndSalvage(register struct DirSummary *dir);
403 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
404 void CopyOnWrite(register struct DirSummary *dir);
405 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
406 register struct InodeSummary * summary);
407 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
408 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
410 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
411 void GetVolumeSummary(VolumeId singleVolumeNumber);
412 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
414 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
417 void ObtainSalvageLock(void);
418 void PrintInodeList(void);
419 void PrintInodeSummary(void);
420 void PrintVolumeSummary(void);
421 int QuickCheck(register struct InodeSummary *isp, int nVols);
422 void RemoveTheForce(char *path);
423 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
424 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
426 void SalvageFileSysParallel(struct DiskPartition *partP);
427 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
428 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber);
429 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
430 int check, int *deleteMe);
431 int SalvageIndex(Inode ino, VnodeClass class, int RW,
432 register struct ViceInodeInfo *ip,
433 int nInodes, struct VolumeSummary *volSummary, int check);
434 int SalvageVnodes(register struct InodeSummary *rwIsp,
435 register struct InodeSummary * thisIsp,
436 register struct ViceInodeInfo * inodes, int check);
437 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH);
438 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
440 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
442 #define SalvageVolumeGroup DoSalvageVolumeGroup
444 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
445 register struct ViceInodeInfo *inodes,
446 int RW, int check, int *deleteMe);
448 int UseTheForceLuke(char *path);
450 static int IsVnodeOrphaned(VnodeId vnode);
452 /* Uniquifier stored in the Inode */
453 static Unique IUnique(u)
457 return(u & 0x3fffff);
459 #if defined(AFS_SGI_EXMAG)
460 return(u & SGI_UNIQMASK);
463 #endif /* AFS_SGI_EXMAG */
467 static int BadError(aerror)
468 register int aerror; {
469 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
471 return 0; /* otherwise may be transient, e.g. EMFILE */
477 struct cmd_syndesc *as;
479 register struct cmd_item *ti;
480 char pname[100], *temp;
481 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0, i;
482 struct DiskPartition *partP;
484 #ifdef AFS_SGI_VNODE_GLUE
485 if (afs_init_kernel_config(-1) <0) {
486 printf("Can't determine NUMA configuration, not starting salvager.\n");
492 for (i = 0; i < CMD_MAXPARMS; i++) {
493 if (as->parms[i].items) {
499 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
508 #endif /* FAST_RESTART */
509 if (ti = as->parms[0].items) { /* -partition */
511 strncpy(pname, ti->data, 100);
513 if (ti = as->parms[1].items) { /* -volumeid */
515 printf("You must also specify '-partition' option with the '-volumeid' option\n");
519 vid = atoi(ti->data);
521 if (as->parms[2].items) /* -debug */
523 if (as->parms[3].items) /* -nowrite */
525 if (as->parms[4].items) /* -inodes */
527 if (as->parms[5].items) /* -force */
529 if (as->parms[6].items) /* -oktozap */
531 if (as->parms[7].items) /* -rootinodes */
533 if (as->parms[8].items) /* -RebuildDirs */
535 if (as->parms[9].items) /* -ForceReads */
537 if (ti = as->parms[10].items) {/* -Parallel # */
539 if (strncmp(temp,"all",3) == 0) {
543 if (strlen(temp) != 0) {
544 Parallel = atoi(temp);
545 if (Parallel < 1) Parallel = 1;
546 if (Parallel > MAXPARALLEL) {
547 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
548 Parallel = MAXPARALLEL;
552 if (ti = as->parms[11].items) {/* -tmpdir */
556 dirp = opendir(tmpdir);
558 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
563 if (ti = as->parms[12].items) /* -showlog */
565 if (ti = as->parms[13].items) { /* -log */
570 if (ti = as->parms[14].items) { /* -showmounts */
575 if (ti = as->parms[15].items) { /* -orphans */
577 orphans = ORPH_IGNORE;
578 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
579 orphans = ORPH_REMOVE;
580 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
581 orphans = ORPH_ATTACH;
584 #ifndef AFS_NT40_ENV /* ignore options on NT */
585 if ( ti = as->parms[16].items) { /* -syslog */
589 if ( ti = as->parms[17].items) { /* -syslogfacility */
590 useSyslogFacility = atoi(ti->data);
596 if (ti = as->parms[18].items) { /* -DontSalvage */
597 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
605 #endif /* FAST_RESTART */
607 /* Note: if seemvol we initialize this as a standard volume utility: this has the
608 implication that the file server may be running; negotations have to be made with
609 the file server in this case to take the read write volume and associated read-only
610 volumes off line before salvaging */
613 if (afs_winsockInit()<0) {
614 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
615 AFSDIR_SALVAGER_FILE, 0);
616 Log("Failed to initailize winsock, exiting.\n");
621 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
624 if (myjob.cj_number != NOT_CHILD) {
627 (void) strcpy(pname, myjob.cj_part);
632 for (partP = DiskPartitionList; partP; partP = partP->next) {
633 SalvageFileSysParallel(partP);
635 SalvageFileSysParallel(0);
638 partP = VGetPartition(pname, 0);
640 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
645 SalvageFileSys(partP, 0);
647 /* Salvage individual volume */
649 Log("salvage: invalid volume id specified; salvage aborted\n");
652 SalvageFileSys (partP, vid);
660 #include "AFS_component_version_number.c"
664 char *save_args[MAX_ARGS];
666 pthread_t main_thread;
672 struct cmd_syndesc *ts;
674 char commandLine[150];
677 extern char cml_version_number[];
681 * The following signal action for AIX is necessary so that in case of a
682 * crash (i.e. core is generated) we can include the user's data section
683 * in the core dump. Unfortunately, by default, only a partial core is
684 * generated which, in many cases, isn't too useful.
686 struct sigaction nsa;
688 sigemptyset(&nsa.sa_mask);
689 nsa.sa_handler = SIG_DFL;
690 nsa.sa_flags = SA_FULLDUMP;
691 sigaction(SIGABRT, &nsa, NULL);
692 sigaction(SIGSEGV, &nsa, NULL);
695 /* Initialize directory paths */
696 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
698 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
700 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
704 main_thread = pthread_self();
705 if (spawnDatap && spawnDataLen) {
706 /* This is a child per partition salvager. Don't setup log or
707 * try to lock the salvager lock.
709 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
714 for (commandLine[0] = '\0', i=0; i<argc; i++) {
715 if (i > 0) strcat(commandLine, " ");
716 strcat(commandLine, argv[i]);
719 /* All entries to the log will be appended. Useful if there are
720 * multiple salvagers appending to the log.
725 #ifdef AFS_LINUX20_ENV
726 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
728 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
734 if (geteuid() != 0) {
735 printf("Salvager must be run as root.\n");
741 /* bad for normal help flag processing, but can do nada */
743 fprintf(logFile, "%s\n", cml_version_number);
744 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
746 /* Get and hold a lock for the duration of the salvage to make sure
747 * that no other salvage runs at the same time. The routine
748 * VInitVolumePackage (called below) makes sure that a file server or
749 * other volume utilities don't interfere with the salvage.
756 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
757 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
758 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
759 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
760 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
761 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
762 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
763 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
764 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
765 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
766 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
767 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
768 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
769 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
770 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
771 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
772 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
774 /* note - syslog isn't avail on NT, but if we make it conditional, have
775 to deal with screwy offsets for cmd params */
776 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
777 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
780 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");
781 #endif /* FAST_RESTART */
782 err = cmd_Dispatch(argc, argv);
786 /* Get the salvage lock if not already held. Hold until process exits. */
787 void ObtainSalvageLock(void)
792 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
793 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
795 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
797 "salvager: There appears to be another salvager running! Aborted.\n");
801 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
802 assert(salvageLock >= 0);
803 #ifdef AFS_DARWIN_ENV
804 if (flock(salvageLock, LOCK_EX) == -1) {
806 if (lockf(salvageLock, F_LOCK, 0) == -1) {
809 "salvager: There appears to be another salvager running! Aborted.\n");
816 #ifdef AFS_SGI_XFS_IOPS_ENV
817 /* Check if the given partition is mounted. For XFS, the root inode is not a
818 * constant. So we check the hard way.
820 int IsPartitionMounted(char *part)
823 struct mntent *mntent;
825 assert(mntfp = setmntent(MOUNTED, "r"));
826 while (mntent = getmntent(mntfp)) {
827 if (!strcmp(part, mntent->mnt_dir))
832 return mntent ? 1 : 1;
835 /* Check if the given inode is the root of the filesystem. */
836 #ifndef AFS_SGI_XFS_IOPS_ENV
837 int IsRootInode(status)
840 /* The root inode is not a fixed value in XFS partitions. So we need to see if
841 * the partition is in the list of mounted partitions. This only affects the
842 * SalvageFileSys path, so we check there.
844 return (status->st_ino == ROOTINODE);
849 #ifndef AFS_NAMEI_ENV
850 /* We don't want to salvage big files filesystems, since we can't put volumes on
853 int CheckIfBigFilesFS(mountPoint, devName)
857 struct superblock fs;
860 if (strncmp(devName, "/dev/", 5)) {
861 (void) sprintf(name, "/dev/%s", devName);
864 (void) strcpy(name, devName);
867 if (ReadSuper(&fs, name)<0) {
868 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
871 if (IsBigFilesFileSystem(&fs)) {
872 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
881 #define HDSTR "\\Device\\Harddisk"
882 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
883 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
890 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
892 if (strncmp(res, HDSTR, HDLEN)) {
895 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
896 res, HDSTR, p1->devName);
900 d1 = atoi(&res[HDLEN]);
902 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
904 if (strncmp(res, HDSTR, HDLEN)) {
907 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
908 res, HDSTR, p2->devName);
912 d2 = atoi(&res[HDLEN]);
917 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
920 /* This assumes that two partitions with the same device number divided by
921 * PartsPerDisk are on the same disk.
923 void SalvageFileSysParallel(struct DiskPartition *partP)
926 struct DiskPartition *partP;
927 int pid; /* Pid for this job */
928 int jobnumb; /* Log file job number */
929 struct job *nextjob; /* Next partition on disk to salvage */
931 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
932 struct job *thisjob = 0;
933 static int numjobs = 0;
934 static int jobcount = 0;
940 char logFileName[256];
944 /* We have a partition to salvage. Copy it into thisjob */
945 thisjob = (struct job *) malloc(sizeof(struct job));
947 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
950 memset(thisjob, 0, sizeof(struct job));
951 thisjob->partP = partP;
952 thisjob->jobnumb = jobcount;
955 else if (jobcount == 0) {
956 /* We are asking to wait for all jobs (partp == 0), yet we never
959 Log("No file system partitions named %s* found; not salvaged\n",
960 VICE_PARTITION_PREFIX);
964 if (debug || Parallel == 1) {
966 SalvageFileSys(thisjob->partP, 0);
973 /* Check to see if thisjob is for a disk that we are already
974 * salvaging. If it is, link it in as the next job to do. The
975 * jobs array has 1 entry per disk being salvages. numjobs is
976 * the total number of disks currently being salvaged. In
977 * order to keep thejobs array compact, when a disk is
978 * completed, the hightest element in the jobs array is moved
979 * down to now open slot.
981 for (j=0; j<numjobs; j++) {
982 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
983 /* On same disk, add it to this list and return */
984 thisjob->nextjob = jobs[j]->nextjob;
985 jobs[j]->nextjob = thisjob;
992 /* Loop until we start thisjob or until all existing jobs are finished */
993 while ( thisjob || (!partP && (numjobs > 0)) ) {
994 startjob = -1; /* No new job to start */
996 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
997 /* Either the max jobs are running or we have to wait for all
998 * the jobs to finish. In either case, we wait for at least one
999 * job to finish. When it's done, clean up after it.
1001 pid = wait(&wstatus);
1003 for (j=0; j<numjobs; j++) { /* Find which job it is */
1004 if (pid == jobs[j]->pid) break;
1006 assert(j < numjobs);
1007 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1008 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1011 numjobs--; /* job no longer running */
1012 oldjob = jobs[j]; /* remember */
1013 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1014 free(oldjob); /* free the old job */
1016 /* If there is another partition on the disk to salvage, then
1017 * say we will start it (startjob). If not, then put thisjob there
1018 * and say we will start it.
1020 if (jobs[j]) { /* Another partitions to salvage */
1021 startjob = j; /* Will start it */
1022 } else { /* There is not another partition to salvage */
1024 jobs[j] = thisjob; /* Add thisjob */
1026 startjob = j; /* Will start it */
1028 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1029 startjob = -1; /* Don't start it - already running */
1033 /* We don't have to wait for a job to complete */
1035 jobs[numjobs] = thisjob; /* Add this job */
1037 startjob = numjobs; /* Will start it */
1041 /* Start up a new salvage job on a partition in job slot "startjob" */
1042 if (startjob != -1) {
1044 Log("Starting salvage of file system partition %s\n",
1045 jobs[startjob]->partP->name);
1047 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1048 * commands and pass the child job number via the data path.
1050 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1051 jobs[startjob]->jobnumb);
1052 jobs[startjob]->pid = pid;
1057 jobs[startjob]->pid = pid;
1063 for (fd =0; fd < 16; fd++) close(fd);
1064 open("/", 0); dup2(0, 1); dup2(0, 2);
1065 #ifndef AFS_NT40_ENV
1067 openlog(NULL, LOG_PID, useSyslogFacility);
1071 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1072 logFile = fopen(logFileName, "w");
1074 if (!logFile) logFile = stdout;
1076 SalvageFileSys1(jobs[startjob]->partP, 0);
1081 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1083 /* If waited for all jobs to complete, now collect log files and return */
1084 #ifndef AFS_NT40_ENV
1085 if ( ! useSyslog ) /* if syslogging - no need to collect */
1088 for (i=0; i<jobcount; i++) {
1089 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1090 if (passLog = fopen(logFileName, "r")) {
1091 while(fgets(buf, sizeof(buf), passLog)) {
1092 fputs(buf, logFile);
1096 (void)unlink(logFileName);
1104 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1106 if (!canfork || debug || Fork() == 0) {
1107 SalvageFileSys1(partP, singleVolumeNumber);
1108 if (canfork && !debug) {
1114 Wait("SalvageFileSys");
1117 char *get_DevName(pbuffer, wpath)
1118 char *wpath, *pbuffer;
1120 char pbuf[128], *ptr;
1121 strcpy(pbuf, pbuffer);
1122 ptr = (char *)strrchr(pbuf, '/');
1125 strcpy(wpath, pbuf);
1128 ptr = (char *)strrchr(pbuffer, '/');
1130 strcpy(pbuffer, ptr+1);
1136 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1139 char inodeListPath[50];
1140 static char tmpDevName[100];
1141 static char wpath[100];
1142 struct VolumeSummary *vsp, *esp;
1145 fileSysPartition = partP;
1146 fileSysDevice = fileSysPartition->device;
1147 fileSysPathName = VPartitionPath(fileSysPartition);
1150 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1151 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1152 name = partP->devName;
1154 fileSysPath = fileSysPathName;
1155 strcpy(tmpDevName, partP->devName);
1156 name = get_DevName(tmpDevName, wpath);
1157 fileSysDeviceName = name;
1158 filesysfulldev = wpath;
1161 VLockPartition(partP->name);
1162 if (singleVolumeNumber || ForceSalvage)
1165 ForceSalvage = UseTheForceLuke(fileSysPath);
1167 if (singleVolumeNumber) {
1168 if (!VConnectFS()) {
1169 Abort("Couldn't connect to file server\n");
1171 AskOffline(singleVolumeNumber);
1174 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1176 Log("***Forced salvage of all volumes on this partition***\n");
1181 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1188 assert((dirp = opendir(fileSysPath)) != NULL);
1189 while (dp = readdir(dirp)) {
1190 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1191 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1193 Log("Removing old salvager temp files %s\n", dp->d_name);
1194 strcpy(npath, fileSysPath);
1196 strcat(npath, dp->d_name);
1202 tdir = (tmpdir ? tmpdir : fileSysPath);
1204 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1205 (void) strcpy(inodeListPath, _tempnam(tdir, "salvage.inodes."));
1207 sprintf(inodeListPath, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1209 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1210 unlink(inodeListPath);
1214 /* Using nt_unlink here since we're really using the delete on close
1215 * semantics of unlink. In most places in the salvager, we really do
1216 * mean to unlink the file at that point. Those places have been
1217 * modified to actually do that so that the NT crt can be used there.
1219 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1220 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1222 inodeFd = open(inodeListPath, O_RDONLY);
1223 unlink(inodeListPath);
1226 Abort("Temporary file %s is missing...\n",
1228 if (ListInodeOption) {
1232 /* enumerate volumes in the partition.
1233 figure out sets of read-only + rw volumes.
1234 salvage each set, read-only volumes first, then read-write.
1235 Fix up inodes on last volume in set (whether it is read-write
1238 GetVolumeSummary(singleVolumeNumber);
1240 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1241 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1242 for (j=i; j < nVolumesInInodeFile
1243 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1244 VolumeId vid = inodeSummary[j].volumeId;
1245 struct VolumeSummary *tsp;
1246 /* Scan volume list (from partition root directory) looking for the
1247 current rw volume number in the volume list from the inode scan.
1248 If there is one here that is not in the inode volume list,
1250 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1252 DeleteExtraVolumeHeaderFile(vsp);
1254 /* Now match up the volume summary info from the root directory with the
1255 entry in the volume list obtained from scanning inodes */
1256 inodeSummary[j].volSummary = NULL;
1257 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1258 if (tsp->header.id == vid) {
1259 inodeSummary[j].volSummary = tsp;
1265 /* Salvage the group of volumes (several read-only + 1 read/write)
1266 * starting with the current read-only volume we're looking at.
1268 SalvageVolumeGroup(&inodeSummary[i], j-i);
1271 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1272 for ( ; vsp<esp; vsp++) {
1274 DeleteExtraVolumeHeaderFile(vsp);
1277 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1278 RemoveTheForce(fileSysPath);
1280 if (!Testing && singleVolumeNumber) {
1281 AskOnline(singleVolumeNumber, fileSysPartition->name);
1283 /* Step through the volumeSummary list and set all volumes on-line.
1284 * The volumes were taken off-line in GetVolumeSummary.
1286 for (j=0; j<nVolumes; j++) {
1287 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1292 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1293 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1296 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1299 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1301 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1302 vsp->fileName, (Testing? "would have been ":""));
1304 unlink(vsp->fileName);
1308 CompareInodes(_p1,_p2)
1309 const void *_p1,*_p2;
1311 register const struct ViceInodeInfo *p1 = _p1;
1312 register const struct ViceInodeInfo *p2 = _p2;
1313 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1314 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1315 VolumeId p1rwid, p2rwid;
1316 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1317 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1318 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1319 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1320 if (p1rwid < p2rwid)
1322 if (p1rwid > p2rwid)
1324 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1325 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1326 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1327 return (p1->u.special.type < p2->u.special.type? -1: 1);
1328 if (p1->u.vnode.volumeId == p1rwid)
1330 if (p2->u.vnode.volumeId == p2rwid)
1332 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1334 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1335 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1336 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1338 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1340 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1342 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1344 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1346 /* The following tests are reversed, so that the most desirable
1347 of several similar inodes comes first */
1348 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1349 #ifdef AFS_3DISPARES
1350 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1351 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1354 #ifdef AFS_SGI_EXMAG
1355 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1356 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1361 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1362 #ifdef AFS_3DISPARES
1363 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1364 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1367 #ifdef AFS_SGI_EXMAG
1368 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1369 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1374 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1375 #ifdef AFS_3DISPARES
1376 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1377 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1380 #ifdef AFS_SGI_EXMAG
1381 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1382 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1387 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1388 #ifdef AFS_3DISPARES
1389 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1390 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1393 #ifdef AFS_SGI_EXMAG
1394 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1395 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1403 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1404 register struct InodeSummary * summary)
1406 int volume = ip->u.vnode.volumeId;
1407 int rwvolume = volume;
1408 register n, nSpecial;
1409 register Unique maxunique;
1412 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1414 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1416 rwvolume = ip->u.special.parentId;
1417 /* This isn't quite right, as there could (in error) be different
1418 parent inodes in different special vnodes */
1421 if (maxunique < ip->u.vnode.vnodeUniquifier)
1422 maxunique = ip->u.vnode.vnodeUniquifier;
1426 summary->volumeId = volume;
1427 summary->RWvolumeId = rwvolume;
1428 summary->nInodes =n;
1429 summary->nSpecialInodes = nSpecial;
1430 summary->maxUniquifier = maxunique;
1433 int OnlyOneVolume(inodeinfo, singleVolumeNumber)
1434 struct ViceInodeInfo *inodeinfo;
1435 VolumeId singleVolumeNumber;
1437 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1438 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1439 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1444 * Collect list of inodes in file named by path. If a truly fatal error,
1445 * unlink the file and abort. For lessor errors, return -1. The file will
1446 * be unlinked by the caller.
1448 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1450 #ifdef AFS_LARGEFILE_ENV
1451 struct stat64 status;
1452 #else /* !AFS_LARGEFILE_ENV */
1454 #endif /* !AFS_LARGEFILE_ENV */
1456 struct ViceInodeInfo *ip;
1457 struct InodeSummary summary;
1458 char summaryFileName[50];
1461 char *dev = fileSysPath;
1462 char *wpath = fileSysPath;
1464 char *dev = fileSysDeviceName;
1465 char *wpath = filesysfulldev;
1467 char *part = fileSysPath;
1470 /* This file used to come from vfsck; cobble it up ourselves now... */
1471 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1473 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1478 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1480 if (forceSal && !ForceSalvage) {
1481 Log("***Forced salvage of all volumes on this partition***\n");
1484 inodeFd = open(path, O_RDWR);
1485 if (inodeFd == -1 ||
1486 #ifdef AFS_LARGEFILE_ENV
1487 fstat64(inodeFd, &status)
1488 #else /* !AFS_LARGEFILE_ENV */
1489 fstat(inodeFd, &status)
1490 #endif /* !AFS_LARGEFILE_ENV */
1493 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1495 tdir = (tmpdir ? tmpdir : part);
1497 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1498 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1500 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1502 summaryFile = fopen(summaryFileName, "a+");
1503 if (summaryFile == NULL) {
1506 Abort("Unable to create inode summary file\n");
1508 if (!canfork || debug || Fork() == 0) {
1510 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1512 fclose(summaryFile); close(inodeFd);
1513 unlink(summaryFileName);
1514 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1515 RemoveTheForce(fileSysPath);
1516 Log("%s vice inodes on %s; not salvaged\n",
1517 singleVolumeNumber? "No applicable": "No", dev);
1520 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1522 fclose(summaryFile); close(inodeFd);
1524 unlink(summaryFileName);
1525 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1527 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1528 fclose(summaryFile); close(inodeFd);
1530 unlink(summaryFileName);
1531 Abort("Unable to read inode table; %s not salvaged\n", dev);
1533 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1535 #ifdef AFS_LARGEFILE_ENV
1536 lseek64(inodeFd, (off64_t) 0, SEEK_SET) == -1
1537 #else /* !AFS_LARGEFILE_ENV */
1538 lseek(inodeFd, (off_t) 0, SEEK_SET) == -1
1539 #endif /* !AFS_LARGEFILE_ENV */
1540 || write(inodeFd, ip, status.st_size) != status.st_size) {
1541 fclose(summaryFile); close(inodeFd);
1543 unlink(summaryFileName);
1544 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1548 CountVolumeInodes(ip, nInodes, &summary);
1549 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1550 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1551 fclose(summaryFile); close(inodeFd);
1554 summary.index += (summary.nInodes);
1555 nInodes -= summary.nInodes;
1556 ip += summary.nInodes;
1558 /* Following fflush is not fclose, because if it was debug mode would not work */
1559 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1560 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1561 fclose(summaryFile); close(inodeFd);
1564 if (canfork && !debug) {
1570 if (Wait("Inode summary") == -1) {
1571 fclose(summaryFile); close(inodeFd);
1573 unlink(summaryFileName);
1574 Exit(1); /* salvage of this partition aborted */
1577 #ifdef AFS_LARGEFILE_ENV
1578 assert(fstat64(fileno(summaryFile), &status) != -1);
1579 #else /* !AFS_LARGEFILE_ENV */
1580 assert(fstat(fileno(summaryFile), &status) != -1);
1581 #endif /* !AFS_LARGEFILE_ENV */
1582 if ( status.st_size != 0 ) {
1584 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1585 assert(inodeSummary != NULL);
1586 /* For GNU we need to do lseek to get the file pointer moved. */
1587 #ifdef AFS_LARGEFILE_ENV
1588 assert(lseek64(fileno(summaryFile), (off64_t) 0, SEEK_SET) == 0);
1589 #else /* !AFS_LARGEFILE_ENV */
1590 assert(lseek(fileno(summaryFile), (off_t) 0, SEEK_SET) == 0);
1591 #endif /* !AFS_LARGEFILE_ENV */
1592 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1593 assert(ret == status.st_size);
1595 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1596 fclose(summaryFile);
1598 unlink(summaryFileName);
1602 /* Comparison routine for volume sort.
1603 This is setup so that a read-write volume comes immediately before
1604 any read-only clones of that volume */
1605 CompareVolumes(_p1,_p2)
1606 const void *_p1,*_p2;
1608 register const struct VolumeSummary *p1 = _p1;
1609 register const struct VolumeSummary *p2 = _p2;
1610 if (p1->header.parent != p2->header.parent)
1611 return p1->header.parent < p2->header.parent? -1: 1;
1612 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1614 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1616 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1619 void GetVolumeSummary(VolumeId singleVolumeNumber)
1622 afs_int32 nvols = 0;
1623 struct VolumeSummary *vsp, vs;
1624 struct VolumeDiskHeader diskHeader;
1627 /* Get headers from volume directory */
1628 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1629 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1630 if (!singleVolumeNumber) {
1631 while (dp = readdir(dirp)) {
1632 char *p = dp->d_name;
1633 p = strrchr(dp->d_name, '.');
1634 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1636 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1637 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1638 == sizeof (diskHeader) &&
1639 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1640 DiskToVolumeHeader(&vs.header, &diskHeader);
1647 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1651 if (!nvols) nvols = 1;
1652 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1654 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1655 assert(volumeSummaryp != NULL);
1658 vsp = volumeSummaryp;
1659 while (dp = readdir(dirp)) {
1660 char *p = dp->d_name;
1661 p = strrchr(dp->d_name, '.');
1662 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1665 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1666 || read(fd, &diskHeader, sizeof (diskHeader))
1667 != sizeof (diskHeader)
1668 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1673 if (!singleVolumeNumber) {
1674 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1675 dp->d_name, (Testing? "it would have been ":""));
1681 char nameShouldBe[64];
1682 DiskToVolumeHeader(&vsp->header, &diskHeader);
1683 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1684 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1687 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1688 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1689 if (singleVolumeNumber)
1690 AskOffline(vsp->header.id);
1691 if (strcmp(nameShouldBe, dp->d_name)) {
1692 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 ":""));
1697 vsp->fileName = ToString(dp->d_name);
1707 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1710 /* Find the link table. This should be associated with the RW volume or, if
1711 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1713 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1714 struct ViceInodeInfo *allInodes)
1717 struct ViceInodeInfo *ip;
1719 for (i=0; i<nVols; i++) {
1720 ip = allInodes + isp[i].index;
1721 for (j=0; j<isp[i].nSpecialInodes; j++) {
1722 if (ip[j].u.special.type == VI_LINKTABLE)
1723 return ip[j].inodeNumber;
1729 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1731 struct versionStamp version;
1734 if (!VALID_INO(ino))
1735 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1736 isp->volumeId, INODESPECIAL,
1737 VI_LINKTABLE, isp->RWvolumeId);
1738 if (!VALID_INO(ino))
1739 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1740 isp->RWvolumeId, errno);
1741 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1742 fdP = IH_OPEN(VGLinkH);
1744 Abort("Can't open link table for volume %u (error = %d)\n",
1745 isp->RWvolumeId, errno);
1747 if (FDH_TRUNC(fdP, 0)<0)
1748 Abort("Can't truncate link table for volume %u (error = %d)\n",
1749 isp->RWvolumeId, errno);
1751 version.magic = LINKTABLEMAGIC;
1752 version.version = LINKTABLEVERSION;
1754 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1756 Abort("Can't truncate link table for volume %u (error = %d)\n",
1757 isp->RWvolumeId, errno);
1759 FDH_REALLYCLOSE(fdP);
1761 /* If the volume summary exits (i.e., the V*.vol header file exists),
1762 * then set this inode there as well.
1764 if (isp->volSummary)
1765 isp->volSummary->header.linkTable = ino;
1771 void *nt_SVG(void *arg)
1773 SVGParms_t *parms = (SVGParms_t*)arg;
1774 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1778 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1781 pthread_attr_t tattr;
1785 /* Initialize per volume global variables, even if later code does so */
1789 memset(&VolInfo, 0, sizeof(VolInfo));
1791 parms.svgp_inodeSummaryp = isp;
1792 parms.svgp_count = nVols;
1793 code = pthread_attr_init(&tattr);
1795 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1799 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1801 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1805 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1807 Log("Failed to create thread to salvage volume group %u\n",
1811 (void) pthread_join(tid, NULL);
1813 #endif /* AFS_NT40_ENV */
1815 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1817 struct ViceInodeInfo *inodes,*allInodes,*ip;
1818 int i, totalInodes, size, salvageTo;
1822 int dec_VGLinkH = 0;
1824 FdHandle_t *fdP = NULL;
1827 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1828 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1829 if (!ForceSalvage && QuickCheck(isp, nVols))
1832 if (ShowMounts && !haveRWvolume)
1834 if (canfork && !debug && Fork() != 0) {
1835 (void) Wait("Salvage volume group");
1838 for (i = 0, totalInodes = 0; i<nVols; i++)
1839 totalInodes += isp[i].nInodes;
1840 size = totalInodes * sizeof (struct ViceInodeInfo);
1841 inodes = (struct ViceInodeInfo *) malloc(size);
1842 allInodes = inodes - isp->index; /* this would the base of all the inodes
1843 for the partition, if all the inodes
1844 had been read into memory */
1845 #ifdef AFS_LARGEFILE_ENV
1846 assert(lseek64(inodeFd,(off64_t)(isp->index*sizeof(struct ViceInodeInfo)),SEEK_SET) != -1)
1847 #else /* !AFS_LARGEFILE_ENV */
1848 assert(lseek(inodeFd,(off_t)(isp->index*sizeof(struct ViceInodeInfo)),SEEK_SET) != -1)
1849 #endif /* !AFS_LARGEFILE_ENV */
1850 assert(read(inodeFd,inodes,size) == size)
1852 /* Don't try to salvage a read write volume if there isn't one on this
1854 salvageTo = haveRWvolume? 0:1;
1856 #ifdef AFS_NAMEI_ENV
1857 ino = FindLinkHandle(isp, nVols, allInodes);
1858 if (VALID_INO(ino)) {
1859 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1860 fdP = IH_OPEN(VGLinkH);
1862 if (!VALID_INO(ino) || fdP == NULL) {
1863 Log("%s link table for volume %u.\n",
1864 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1866 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1869 CreateLinkTable(isp, ino);
1873 FDH_REALLYCLOSE(fdP);
1875 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1878 /* Salvage in reverse order--read/write volume last; this way any
1879 Inodes not referenced by the time we salvage the read/write volume
1880 can be picked up by the read/write volume */
1881 /* ACTUALLY, that's not done right now--the inodes just vanish */
1882 for (i = nVols-1; i>=salvageTo; i--) {
1884 struct InodeSummary *lisp = &isp[i];
1885 #ifdef AFS_NAMEI_ENV
1886 /* If only the RO is present on this partition, the link table
1887 * shows up as a RW volume special file. Need to make sure the
1888 * salvager doesn't try to salvage the non-existent RW.
1890 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1891 /* If this only special inode is the link table, continue */
1892 if (inodes->u.special.type == VI_LINKTABLE) {
1898 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1899 lisp->volumeId, (Testing?"(READONLY mode)":""));
1900 /* Check inodes twice. The second time do things seriously. This
1901 way the whole RO volume can be deleted, below, if anything goes wrong */
1902 for (check = 1; check>=0; check--) {
1904 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1905 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1906 if (rw && deleteMe) {
1907 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1908 volume won't be called */
1914 if (rw && check == 1)
1916 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1917 MaybeZapVolume(lisp,"Vnode index", 0, check);
1923 /* Fix actual inode counts */
1925 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1926 static int TraceBadLinkCounts = 0;
1927 #ifdef AFS_NAMEI_ENV
1928 if (VGLinkH->ih_ino == ip->inodeNumber) {
1929 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1930 VGLinkH_p1 = ip->u.param[0];
1931 continue; /* Deal with this last. */
1934 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1935 TraceBadLinkCounts--; /* Limit reports, per volume */
1936 #ifdef AFS_LARGEFILE_ENV
1937 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size (0X%x,0x%x), p=(%u,%u,%u,%u)\n",
1938 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1939 (unsigned) ((ip->byteCount) >> 32),
1940 (unsigned) ((ip->byteCount) & 0xffffffff),
1941 ip->u.param[0], ip->u.param[1],
1942 ip->u.param[2], ip->u.param[3]);
1944 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1945 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1946 ip->byteCount, ip->u.param[0], ip->u.param[1],
1947 ip->u.param[2], ip->u.param[3]);
1948 #endif /* !AFS_LARGEFILE_ENV */
1950 while (ip->linkCount > 0) {
1951 /* below used to assert, not break */
1953 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1954 Log ("idec failed. inode %s errno %d\n",
1955 PrintInode(NULL, ip->inodeNumber), errno);
1961 while (ip->linkCount < 0) {
1962 /* these used to be asserts */
1964 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1965 Log ("iinc failed. inode %s errno %d\n",
1966 PrintInode(NULL, ip->inodeNumber) ,errno);
1973 #ifdef AFS_NAMEI_ENV
1974 while (dec_VGLinkH > 0) {
1975 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1976 Log("idec failed on link table, errno = %d\n", errno);
1980 while (dec_VGLinkH < 0) {
1981 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1982 Log("iinc failed on link table, errno = %d\n", errno);
1989 /* Directory consistency checks on the rw volume */
1991 SalvageVolume(isp, VGLinkH);
1992 IH_RELEASE(VGLinkH);
1994 if (canfork && !debug) {
2000 int QuickCheck(register struct InodeSummary *isp, int nVols)
2002 /* Check headers BEFORE forking */
2006 for (i = 0; i<nVols; i++) {
2007 struct VolumeSummary *vs = isp[i].volSummary;
2008 VolumeDiskData volHeader;
2010 /* Don't salvage just because phantom rw volume is there... */
2011 /* (If a read-only volume exists, read/write inodes must also exist) */
2012 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
2016 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2017 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
2018 == sizeof(volHeader)
2019 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2020 && volHeader.dontSalvage == DONT_SALVAGE
2021 && volHeader.needsSalvaged == 0
2022 && volHeader.destroyMe == 0) {
2023 if (volHeader.inUse == 1) {
2024 volHeader.inUse = 0;
2025 volHeader.inService = 1;
2027 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
2028 != sizeof(volHeader)) {
2045 /* SalvageVolumeHeaderFile
2047 * Salvage the top level V*.vol header file. Make sure the special files
2048 * exist and that there are no duplicates.
2050 * Calls SalvageHeader for each possible type of volume special file.
2053 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2054 register struct ViceInodeInfo *inodes,
2055 int RW, int check, int *deleteMe)
2059 register struct ViceInodeInfo *ip;
2060 int allinodesobsolete = 1;
2061 struct VolumeDiskHeader diskHeader;
2065 memset(&tempHeader, 0, sizeof(tempHeader));
2066 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2067 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2068 tempHeader.id = isp->volumeId;
2069 tempHeader.parent = isp->RWvolumeId;
2070 /* Check for duplicates (inodes are sorted by type field) */
2071 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2072 ip = &inodes[isp->index+i];
2073 if (ip->u.special.type == (ip+1)->u.special.type) {
2074 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2078 for (i = 0; i<isp->nSpecialInodes; i++) {
2079 ip = &inodes[isp->index+i];
2080 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2082 Log("Rubbish header inode\n");
2085 Log("Rubbish header inode; deleted\n");
2087 else if (!stuff[ip->u.special.type-1].obsolete) {
2088 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2089 if (!check && ip->u.special.type != VI_LINKTABLE)
2090 ip->linkCount--; /* Keep the inode around */
2091 allinodesobsolete = 0;
2095 if (allinodesobsolete) {
2102 VGLinkH_cnt ++; /* one for every header. */
2104 if (!RW && !check && isp->volSummary) {
2105 ClearROInUseBit(isp->volSummary);
2109 for (i = 0; i< MAXINODETYPE; i++) {
2110 if (stuff[i].inodeType == VI_LINKTABLE) {
2111 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2112 * And we may have recreated the link table earlier, so set the
2113 * RW header as well.
2115 if (VALID_INO(VGLinkH->ih_ino)) {
2116 *stuff[i].inode = VGLinkH->ih_ino;
2120 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2124 if (isp->volSummary == NULL) {
2126 sprintf(name, VFORMAT, isp->volumeId);
2128 Log("No header file for volume %u\n", isp->volumeId);
2131 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2132 isp->volumeId, (Testing?"it would have been ":""),
2133 fileSysPathName, name);
2134 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2135 assert(headerFd != -1)
2136 isp->volSummary = (struct VolumeSummary *)
2137 malloc(sizeof(struct VolumeSummary));
2138 isp->volSummary->fileName = ToString(name);
2142 /* hack: these two fields are obsolete... */
2143 isp->volSummary->header.volumeAcl = 0;
2144 isp->volSummary->header.volumeMountTable = 0;
2146 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2147 /* We often remove the name before calling us, so we make a fake one up */
2148 if (isp->volSummary->fileName) {
2149 strcpy(name, isp->volSummary->fileName);
2151 sprintf(name, VFORMAT, isp->volumeId);
2152 isp->volSummary->fileName = ToString(name);
2155 Log("Header file %s is damaged or no longer valid%s\n",
2156 name, (check ? "" : "; repairing"));
2160 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2161 assert(headerFd != -1)
2165 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2167 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2169 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2170 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2171 != sizeof(struct VolumeDiskHeader)) {
2172 Log("Couldn't rewrite volume header file!\n");
2179 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2180 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2184 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2185 int check, int *deleteMe)
2188 VolumeDiskData volumeInfo;
2189 struct versionStamp fileHeader;
2198 #ifndef AFS_NAMEI_ENV
2199 if ( sp->inodeType == VI_LINKTABLE)
2202 if (*(sp->inode) == 0) {
2204 Log("Missing inode in volume header (%s)\n", sp->description);
2207 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2208 sp->description, (Testing ? "it would have recreated it": "recreating"));
2210 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2211 isp->volumeId, INODESPECIAL,
2212 sp->inodeType, isp->RWvolumeId);
2213 if (!VALID_INO(*(sp->inode)))
2214 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2215 sp->description, errno);
2220 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2221 fdP = IH_OPEN(specH);
2222 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2223 /* bail out early and destroy the volume */
2224 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2226 if (deleteMe) *deleteMe = 1;
2231 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2232 sp->description, errno);
2235 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2236 || header.fileHeader.magic != sp->stamp.magic)) {
2238 Log("Part of the header (%s) is corrupted\n", sp->description);
2239 FDH_REALLYCLOSE(fdP);
2243 Log("Part of the header (%s) is corrupted; recreating\n",
2247 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2250 FDH_REALLYCLOSE(fdP);
2254 if (recreate && !Testing) {
2256 Abort("Internal error: recreating volume header (%s) in check mode\n",
2258 code = FDH_TRUNC(fdP, 0);
2260 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2261 sp->description, errno);
2263 /* The following code should be moved into vutil.c */
2264 if (sp->inodeType == VI_VOLINFO) {
2266 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2267 header.volumeInfo.stamp = sp->stamp;
2268 header.volumeInfo.id = isp->volumeId;
2269 header.volumeInfo.parentId = isp->RWvolumeId;
2270 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2271 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2272 header.volumeInfo.inService = 0;
2273 header.volumeInfo.blessed = 0;
2274 /* The + 1000 is a hack in case there are any files out in venus caches */
2275 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2276 header.volumeInfo.type =
2277 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2278 header.volumeInfo.needsCallback = 0;
2279 gettimeofday(&tp,0);
2280 header.volumeInfo.creationDate = tp.tv_sec;
2281 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2282 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2284 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2285 sizeof(header.volumeInfo));
2286 if (code != sizeof(header.volumeInfo)) {
2288 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2289 sp->description, errno);
2290 Abort("Unable to write entire volume header file (%s)\n",
2295 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2296 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2298 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2299 if (code != sizeof(sp->stamp)) {
2301 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2302 sp->description, errno);
2303 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2308 FDH_REALLYCLOSE(fdP);
2310 if (sp->inodeType == VI_VOLINFO) {
2311 VolInfo = header.volumeInfo;
2314 if (VolInfo.updateDate) {
2315 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2316 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2317 (Testing?"it would have been ":""), update);
2319 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2320 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2329 int SalvageVnodes(register struct InodeSummary *rwIsp,
2330 register struct InodeSummary * thisIsp,
2331 register struct ViceInodeInfo * inodes, int check)
2333 int ilarge, ismall, ioffset, RW, nInodes;
2334 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2335 if (Showmode) return 0;
2336 RW = (rwIsp == thisIsp);
2337 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2338 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2339 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2340 if (check && ismall == -1)
2342 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2343 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2344 return (ilarge==0 && ismall==0 ? 0: -1);
2347 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2348 register struct ViceInodeInfo *ip,
2349 int nInodes, struct VolumeSummary *volSummary, int check)
2351 VolumeId volumeNumber;
2352 char buf[SIZEOF_LARGEDISKVNODE];
2353 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2355 StreamHandle_t *file;
2356 struct VnodeClassInfo *vcp;
2357 afs_size_t size, vnodeSize;
2358 int vnodeIndex, nVnodes;
2359 afs_ino_str_t stmp1, stmp2;
2363 volumeNumber = volSummary->header.id;
2364 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2365 fdP = IH_OPEN(handle);
2366 assert(fdP != NULL);
2367 file = FDH_FDOPEN(fdP, "r+");
2368 assert(file != NULL)
2369 vcp = &VnodeClassInfo[class];
2370 size = OS_SIZE(fdP->fd_fd);
2372 nVnodes = (size / vcp->diskSize) - 1;
2374 assert((nVnodes+1) * vcp->diskSize == size)
2375 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
2380 for (vnodeIndex = 0;
2381 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2382 nVnodes--, vnodeIndex++) {
2383 if (vnode->type != vNull) {
2384 int vnodeChanged = 0;
2385 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2386 /* Log programs that belong to root (potentially suid root);
2387 don't bother for read-only or backup volumes */
2388 #ifdef notdef /* This is done elsewhere */
2389 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2390 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2391 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2392 vnode->owner, vnode->modeBits);
2394 if (VNDISK_GET_INO(vnode) == 0) {
2396 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2397 memset(vnode, 0, vcp->diskSize);
2402 if (vcp->magic != vnode->vnodeMagic) {
2403 /* bad magic #, probably partially created vnode */
2404 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2405 memset(vnode, 0, vcp->diskSize);
2409 /* ****** Should do a bit more salvage here: e.g. make sure
2410 vnode type matches what it should be given the index */
2411 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2412 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2413 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2414 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2421 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2422 /* The following doesn't work, because the version number
2423 is not maintained correctly by the file server */
2424 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2425 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2427 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2434 /* For RW volume, look for vnode with matching inode number;
2435 if no such match, take the first determined by our sort
2437 register struct ViceInodeInfo *lip = ip;
2438 register lnInodes = nInodes;
2439 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2440 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2449 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2450 /* "Matching" inode */
2454 vu = vnode->uniquifier;
2455 iu = ip->u.vnode.vnodeUniquifier;
2456 vd = vnode->dataVersion;
2457 id = ip->u.vnode.inodeDataVersion;
2459 * Because of the possibility of the uniquifier overflows (> 4M)
2460 * we compare them modulo the low 22-bits; we shouldn't worry
2461 * about mismatching since they shouldn't to many old
2462 * uniquifiers of the same vnode...
2464 if (IUnique(vu) != IUnique(iu)) {
2466 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2467 vnodeNumber, IUnique(vu), IUnique(iu));
2470 vnode->uniquifier = iu;
2471 #ifdef AFS_3DISPARES
2472 vnode->dataVersion = (id >= vd ?
2473 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2474 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2476 #if defined(AFS_SGI_EXMAG)
2477 vnode->dataVersion = (id >= vd ?
2478 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2479 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2481 vnode->dataVersion = (id>vd ? id:vd);
2482 #endif /* AFS_SGI_EXMAG */
2483 #endif /* AFS_3DISPARES */
2487 /* don't bother checking for vd > id any more, since
2488 partial file transfers always result in this state,
2489 and you can't do much else anyway (you've already
2490 found the best data you can) */
2491 #ifdef AFS_3DISPARES
2492 if (!vnodeIsDirectory(vnodeNumber) &&
2493 ((vd < id && (id-vd) < 1887437) ||
2494 ((vd > id && (vd-id) > 1887437)))) {
2496 #if defined(AFS_SGI_EXMAG)
2497 if (!vnodeIsDirectory(vnodeNumber) &&
2498 ((vd < id && (id-vd) < 15099494) ||
2499 ((vd > id && (vd-id) > 15099494)))) {
2501 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2502 #endif /* AFS_SGI_EXMAG */
2504 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2505 vnode->dataVersion = id;
2510 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2513 #ifdef AFS_LARGEFILE_ENV
2514 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=(0X%x,0X%x)\n",
2516 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2517 PrintInode(stmp2, ip->inodeNumber),
2518 (unsigned) (ip->byteCount >> 32),
2519 (unsigned) (ip->byteCount & 0xffffffff));
2521 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2523 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2524 PrintInode(stmp2, ip->inodeNumber),
2526 #endif /* !AFS_LARGEFILE_ENV */
2528 VNDISK_SET_INO(vnode, ip->inodeNumber);
2533 #ifdef AFS_LARGEFILE_ENV
2534 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=(0X%x,0X%x)\n",
2536 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2537 PrintInode(stmp2, ip->inodeNumber),
2538 (unsigned) (ip->byteCount >> 32),
2539 (unsigned) (ip->byteCount & 0xffffffff));
2541 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2543 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2544 PrintInode(stmp2, ip->inodeNumber),
2546 #endif /* !AFS_LARGEFILE_ENV */
2548 VNDISK_SET_INO(vnode, ip->inodeNumber);
2551 VNDISK_GET_LEN(vnodeSize, vnode);
2552 if (ip->byteCount != vnodeSize) {
2555 #ifdef AFS_LARGEFILE_ENV
2556 Log("Vnode %d: length incorrect; (is (0X%x,0X%x) should be (0X%x,0X%x))\n",
2558 (unsigned) (vnodeSize >> 32),
2559 (unsigned) (vnodeSize & 0xffffffff),
2560 (unsigned) (ip->byteCount >> 32),
2561 (unsigned) (ip->byteCount & 0xffffffff));
2563 Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2564 vnodeNumber, vnodeSize, ip->byteCount);
2565 #endif /* !AFS_LARGEFILE_ENV */
2570 #ifdef AFS_LARGEFILE_ENV
2571 Log("Vnode %d: length incorrect; changed from (0X%x,0X%x) to (0X%x,0X%x)\n",
2573 (unsigned) (vnodeSize >> 32),
2574 (unsigned) (vnodeSize & 0xffffffff),
2575 (unsigned) (ip->byteCount >> 32),
2576 (unsigned) (ip->byteCount & 0xffffffff));
2578 Log("Vnode %d: length incorrect; changed from %d to %d\n",
2579 vnodeNumber, vnodeSize, ip->byteCount);
2580 #endif /* !AFS_LARGEFILE_ENV */
2581 VNDISK_SET_LEN(vnode, ip->byteCount);
2585 ip->linkCount--; /* Keep the inode around */
2589 else { /* no matching inode */
2590 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2591 /* No matching inode--get rid of the vnode */
2593 if (VNDISK_GET_INO(vnode)) {
2595 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2596 vnodeNumber, vnode->uniquifier,
2597 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2600 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2605 if (VNDISK_GET_INO(vnode)) {
2607 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2608 vnodeNumber, vnode->uniquifier,
2609 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2610 ctime((time_t *)&(vnode->serverModifyTime)));
2613 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)));
2615 memset(vnode, 0, vcp->diskSize);
2618 /* Should not reach here becuase we checked for
2619 * (inodeNumber == 0) above. And where we zero the vnode,
2620 * we also goto vnodeDone.
2624 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2628 } /* VNDISK_GET_INO(vnode) != 0 */
2630 assert(!(vnodeChanged && check));
2631 if (vnodeChanged && !Testing) {
2632 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2633 (char*)vnode, vcp->diskSize)
2635 VolumeChanged = 1; /* For break call back */
2646 struct VnodeEssence *CheckVnodeNumber(vnodeNumber)
2647 VnodeId vnodeNumber;
2650 struct VnodeInfo *vip;
2653 class = vnodeIdToClass(vnodeNumber);
2654 vip = &vnodeInfo[class];
2655 offset = vnodeIdToBitNumber(vnodeNumber);
2656 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2660 void CopyOnWrite(register struct DirSummary *dir)
2662 /* Copy the directory unconditionally if we are going to change it:
2663 * not just if was cloned.
2665 struct VnodeDiskObject vnode;
2666 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2667 Inode oldinode, newinode;
2670 if (dir->copied || Testing)
2672 DFlush(); /* Well justified paranoia... */
2674 code = IH_IREAD(vnodeInfo[vLarge].handle,
2675 vnodeIndexOffset(vcp, dir->vnodeNumber),
2676 (char*)&vnode, sizeof (vnode));
2677 assert(code == sizeof(vnode));
2678 oldinode = VNDISK_GET_INO(&vnode);
2679 /* Increment the version number by a whole lot to avoid problems with
2680 * clients that were promised new version numbers--but the file server
2681 * crashed before the versions were written to disk.
2683 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2684 dir->rwVid, dir->vnodeNumber,
2685 vnode.uniquifier, vnode.dataVersion += 200);
2686 assert(VALID_INO(newinode));
2687 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2689 VNDISK_SET_INO(&vnode, newinode);
2690 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2691 vnodeIndexOffset(vcp, dir->vnodeNumber),
2692 (char*)&vnode, sizeof (vnode));
2693 assert(code == sizeof (vnode));
2695 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2696 fileSysDevice, newinode);
2697 /* Don't delete the original inode right away, because the directory is
2698 * still being scanned.
2703 /* This function should either successfully create a new dir, or give up and leave
2704 * things the way they were. In particular, if it fails to write the new dir properly,
2705 * it should return w/o changing the reference to the old dir.
2707 void CopyAndSalvage(register struct DirSummary *dir)
2709 struct VnodeDiskObject vnode;
2710 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2711 Inode oldinode, newinode;
2713 register afs_int32 code;
2714 afs_int32 parentUnique= 1;
2715 struct VnodeEssence *vnodeEssence;
2719 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2720 code = IH_IREAD(vnodeInfo[vLarge].handle,
2721 vnodeIndexOffset(vcp, dir->vnodeNumber),
2722 (char*)&vnode, sizeof (vnode));
2723 assert(code == sizeof (vnode));
2724 oldinode = VNDISK_GET_INO(&vnode);
2725 /* Increment the version number by a whole lot to avoid problems with
2726 * clients that were promised new version numbers--but the file server
2727 * crashed before the versions were written to disk.
2729 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2730 dir->rwVid, dir->vnodeNumber,
2732 vnode.dataVersion += 200);
2733 assert(VALID_INO(newinode));
2734 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2736 /* Assign . and .. vnode numbers from dir and vnode.parent.
2737 * The uniquifier for . is in the vnode.
2738 * The uniquifier for .. might be set to a bogus value of 1 and
2739 * the salvager will later clean it up.
2741 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2742 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2744 code = DirSalvage(&dir->dirHandle, &newdir,
2745 dir->vnodeNumber, vnode.uniquifier,
2746 (vnode.parent?vnode.parent:dir->vnodeNumber),
2748 if (code == 0) code = DFlush();
2750 /* didn't really build the new directory properly, let's just give up. */
2751 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2753 Log("Directory salvage returned code %d, continuing.\n", code);
2756 Log("Checking the results of the directory salvage...\n");
2757 if (!DirOK(&newdir)) {
2758 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2759 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2764 VNDISK_SET_INO(&vnode, newinode);
2765 VNDISK_SET_LEN(&vnode, (afs_size_t) Length(&newdir));
2766 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2767 vnodeIndexOffset(vcp, dir->vnodeNumber),
2768 (char*)&vnode, sizeof (vnode));
2769 assert(code == sizeof (vnode));
2771 nt_sync(fileSysDevice);
2773 sync(); /* this is slow, but hopefully rarely called. We don't have
2774 * an open FD on the file itself to fsync.
2777 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2779 dir->dirHandle = newdir;
2782 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2785 struct VnodeEssence *vnodeEssence;
2786 afs_int32 dirOrphaned, todelete;
2788 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2790 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2791 if (vnodeEssence == NULL) {
2793 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2794 dir->vnodeNumber, dir->name?dir->name:"??",
2795 name, vnodeNumber, unique);
2799 assert(Delete(&dir->dirHandle, name) == 0)
2805 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2806 * mount inode for the partition. If this inode were deleted, it would crash
2809 if (vnodeEssence->InodeNumber == 0) {
2810 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2811 dir->vnodeNumber, (dir->name?dir->name:"??"),
2812 name, vnodeNumber, unique,
2813 (Testing?"-- would have deleted":" -- deleted"));
2816 assert(Delete(&dir->dirHandle, name) == 0);
2822 if (!(vnodeNumber & 1) && !Showmode &&
2823 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2824 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2825 dir->vnodeNumber, (dir->name?dir->name:"??"),
2826 name, vnodeNumber, unique,
2827 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2831 assert(Delete(&dir->dirHandle, name) == 0);
2837 /* Check if the Uniquifiers match. If not, change the directory entry
2838 * so its unique matches the vnode unique. Delete if the unique is zero
2839 * or if the directory is orphaned.
2841 if (!vnodeEssence->unique ||
2842 (vnodeEssence->unique) != unique) {
2843 if (!vnodeEssence->unique &&
2844 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2845 /* This is an orphaned directory. Don't delete the . or ..
2846 * entry. Otherwise, it will get created in the next
2847 * salvage and deleted again here. So Just skip it.
2852 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2855 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2856 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2857 name, vnodeNumber, unique, vnodeEssence->unique,
2858 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2862 fid.Vnode = vnodeNumber;
2863 fid.Unique = vnodeEssence->unique;
2865 assert(Delete(&dir->dirHandle, name) == 0)
2867 assert(Create(&dir->dirHandle, name, &fid) == 0)
2869 if (todelete) return; /* no need to continue */
2872 if (strcmp(name,".") == 0) {
2873 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2875 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2876 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2879 assert(Delete(&dir->dirHandle, ".") == 0)
2880 fid.Vnode = dir->vnodeNumber;
2881 fid.Unique = dir->unique;
2882 assert(Create(&dir->dirHandle, ".", &fid) == 0)
2885 vnodeNumber = fid.Vnode; /* Get the new Essence */
2886 unique = fid.Unique;
2887 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2891 else if (strcmp(name,"..") == 0) {
2894 struct VnodeEssence *dotdot;
2895 pa.Vnode = dir->parent;
2896 dotdot = CheckVnodeNumber(pa.Vnode);
2897 assert (dotdot != NULL); /* XXX Should not be assert */
2898 pa.Unique = dotdot->unique;
2901 pa.Vnode = dir->vnodeNumber;
2902 pa.Unique = dir->unique;
2904 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2905 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2906 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2909 assert(Delete(&dir->dirHandle, "..") == 0);
2910 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2913 vnodeNumber = pa.Vnode; /* Get the new Essence */
2915 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2917 dir->haveDotDot = 1;
2918 } else if (strncmp(name,".__afs",6) == 0) {
2920 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2921 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2925 assert(Delete(&dir->dirHandle, name) == 0)
2927 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2928 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2932 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2933 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2934 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber, dir->vnodeNumber);
2935 if (ShowMounts && (vnodeEssence->type == vSymlink) && !(vnodeEssence->modeBits & 0111)) {
2941 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2942 vnodeEssence->InodeNumber);
2944 assert(fdP != NULL);
2945 size = FDH_SIZE(fdP);
2947 memset(buf, 0, 1024);
2948 if (size > 1024) size = 1024;
2949 code = FDH_READ(fdP, buf, size);
2950 assert(code == size);
2951 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2952 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2953 dir->name?dir->name:"??", name, buf);
2954 FDH_REALLYCLOSE(fdP);
2957 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2958 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2959 vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
2960 if (vnodeIdToClass(vnodeNumber) == vLarge && vnodeEssence->name == NULL) {
2962 if (n = (char*)malloc(strlen(name)+1))
2964 vnodeEssence->name = n;
2967 /* The directory entry points to the vnode. Check to see if the
2968 * vnode points back to the directory. If not, then let the
2969 * directory claim it (else it might end up orphaned). Vnodes
2970 * already claimed by another directory are deleted from this
2971 * directory: hardlinks to the same vnode are not allowed
2972 * from different directories.
2974 if (vnodeEssence->parent != dir->vnodeNumber) {
2975 if (!vnodeEssence->claimed && !dirOrphaned) {
2976 /* Vnode does not point back to this directory.
2977 * Orphaned dirs cannot claim a file (it may belong to
2978 * another non-orphaned dir).
2981 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2982 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2983 vnodeNumber, unique, (Testing?"would have been ":""),
2984 vnodeEssence->parent, dir->vnodeNumber);
2986 vnodeEssence->parent = dir->vnodeNumber;
2987 vnodeEssence->changed = 1;
2989 /* Vnode was claimed by another directory */
2992 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2993 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2994 vnodeEssence->parent, vnodeNumber, unique,
2995 (Testing?"would have been ":""));
2997 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2998 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2999 vnodeEssence->parent, vnodeNumber, unique,
3000 (Testing?"would have been ":""));
3005 assert(Delete(&dir->dirHandle, name) == 0);
3010 /* This directory claims the vnode */
3011 vnodeEssence->claimed = 1;
3013 vnodeEssence->count--;
3016 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
3019 register struct VnodeInfo *vip = &vnodeInfo[class];
3020 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3021 char buf[SIZEOF_LARGEDISKVNODE];
3022 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
3024 StreamHandle_t *file;
3029 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3030 fdP = IH_OPEN(vip->handle);
3032 file = FDH_FDOPEN(fdP, "r+");
3033 assert(file != NULL);
3034 size = OS_SIZE(fdP->fd_fd);
3036 vip->nVnodes = (size / vcp->diskSize) - 1;
3037 if (vip->nVnodes > 0) {
3038 assert((vip->nVnodes+1)*vcp->diskSize == size)
3039 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
3040 assert((vip->vnodes = (struct VnodeEssence *)
3041 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL)
3042 if (class == vLarge) {
3043 assert((vip->inodes = (Inode *)
3044 calloc(vip->nVnodes, sizeof (Inode))) != NULL)
3055 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3056 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3057 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3058 nVnodes--, vnodeIndex++) {
3059 if (vnode->type != vNull) {
3060 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3061 afs_size_t vnodeLength;
3062 vip->nAllocatedVnodes++;
3063 vep->count = vnode->linkCount;
3064 VNDISK_GET_LEN(vnodeLength, vnode);
3065 vep->blockCount = nBlocks(vnodeLength);
3066 vip->volumeBlockCount += vep->blockCount;
3067 vep->parent = vnode->parent;
3068 vep->unique = vnode->uniquifier;
3069 if (*maxu < vnode->uniquifier)
3070 *maxu = vnode->uniquifier;
3071 vep->modeBits = vnode->modeBits;
3072 vep->InodeNumber = VNDISK_GET_INO(vnode);
3073 vep->type = vnode->type;
3074 vep->author = vnode->author;
3075 vep->owner = vnode->owner;
3076 vep->group = vnode->group;
3077 if (vnode->type == vDirectory) {
3078 assert(class == vLarge)
3079 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3087 static char *GetDirName(vnode, vp, path)
3089 struct VnodeEssence *vp;
3092 struct VnodeEssence *parentvp;
3098 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3100 strcat(path, vp->name);
3106 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3107 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3109 static int IsVnodeOrphaned(vnode)
3112 struct VnodeEssence *vep;
3114 if (vnode == 0) return(1); /* Vnode zero does not exist */
3115 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3116 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3117 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3119 return( IsVnodeOrphaned(vep->parent) );
3122 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3123 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3126 static struct DirSummary dir;
3127 static struct DirHandle dirHandle;
3128 struct VnodeEssence *parent;
3129 static char path[MAXPATHLEN];
3132 if (dirVnodeInfo->vnodes[i].salvaged)
3133 return; /* already salvaged */
3136 dirVnodeInfo->vnodes[i].salvaged = 1;
3138 if (dirVnodeInfo->inodes[i] == 0)
3139 return; /* Not allocated to a directory */
3141 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3142 if (parent && parent->salvaged == 0)
3143 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3144 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3145 rootdir, rootdirfound);
3146 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3147 dir.unique = dirVnodeInfo->vnodes[i].unique;
3150 dir.parent = dirVnodeInfo->vnodes[i].parent;
3151 dir.haveDot = dir.haveDotDot = 0;
3152 dir.ds_linkH = alinkH;
3153 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3155 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3158 Log("Directory bad, vnode %d; %s...\n",
3159 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3162 CopyAndSalvage(&dir);
3166 dirHandle = dir.dirHandle;
3168 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3171 /* If enumeration failed for random reasons, we will probably delete
3172 * too much stuff, so we guard against this instead.
3174 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3177 /* Delete the old directory if it was copied in order to salvage.
3178 * CopyOnWrite has written the new inode # to the disk, but we still
3179 * have the old one in our local structure here. Thus, we idec the
3183 if (dir.copied && !Testing) {
3184 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3186 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3189 /* Remember rootdir DirSummary _after_ it has been judged */
3190 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3191 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3198 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3200 /* This routine, for now, will only be called for read-write volumes */
3202 int BlocksInVolume = 0, FilesInVolume = 0;
3203 register VnodeClass class;
3204 struct DirSummary rootdir, oldrootdir;
3205 struct VnodeInfo *dirVnodeInfo;
3206 struct VnodeDiskObject vnode;
3207 VolumeDiskData volHeader;
3209 int orphaned, rootdirfound = 0;
3210 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3211 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3212 struct VnodeEssence *vep;
3217 VnodeId LFVnode, ThisVnode;
3218 Unique LFUnique, ThisUnique;
3221 vid = rwIsp->volSummary->header.id;
3222 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3223 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3224 assert(nBytes == sizeof(volHeader));
3225 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3226 assert (volHeader.destroyMe != DESTROY_ME);
3227 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3229 DistilVnodeEssence(vid, vLarge,
3230 rwIsp->volSummary->header.largeVnodeIndex,
3232 DistilVnodeEssence(vid, vSmall,
3233 rwIsp->volSummary->header.smallVnodeIndex,
3236 dirVnodeInfo = &vnodeInfo[vLarge];
3237 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3238 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3239 &rootdir, &rootdirfound);
3246 /* Parse each vnode looking for orphaned vnodes and
3247 * connect them to the tree as orphaned (if requested).
3249 oldrootdir = rootdir;
3250 for (class=0; class < nVNODECLASSES; class++) {
3251 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3252 vep = &(vnodeInfo[class].vnodes[v]);
3253 ThisVnode = bitNumberToVnodeNumber(v, class);
3254 ThisUnique = vep->unique;
3256 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3257 continue; /* Ignore unused, claimed, and root vnodes */
3259 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3260 * entry in this vnode had incremented the parent link count (In
3261 * JudgeEntry()). We need to go to the parent and decrement that
3262 * link count. But if the parent's unique is zero, then the parent
3263 * link count was not incremented in JudgeEntry().
3265 if (class == vLarge) { /* directory vnode */
3266 pv = vnodeIdToBitNumber(vep->parent);
3267 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3268 vnodeInfo[vLarge].vnodes[pv].count++;
3272 continue; /* If no rootdir, can't attach orphaned files */
3274 /* Here we attach orphaned files and directories into the
3275 * root directory, LVVnode, making sure link counts stay correct.
3277 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3278 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3279 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3281 /* Update this orphaned vnode's info. Its parent info and
3282 * link count (do for orphaned directories and files).
3284 vep->parent = LFVnode; /* Parent is the root dir */
3285 vep->unique = LFUnique;
3288 vep->count--; /* Inc link count (root dir will pt to it) */
3290 /* If this orphaned vnode is a directory, change '..'.
3291 * The name of the orphaned dir/file is unknown, so we
3292 * build a unique name. No need to CopyOnWrite the directory
3293 * since it is not connected to tree in BK or RO volume and
3294 * won't be visible there.
3296 if (class == vLarge) {
3300 /* Remove and recreate the ".." entry in this orphaned directory */
3301 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3303 pa.Unique = LFUnique;
3304 assert(Delete(&dh, "..") == 0);
3305 assert(Create(&dh, "..", &pa) == 0);
3307 /* The original parent's link count was decremented above.
3308 * Here we increment the new parent's link count.
3310 pv = vnodeIdToBitNumber(LFVnode);
3311 vnodeInfo[vLarge].vnodes[pv].count--;
3315 /* Go to the root dir and add this entry. The link count of the
3316 * root dir was incremented when ".." was created. Try 10 times.
3318 for (j=0; j<10; j++) {
3319 pa.Vnode = ThisVnode;
3320 pa.Unique = ThisUnique;
3322 sprintf(npath, "%s.%d.%d",
3323 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3324 ThisVnode, ThisUnique);
3326 CopyOnWrite(&rootdir);
3327 code = Create(&rootdir.dirHandle, npath, &pa);
3330 ThisUnique += 50; /* Try creating a different file */
3333 Log("Attaching orphaned %s to volume's root dir as %s\n",
3334 ((class == vLarge)?"directory":"file"), npath);
3336 } /* for each vnode in the class */
3337 } /* for each class of vnode */
3339 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3341 if (!oldrootdir.copied && rootdir.copied) {
3342 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3344 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3347 DFlush(); /* Flush the changes */
3348 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3349 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3350 orphans = ORPH_IGNORE;
3353 /* Write out all changed vnodes. Orphaned files and directories
3354 * will get removed here also (if requested).
3356 for (class = 0; class < nVNODECLASSES; class++){
3357 int nVnodes = vnodeInfo[class].nVnodes;
3358 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3359 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3360 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3361 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3362 for (i = 0; i<nVnodes; i++) {
3363 register struct VnodeEssence *vnp = &vnodes[i];
3364 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3366 /* If the vnode is good but is unclaimed (not listed in
3367 * any directory entries), then it is orphaned.
3370 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3371 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3375 if (vnp->changed || vnp->count) {
3378 nBytes = IH_IREAD(vnodeInfo[class].handle,
3379 vnodeIndexOffset(vcp, vnodeNumber),
3380 (char*)&vnode, sizeof (vnode));
3381 assert(nBytes == sizeof(vnode));
3383 vnode.parent = vnp->parent;
3384 oldCount = vnode.linkCount;
3385 vnode.linkCount = vnode.linkCount - vnp->count;
3388 orphaned = IsVnodeOrphaned(vnodeNumber);
3390 if (!vnp->todelete) {
3391 /* Orphans should have already been attached (if requested) */
3392 assert(orphans != ORPH_ATTACH);
3393 oblocks += vnp->blockCount;
3396 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3397 BlocksInVolume -= vnp->blockCount;
3399 if (VNDISK_GET_INO(&vnode)) {
3400 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3403 memset(&vnode, 0, sizeof(vnode));
3405 } else if (vnp->count) {
3407 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3408 vnodeNumber, oldCount,
3409 (Testing?"would have changed to":"now"), vnode.linkCount);
3413 vnode.dataVersion++;
3415 nBytes = IH_IWRITE(vnodeInfo[class].handle,
3416 vnodeIndexOffset(vcp, vnodeNumber),
3417 (char*)&vnode, sizeof (vnode));
3418 assert(nBytes == sizeof(vnode));
3424 if (!Showmode && ofiles) {
3425 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3426 (!Testing && (orphans == ORPH_REMOVE))?"Removed":"Found",
3430 for (class = 0; class < nVNODECLASSES; class++) {
3431 register struct VnodeInfo *vip = &vnodeInfo[class];
3432 for (i=0; i<vip->nVnodes; i++)
3433 if (vip->vnodes[i].name) free(vip->vnodes[i].name);
3434 if (vip->vnodes) free(vip->vnodes);
3435 if (vip->inodes) free(vip->inodes);
3438 /* Set correct resource utilization statistics */
3439 volHeader.filecount = FilesInVolume;
3440 volHeader.diskused = BlocksInVolume;
3442 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3443 if (volHeader.uniquifier < (maxunique + 1)) {
3444 if (!Showmode) Log("Volume uniquifier is too low; fixed\n");
3445 /* Plus 2,000 in case there are workstations out there with
3446 * cached vnodes that have since been deleted
3448 volHeader.uniquifier = (maxunique + 1 + 2000);
3451 /* Turn off the inUse bit; the volume's been salvaged! */
3452 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3453 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3454 volHeader.inService = 1; /* allow service again */
3455 volHeader.needsCallback = (VolumeChanged != 0);
3456 volHeader.dontSalvage = DONT_SALVAGE;
3459 nBytes = IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader));
3460 assert(nBytes == sizeof(volHeader));
3463 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3464 (Testing?"It would have ":""), volHeader.name,
3465 volHeader.id, FilesInVolume, BlocksInVolume);
3467 IH_RELEASE(vnodeInfo[vSmall].handle);
3468 IH_RELEASE(vnodeInfo[vLarge].handle);
3473 void ClearROInUseBit(struct VolumeSummary *summary)
3475 IHandle_t *h = summary->volumeInfoHandle;
3478 VolumeDiskData volHeader;
3480 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3481 assert(nBytes == sizeof(volHeader));
3482 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC)
3483 volHeader.inUse = 0;
3484 volHeader.needsSalvaged = 0;
3485 volHeader.inService = 1;
3486 volHeader.dontSalvage = DONT_SALVAGE;
3488 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3489 assert(nBytes == sizeof(volHeader));
3494 * Possible delete the volume.
3496 * deleteMe - Always do so, only a partial volume.
3498 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
3502 if (readOnly(isp) || deleteMe) {
3503 if (isp->volSummary && isp->volSummary->fileName) {
3505 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);
3506 if (!Showmode) Log("It will be deleted on this server (you may find it elsewhere)\n");
3508 if (!Showmode) Log("Volume %u needs to be salvaged. Since it is read-only, however,\n",isp->volumeId);
3509 if (!Showmode) Log("it will be deleted instead. It should be recloned.\n");
3512 unlink(isp->volSummary->fileName);
3516 Log("%s salvage was unsuccessful: read-write volume %u\n",
3517 message, isp->volumeId);
3518 Abort("Salvage of volume %u aborted\n",
3524 void AskOffline(VolumeId volumeId)
3526 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3527 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3528 Abort("Salvage aborted\n");
3532 void AskOnline(VolumeId volumeId, char *partition)
3534 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3535 Log("AskOnline: file server denied online request to volume %u partition %s\n",
3536 volumeId, partition);
3540 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3542 /* Volume parameter is passed in case iopen is upgraded in future to
3543 * require a volume Id to be passed
3546 IHandle_t *srcH, *destH;
3547 FdHandle_t *srcFdP, *destFdP;
3550 IH_INIT(srcH, device, rwvolume, inode1);
3551 srcFdP = IH_OPEN(srcH);
3552 assert(srcFdP != NULL);
3553 IH_INIT(destH, device, rwvolume, inode2);
3554 destFdP = IH_OPEN(destH);
3556 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3557 assert(FDH_WRITE(destFdP, buf, n) == n);
3559 FDH_REALLYCLOSE(srcFdP);
3560 FDH_REALLYCLOSE(destFdP);
3566 void PrintInodeList(void)
3568 register struct ViceInodeInfo *ip;
3569 struct ViceInodeInfo *buf;
3570 #ifdef AFS_LARGEFILE_ENV
3571 struct stat64 status;
3572 #else /* !AFS_LARGEFILE_ENV */
3574 #endif /* !AFS_LARGEFILE_ENV */
3577 #ifdef AFS_LARGEFILE_ENV
3578 assert(fstat64(inodeFd, &status) == 0);
3579 #else /* !AFS_LARGEFILE_ENV */
3580 assert(fstat(inodeFd, &status) == 0);
3581 #endif /* !AFS_LARGEFILE_ENV */
3582 buf = (struct ViceInodeInfo *) malloc(status.st_size);
3583 assert(buf != NULL);
3584 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3585 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3586 for(ip = buf; nInodes--; ip++) {
3587 #ifdef AFS_LARGEFILE_ENV
3588 Log("Inode:%s, linkCount=%d, size=(0X%x,0X%x), p=(%u,%u,%u,%u)\n",
3589 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3590 (unsigned) (ip->byteCount >> 32),
3591 (unsigned) (ip->byteCount & 0xffffffff),
3592 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3594 Log("Inode:%s, linkCount=%d, size=%u, p=(%u,%u,%u,%u)\n",
3595 PrintInode(NULL, ip->inodeNumber), ip->linkCount, ip->byteCount,
3596 ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
3602 void PrintInodeSummary(void)
3605 struct InodeSummary *isp;
3607 for (i=0; i<nVolumesInInodeFile; i++) {
3608 isp = &inodeSummary[i];
3609 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n",
3610 isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes,
3611 isp->nSpecialInodes, isp->maxUniquifier);
3615 void PrintVolumeSummary(void)
3618 struct VolumeSummary *vsp;
3620 for (i=0, vsp=volumeSummaryp; i<nVolumes; vsp++, i++) {
3621 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3629 assert(0); /* Fork is never executed in the NT code path */
3640 if (ShowLog) showlog();
3642 if (main_thread != pthread_self())
3643 pthread_exit((void*)code);
3651 int Wait(char *prog)
3655 pid = wait(&status);
3657 if (WCOREDUMP(status))
3658 Log("\"%s\" core dumped!\n", prog);
3659 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3664 static char *TimeStamp(time_t clock, int precision)
3667 static char timestamp[20];
3668 lt = localtime(&clock);
3670 strftime (timestamp, 20, "%m/%d/%Y %T", lt);
3672 strftime (timestamp, 20, "%m/%d/%Y %H:%M", lt);
3676 void CheckLogFile(void)
3678 char oldSlvgLog[AFSDIR_PATH_MAX];
3680 #ifndef AFS_NT40_ENV
3687 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3688 strcat(oldSlvgLog, ".old");
3690 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3691 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3693 if (!logFile) { /* still nothing, use stdout */
3698 #ifndef AFS_NAMEI_ENV
3699 AFS_DEBUG_IOPS_LOG(logFile);
3708 #ifndef AFS_NT40_ENV
3710 printf("Can't show log since using syslog.\n");
3719 logFile = fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3722 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3725 while (fgets(line, sizeof(line), logFile))
3731 void Log(a,b,c,d,e,f,g,h,i,j,k)
3732 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3736 #ifndef AFS_NT40_ENV
3739 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3743 gettimeofday(&now, 0);
3744 fprintf(logFile, "%s ", TimeStamp(now.tv_sec, 1));
3745 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3750 void Abort(a,b,c,d,e,f,g,h,i,j,k)
3751 char *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k;
3753 #ifndef AFS_NT40_ENV
3756 syslog(LOG_INFO, a,b,c,d,e,f,g,h,i,j,k);
3760 fprintf(logFile, a,b,c,d,e,f,g,h,i,j,k);
3762 if (ShowLog) showlog();
3769 char * ToString(char *s)
3772 p = (char *) malloc(strlen(s)+1);
3779 /* Remove the FORCESALVAGE file */
3780 void RemoveTheForce(char *path)
3782 if (!Testing && ForceSalvage) {
3783 if (chdir(path) == 0)
3784 unlink("FORCESALVAGE");
3788 #ifndef AFS_AIX32_ENV
3790 * UseTheForceLuke - see if we can use the force
3792 int UseTheForceLuke(char *path)
3796 assert(chdir(path) != -1);
3798 return (stat("FORCESALVAGE", &force) == 0);
3802 * UseTheForceLuke - see if we can use the force
3805 * The VRMIX fsck will not muck with the filesystem it is supposedly
3806 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3807 * muck directly with the root inode, which is within the normal
3809 * ListViceInodes() has a side effect of setting ForceSalvage if
3810 * it detects a need, based on root inode examination.
3812 int UseTheForceLuke(char *path)
3815 return 0; /* sorry OB1 */
3820 /* NT support routines */
3822 static char execpathname[MAX_PATH];
3823 int nt_SalvagePartition(char *partName, int jobn)
3828 if (!*execpathname) {
3829 n = GetModuleFileName(NULL, execpathname, MAX_PATH-1);
3830 if (!n || n == 1023)
3833 job.cj_magic = SALVAGER_MAGIC;
3834 job.cj_number = jobn;
3835 (void) strcpy(job.cj_part, partName);
3836 pid = (int)spawnprocveb(execpathname, save_args, NULL,
3841 int nt_SetupPartitionSalvage(void *datap, int len)
3843 childJob_t *jobp = (childJob_t*)datap;
3844 char logname[AFSDIR_PATH_MAX];
3846 if (len != sizeof(childJob_t))
3848 if (jobp->cj_magic != SALVAGER_MAGIC)
3853 (void) sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3855 logFile = fopen(logname, "w");
3856 if (!logFile) logFile = stdout;
3862 #endif /* AFS_NT40_ENV */