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(Unique u)
456 return(u & 0x3fffff);
458 #if defined(AFS_SGI_EXMAG)
459 return(u & SGI_UNIQMASK);
462 #endif /* AFS_SGI_EXMAG */
466 static int BadError(register int aerror)
468 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
470 return 0; /* otherwise may be transient, e.g. EMFILE */
475 static int handleit(struct cmd_syndesc *as)
477 register struct cmd_item *ti;
478 char pname[100], *temp;
479 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0, i;
480 struct DiskPartition *partP;
482 #ifdef AFS_SGI_VNODE_GLUE
483 if (afs_init_kernel_config(-1) <0) {
484 printf("Can't determine NUMA configuration, not starting salvager.\n");
490 for (i = 0; i < CMD_MAXPARMS; i++) {
491 if (as->parms[i].items) {
497 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
506 #endif /* FAST_RESTART */
507 if (ti = as->parms[0].items) { /* -partition */
509 strncpy(pname, ti->data, 100);
511 if (ti = as->parms[1].items) { /* -volumeid */
513 printf("You must also specify '-partition' option with the '-volumeid' option\n");
517 vid = atoi(ti->data);
519 if (as->parms[2].items) /* -debug */
521 if (as->parms[3].items) /* -nowrite */
523 if (as->parms[4].items) /* -inodes */
525 if (as->parms[5].items) /* -force */
527 if (as->parms[6].items) /* -oktozap */
529 if (as->parms[7].items) /* -rootinodes */
531 if (as->parms[8].items) /* -RebuildDirs */
533 if (as->parms[9].items) /* -ForceReads */
535 if (ti = as->parms[10].items) {/* -Parallel # */
537 if (strncmp(temp,"all",3) == 0) {
541 if (strlen(temp) != 0) {
542 Parallel = atoi(temp);
543 if (Parallel < 1) Parallel = 1;
544 if (Parallel > MAXPARALLEL) {
545 printf("Setting parallel salvages to maximum of %d \n", MAXPARALLEL);
546 Parallel = MAXPARALLEL;
550 if (ti = as->parms[11].items) {/* -tmpdir */
554 dirp = opendir(tmpdir);
556 printf("Can't open temporary placeholder dir %s; using current partition \n", tmpdir);
561 if (ti = as->parms[12].items) /* -showlog */
563 if (ti = as->parms[13].items) { /* -log */
568 if (ti = as->parms[14].items) { /* -showmounts */
573 if (ti = as->parms[15].items) { /* -orphans */
575 orphans = ORPH_IGNORE;
576 else if (strcmp(ti->data, "remove")==0 || strcmp(ti->data, "r")==0)
577 orphans = ORPH_REMOVE;
578 else if (strcmp(ti->data, "attach")==0 || strcmp(ti->data, "a")==0)
579 orphans = ORPH_ATTACH;
582 #ifndef AFS_NT40_ENV /* ignore options on NT */
583 if ( ti = as->parms[16].items) { /* -syslog */
587 if ( ti = as->parms[17].items) { /* -syslogfacility */
588 useSyslogFacility = atoi(ti->data);
594 if (ti = as->parms[18].items) { /* -DontSalvage */
595 char *msg = "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
603 #endif /* FAST_RESTART */
605 /* Note: if seemvol we initialize this as a standard volume utility: this has the
606 implication that the file server may be running; negotations have to be made with
607 the file server in this case to take the read write volume and associated read-only
608 volumes off line before salvaging */
611 if (afs_winsockInit()<0) {
612 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
613 AFSDIR_SALVAGER_FILE, 0);
614 Log("Failed to initailize winsock, exiting.\n");
619 VInitVolumePackage(seenvol ? volumeUtility: salvager, 5, 5, DONT_CONNECT_FS, 0);
622 if (myjob.cj_number != NOT_CHILD) {
625 (void) strcpy(pname, myjob.cj_part);
630 for (partP = DiskPartitionList; partP; partP = partP->next) {
631 SalvageFileSysParallel(partP);
633 SalvageFileSysParallel(0);
636 partP = VGetPartition(pname, 0);
638 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n",
643 SalvageFileSys(partP, 0);
645 /* Salvage individual volume */
647 Log("salvage: invalid volume id specified; salvage aborted\n");
650 SalvageFileSys (partP, vid);
658 #include "AFS_component_version_number.c"
662 char *save_args[MAX_ARGS];
664 pthread_t main_thread;
667 int main(int argc, char **argv)
669 struct cmd_syndesc *ts;
671 char commandLine[150];
674 extern char cml_version_number[];
678 * The following signal action for AIX is necessary so that in case of a
679 * crash (i.e. core is generated) we can include the user's data section
680 * in the core dump. Unfortunately, by default, only a partial core is
681 * generated which, in many cases, isn't too useful.
683 struct sigaction nsa;
685 sigemptyset(&nsa.sa_mask);
686 nsa.sa_handler = SIG_DFL;
687 nsa.sa_flags = SA_FULLDUMP;
688 sigaction(SIGABRT, &nsa, NULL);
689 sigaction(SIGSEGV, &nsa, NULL);
692 /* Initialize directory paths */
693 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
695 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
697 fprintf(stderr,"%s: Unable to obtain AFS server directory.\n", argv[0]);
701 main_thread = pthread_self();
702 if (spawnDatap && spawnDataLen) {
703 /* This is a child per partition salvager. Don't setup log or
704 * try to lock the salvager lock.
706 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen)<0)
711 for (commandLine[0] = '\0', i=0; i<argc; i++) {
712 if (i > 0) strcat(commandLine, " ");
713 strcat(commandLine, argv[i]);
716 /* All entries to the log will be appended. Useful if there are
717 * multiple salvagers appending to the log.
722 #ifdef AFS_LINUX20_ENV
723 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
725 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
731 if (geteuid() != 0) {
732 printf("Salvager must be run as root.\n");
738 /* bad for normal help flag processing, but can do nada */
740 fprintf(logFile, "%s\n", cml_version_number);
741 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
743 /* Get and hold a lock for the duration of the salvage to make sure
744 * that no other salvage runs at the same time. The routine
745 * VInitVolumePackage (called below) makes sure that a file server or
746 * other volume utilities don't interfere with the salvage.
753 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
754 cmd_AddParm(ts, "-partition", CMD_SINGLE,CMD_OPTIONAL, "Name of partition to salvage");
755 cmd_AddParm(ts, "-volumeid", CMD_SINGLE,CMD_OPTIONAL, "Volume Id to salvage");
756 cmd_AddParm(ts, "-debug", CMD_FLAG,CMD_OPTIONAL, "Run in Debugging mode");
757 cmd_AddParm(ts, "-nowrite", CMD_FLAG,CMD_OPTIONAL, "Run readonly/test mode");
758 cmd_AddParm(ts, "-inodes", CMD_FLAG,CMD_OPTIONAL, "Just list affected afs inodes - debugging flag");
759 cmd_AddParm(ts, "-force", CMD_FLAG,CMD_OPTIONAL, "Force full salvaging");
760 cmd_AddParm(ts, "-oktozap", CMD_FLAG,CMD_OPTIONAL, "Give permission to destroy bogus inodes/volumes - debugging flag");
761 cmd_AddParm(ts, "-rootinodes", CMD_FLAG,CMD_OPTIONAL, "Show inodes owned by root - debugging flag");
762 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG,CMD_OPTIONAL, "Force rebuild/salvage of all directories");
763 cmd_AddParm(ts, "-blockreads", CMD_FLAG,CMD_OPTIONAL, "Read smaller blocks to handle IO/bad blocks");
764 cmd_AddParm(ts, "-parallel", CMD_SINGLE,CMD_OPTIONAL, "# of max parallel partition salvaging");
765 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE,CMD_OPTIONAL, "Name of dir to place tmp files ");
766 cmd_AddParm(ts, "-showlog", CMD_FLAG,CMD_OPTIONAL, "Show log file upon completion");
767 cmd_AddParm(ts, "-showsuid", CMD_FLAG,CMD_OPTIONAL, "Report on suid/sgid files");
768 cmd_AddParm(ts, "-showmounts", CMD_FLAG,CMD_OPTIONAL, "Report on mountpoints");
769 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL, "ignore | remove | attach");
771 /* note - syslog isn't avail on NT, but if we make it conditional, have
772 to deal with screwy offsets for cmd params */
773 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL, "Write salvage log to syslogs");
774 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL, "Syslog facility number to use");
777 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");
778 #endif /* FAST_RESTART */
779 err = cmd_Dispatch(argc, argv);
783 /* Get the salvage lock if not already held. Hold until process exits. */
784 void ObtainSalvageLock(void)
789 salvageLock = (int) CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
790 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
792 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
794 "salvager: There appears to be another salvager running! Aborted.\n");
798 salvageLock = open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT|O_RDWR, 0666);
799 assert(salvageLock >= 0);
800 #ifdef AFS_DARWIN_ENV
801 if (flock(salvageLock, LOCK_EX) == -1) {
803 if (lockf(salvageLock, F_LOCK, 0) == -1) {
806 "salvager: There appears to be another salvager running! Aborted.\n");
813 #ifdef AFS_SGI_XFS_IOPS_ENV
814 /* Check if the given partition is mounted. For XFS, the root inode is not a
815 * constant. So we check the hard way.
817 int IsPartitionMounted(char *part)
820 struct mntent *mntent;
822 assert(mntfp = setmntent(MOUNTED, "r"));
823 while (mntent = getmntent(mntfp)) {
824 if (!strcmp(part, mntent->mnt_dir))
829 return mntent ? 1 : 1;
832 /* Check if the given inode is the root of the filesystem. */
833 #ifndef AFS_SGI_XFS_IOPS_ENV
834 int IsRootInode(struct stat *status)
837 * The root inode is not a fixed value in XFS partitions. So we need to
838 * see if the partition is in the list of mounted partitions. This only
839 * affects the SalvageFileSys path, so we check there.
841 return (status->st_ino == ROOTINODE);
846 #ifndef AFS_NAMEI_ENV
847 /* We don't want to salvage big files filesystems, since we can't put volumes on
850 int CheckIfBigFilesFS(char *mountPoint, char *devName)
852 struct superblock fs;
855 if (strncmp(devName, "/dev/", 5)) {
856 (void) sprintf(name, "/dev/%s", devName);
859 (void) strcpy(name, devName);
862 if (ReadSuper(&fs, name)<0) {
863 Log("Unable to read superblock. Not salvaging partition %s.\n", mountPoint);
866 if (IsBigFilesFileSystem(&fs)) {
867 Log("Partition %s is a big files filesystem, not salvaging.\n", mountPoint);
876 #define HDSTR "\\Device\\Harddisk"
877 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
878 int SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
885 if (!QueryDosDevice(p1->devName, res, RES_LEN-1))
887 if (strncmp(res, HDSTR, HDLEN)) {
890 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
891 res, HDSTR, p1->devName);
895 d1 = atoi(&res[HDLEN]);
897 if (!QueryDosDevice(p2->devName, res, RES_LEN-1))
899 if (strncmp(res, HDSTR, HDLEN)) {
902 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
903 res, HDSTR, p2->devName);
907 d2 = atoi(&res[HDLEN]);
912 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
915 /* This assumes that two partitions with the same device number divided by
916 * PartsPerDisk are on the same disk.
918 void SalvageFileSysParallel(struct DiskPartition *partP)
921 struct DiskPartition *partP;
922 int pid; /* Pid for this job */
923 int jobnumb; /* Log file job number */
924 struct job *nextjob; /* Next partition on disk to salvage */
926 static struct job *jobs[MAXPARALLEL] = {0}; /* Need to zero this */
927 struct job *thisjob = 0;
928 static int numjobs = 0;
929 static int jobcount = 0;
935 char logFileName[256];
939 /* We have a partition to salvage. Copy it into thisjob */
940 thisjob = (struct job *) malloc(sizeof(struct job));
942 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
945 memset(thisjob, 0, sizeof(struct job));
946 thisjob->partP = partP;
947 thisjob->jobnumb = jobcount;
950 else if (jobcount == 0) {
951 /* We are asking to wait for all jobs (partp == 0), yet we never
954 Log("No file system partitions named %s* found; not salvaged\n",
955 VICE_PARTITION_PREFIX);
959 if (debug || Parallel == 1) {
961 SalvageFileSys(thisjob->partP, 0);
968 /* Check to see if thisjob is for a disk that we are already
969 * salvaging. If it is, link it in as the next job to do. The
970 * jobs array has 1 entry per disk being salvages. numjobs is
971 * the total number of disks currently being salvaged. In
972 * order to keep thejobs array compact, when a disk is
973 * completed, the hightest element in the jobs array is moved
974 * down to now open slot.
976 for (j=0; j<numjobs; j++) {
977 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
978 /* On same disk, add it to this list and return */
979 thisjob->nextjob = jobs[j]->nextjob;
980 jobs[j]->nextjob = thisjob;
987 /* Loop until we start thisjob or until all existing jobs are finished */
988 while ( thisjob || (!partP && (numjobs > 0)) ) {
989 startjob = -1; /* No new job to start */
991 if ( (numjobs >= Parallel) || (!partP && (numjobs > 0)) ) {
992 /* Either the max jobs are running or we have to wait for all
993 * the jobs to finish. In either case, we wait for at least one
994 * job to finish. When it's done, clean up after it.
996 pid = wait(&wstatus);
998 for (j=0; j<numjobs; j++) { /* Find which job it is */
999 if (pid == jobs[j]->pid) break;
1001 assert(j < numjobs);
1002 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1003 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1006 numjobs--; /* job no longer running */
1007 oldjob = jobs[j]; /* remember */
1008 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1009 free(oldjob); /* free the old job */
1011 /* If there is another partition on the disk to salvage, then
1012 * say we will start it (startjob). If not, then put thisjob there
1013 * and say we will start it.
1015 if (jobs[j]) { /* Another partitions to salvage */
1016 startjob = j; /* Will start it */
1017 } else { /* There is not another partition to salvage */
1019 jobs[j] = thisjob; /* Add thisjob */
1021 startjob = j; /* Will start it */
1023 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1024 startjob = -1; /* Don't start it - already running */
1028 /* We don't have to wait for a job to complete */
1030 jobs[numjobs] = thisjob; /* Add this job */
1032 startjob = numjobs; /* Will start it */
1036 /* Start up a new salvage job on a partition in job slot "startjob" */
1037 if (startjob != -1) {
1039 Log("Starting salvage of file system partition %s\n",
1040 jobs[startjob]->partP->name);
1042 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1043 * commands and pass the child job number via the data path.
1045 pid = nt_SalvagePartition(jobs[startjob]->partP->name,
1046 jobs[startjob]->jobnumb);
1047 jobs[startjob]->pid = pid;
1052 jobs[startjob]->pid = pid;
1058 for (fd =0; fd < 16; fd++) close(fd);
1059 open("/", 0); dup2(0, 1); dup2(0, 2);
1060 #ifndef AFS_NT40_ENV
1062 openlog(NULL, LOG_PID, useSyslogFacility);
1066 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, jobs[startjob]->jobnumb);
1067 logFile = fopen(logFileName, "w");
1069 if (!logFile) logFile = stdout;
1071 SalvageFileSys1(jobs[startjob]->partP, 0);
1076 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1078 /* If waited for all jobs to complete, now collect log files and return */
1079 #ifndef AFS_NT40_ENV
1080 if ( ! useSyslog ) /* if syslogging - no need to collect */
1083 for (i=0; i<jobcount; i++) {
1084 sprintf(logFileName, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1085 if (passLog = fopen(logFileName, "r")) {
1086 while(fgets(buf, sizeof(buf), passLog)) {
1087 fputs(buf, logFile);
1091 (void)unlink(logFileName);
1099 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1101 if (!canfork || debug || Fork() == 0) {
1102 SalvageFileSys1(partP, singleVolumeNumber);
1103 if (canfork && !debug) {
1109 Wait("SalvageFileSys");
1112 char *get_DevName(char *pbuffer, char *wpath)
1114 char pbuf[128], *ptr;
1115 strcpy(pbuf, pbuffer);
1116 ptr = (char *)strrchr(pbuf, '/');
1119 strcpy(wpath, pbuf);
1122 ptr = (char *)strrchr(pbuffer, '/');
1124 strcpy(pbuffer, ptr+1);
1130 void SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1133 char inodeListPath[256];
1134 static char tmpDevName[100];
1135 static char wpath[100];
1136 struct VolumeSummary *vsp, *esp;
1139 fileSysPartition = partP;
1140 fileSysDevice = fileSysPartition->device;
1141 fileSysPathName = VPartitionPath(fileSysPartition);
1144 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1145 (void) sprintf(fileSysPath, "%s\\", fileSysPathName);
1146 name = partP->devName;
1148 fileSysPath = fileSysPathName;
1149 strcpy(tmpDevName, partP->devName);
1150 name = get_DevName(tmpDevName, wpath);
1151 fileSysDeviceName = name;
1152 filesysfulldev = wpath;
1155 VLockPartition(partP->name);
1156 if (singleVolumeNumber || ForceSalvage)
1159 ForceSalvage = UseTheForceLuke(fileSysPath);
1161 if (singleVolumeNumber) {
1162 if (!VConnectFS()) {
1163 Abort("Couldn't connect to file server\n");
1165 AskOffline(singleVolumeNumber);
1168 if (!Showmode) Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n", partP->name, name, (Testing? "(READONLY mode)":""));
1170 Log("***Forced salvage of all volumes on this partition***\n");
1175 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1182 assert((dirp = opendir(fileSysPath)) != NULL);
1183 while (dp = readdir(dirp)) {
1184 if (!strncmp(dp->d_name, "salvage.inodes.", 15) ||
1185 !strncmp(dp->d_name, "salvage.temp.", 13)) {
1187 Log("Removing old salvager temp files %s\n", dp->d_name);
1188 strcpy(npath, fileSysPath);
1190 strcat(npath, dp->d_name);
1196 tdir = (tmpdir ? tmpdir : fileSysPath);
1198 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1199 (void) strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
1201 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name, getpid());
1203 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1204 unlink(inodeListPath);
1208 /* Using nt_unlink here since we're really using the delete on close
1209 * semantics of unlink. In most places in the salvager, we really do
1210 * mean to unlink the file at that point. Those places have been
1211 * modified to actually do that so that the NT crt can be used there.
1213 inodeFd = _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1214 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1216 inodeFd = open(inodeListPath, O_RDONLY);
1217 unlink(inodeListPath);
1220 Abort("Temporary file %s is missing...\n",
1222 if (ListInodeOption) {
1226 /* enumerate volumes in the partition.
1227 figure out sets of read-only + rw volumes.
1228 salvage each set, read-only volumes first, then read-write.
1229 Fix up inodes on last volume in set (whether it is read-write
1232 GetVolumeSummary(singleVolumeNumber);
1234 for (i = j = 0,vsp = volumeSummaryp,esp = vsp+nVolumes; i < nVolumesInInodeFile; i = j) {
1235 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1236 for (j=i; j < nVolumesInInodeFile
1237 && inodeSummary[j].RWvolumeId == rwvid; j++) {
1238 VolumeId vid = inodeSummary[j].volumeId;
1239 struct VolumeSummary *tsp;
1240 /* Scan volume list (from partition root directory) looking for the
1241 current rw volume number in the volume list from the inode scan.
1242 If there is one here that is not in the inode volume list,
1244 for ( ; vsp<esp && (vsp->header.parent < rwvid); vsp++) {
1246 DeleteExtraVolumeHeaderFile(vsp);
1248 /* Now match up the volume summary info from the root directory with the
1249 entry in the volume list obtained from scanning inodes */
1250 inodeSummary[j].volSummary = NULL;
1251 for (tsp = vsp; tsp<esp && (tsp->header.parent == rwvid); tsp++) {
1252 if (tsp->header.id == vid) {
1253 inodeSummary[j].volSummary = tsp;
1259 /* Salvage the group of volumes (several read-only + 1 read/write)
1260 * starting with the current read-only volume we're looking at.
1262 SalvageVolumeGroup(&inodeSummary[i], j-i);
1265 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1266 for ( ; vsp<esp; vsp++) {
1268 DeleteExtraVolumeHeaderFile(vsp);
1271 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1272 RemoveTheForce(fileSysPath);
1274 if (!Testing && singleVolumeNumber) {
1275 AskOnline(singleVolumeNumber, fileSysPartition->name);
1277 /* Step through the volumeSummary list and set all volumes on-line.
1278 * The volumes were taken off-line in GetVolumeSummary.
1280 for (j=0; j<nVolumes; j++) {
1281 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1286 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1287 fileSysPartition->name, (Testing ? " (READONLY mode)":""));
1290 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1293 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1295 if (!Showmode) Log("The volume header file %s is not associated with any actual data (%sdeleted)\n",
1296 vsp->fileName, (Testing? "would have been ":""));
1298 unlink(vsp->fileName);
1302 CompareInodes(const void *_p1, const void *_p2)
1304 register const struct ViceInodeInfo *p1 = _p1;
1305 register const struct ViceInodeInfo *p2 = _p2;
1306 if (p1->u.vnode.vnodeNumber == INODESPECIAL ||
1307 p2->u.vnode.vnodeNumber == INODESPECIAL) {
1308 VolumeId p1rwid, p2rwid;
1309 p1rwid = (p1->u.vnode.vnodeNumber==INODESPECIAL
1310 ? p1->u.special.parentId : p1->u.vnode.volumeId);
1311 p2rwid = (p2->u.vnode.vnodeNumber==INODESPECIAL
1312 ? p2->u.special.parentId : p2->u.vnode.volumeId);
1313 if (p1rwid < p2rwid)
1315 if (p1rwid > p2rwid)
1317 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1318 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1319 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1320 return (p1->u.special.type < p2->u.special.type? -1: 1);
1321 if (p1->u.vnode.volumeId == p1rwid)
1323 if (p2->u.vnode.volumeId == p2rwid)
1325 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId? -1: 1);
1327 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1328 return (p2->u.vnode.volumeId == p2rwid? 1: -1);
1329 return (p1->u.vnode.volumeId == p1rwid? -1: 1);
1331 if (p1->u.vnode.volumeId<p2->u.vnode.volumeId)
1333 if (p1->u.vnode.volumeId>p2->u.vnode.volumeId)
1335 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1337 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1339 /* The following tests are reversed, so that the most desirable
1340 of several similar inodes comes first */
1341 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1342 #ifdef AFS_3DISPARES
1343 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1344 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1347 #ifdef AFS_SGI_EXMAG
1348 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1349 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1354 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1355 #ifdef AFS_3DISPARES
1356 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1357 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */)
1360 #ifdef AFS_SGI_EXMAG
1361 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1362 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */)
1367 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1368 #ifdef AFS_3DISPARES
1369 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1370 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1373 #ifdef AFS_SGI_EXMAG
1374 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1375 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1380 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1381 #ifdef AFS_3DISPARES
1382 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1383 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */)
1386 #ifdef AFS_SGI_EXMAG
1387 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1388 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */)
1396 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1397 register struct InodeSummary * summary)
1399 int volume = ip->u.vnode.volumeId;
1400 int rwvolume = volume;
1401 register n, nSpecial;
1402 register Unique maxunique;
1405 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1407 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1409 rwvolume = ip->u.special.parentId;
1410 /* This isn't quite right, as there could (in error) be different
1411 parent inodes in different special vnodes */
1414 if (maxunique < ip->u.vnode.vnodeUniquifier)
1415 maxunique = ip->u.vnode.vnodeUniquifier;
1419 summary->volumeId = volume;
1420 summary->RWvolumeId = rwvolume;
1421 summary->nInodes =n;
1422 summary->nSpecialInodes = nSpecial;
1423 summary->maxUniquifier = maxunique;
1426 int OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber)
1428 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1429 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1430 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1435 * Collect list of inodes in file named by path. If a truly fatal error,
1436 * unlink the file and abort. For lessor errors, return -1. The file will
1437 * be unlinked by the caller.
1439 int GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1441 #ifdef AFS_LARGEFILE_ENV
1442 struct stat64 status;
1443 #else /* !AFS_LARGEFILE_ENV */
1445 #endif /* !AFS_LARGEFILE_ENV */
1447 struct ViceInodeInfo *ip;
1448 struct InodeSummary summary;
1449 char summaryFileName[50];
1452 char *dev = fileSysPath;
1453 char *wpath = fileSysPath;
1455 char *dev = fileSysDeviceName;
1456 char *wpath = filesysfulldev;
1458 char *part = fileSysPath;
1461 /* This file used to come from vfsck; cobble it up ourselves now... */
1462 if ((err = ListViceInodes(dev, fileSysPath, path, singleVolumeNumber?OnlyOneVolume:0, singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1464 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n",
1469 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1471 if (forceSal && !ForceSalvage) {
1472 Log("***Forced salvage of all volumes on this partition***\n");
1475 inodeFd = open(path, O_RDWR);
1476 if (inodeFd == -1 ||
1477 #ifdef AFS_LARGEFILE_ENV
1478 fstat64(inodeFd, &status)
1479 #else /* !AFS_LARGEFILE_ENV */
1480 fstat(inodeFd, &status)
1481 #endif /* !AFS_LARGEFILE_ENV */
1484 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1486 tdir = (tmpdir ? tmpdir : part);
1488 (void) _putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1489 (void) strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1491 sprintf(summaryFileName, "%s/salvage.temp.%d", tdir, getpid());
1493 summaryFile = fopen(summaryFileName, "a+");
1494 if (summaryFile == NULL) {
1497 Abort("Unable to create inode summary file\n");
1499 if (!canfork || debug || Fork() == 0) {
1501 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1503 fclose(summaryFile); close(inodeFd);
1504 unlink(summaryFileName);
1505 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1506 RemoveTheForce(fileSysPath);
1507 Log("%s vice inodes on %s; not salvaged\n",
1508 singleVolumeNumber? "No applicable": "No", dev);
1511 ip = (struct ViceInodeInfo *) malloc(status.st_size);
1513 fclose(summaryFile); close(inodeFd);
1515 unlink(summaryFileName);
1516 Abort("Unable to allocate enough space to read inode table; %s not salvaged\n", dev);
1518 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1519 fclose(summaryFile); close(inodeFd);
1521 unlink(summaryFileName);
1522 Abort("Unable to read inode table; %s not salvaged\n", dev);
1524 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1526 #ifdef AFS_LARGEFILE_ENV
1527 lseek64(inodeFd, (off64_t) 0, SEEK_SET) == -1
1528 #else /* !AFS_LARGEFILE_ENV */
1529 lseek(inodeFd, (off_t) 0, SEEK_SET) == -1
1530 #endif /* !AFS_LARGEFILE_ENV */
1531 || write(inodeFd, ip, status.st_size) != status.st_size) {
1532 fclose(summaryFile); close(inodeFd);
1534 unlink(summaryFileName);
1535 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1539 CountVolumeInodes(ip, nInodes, &summary);
1540 if (fwrite(&summary, sizeof (summary), 1, summaryFile) != 1) {
1541 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1542 fclose(summaryFile); close(inodeFd);
1545 summary.index += (summary.nInodes);
1546 nInodes -= summary.nInodes;
1547 ip += summary.nInodes;
1549 /* Following fflush is not fclose, because if it was debug mode would not work */
1550 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1551 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1552 fclose(summaryFile); close(inodeFd);
1555 if (canfork && !debug) {
1561 if (Wait("Inode summary") == -1) {
1562 fclose(summaryFile); close(inodeFd);
1564 unlink(summaryFileName);
1565 Exit(1); /* salvage of this partition aborted */
1568 #ifdef AFS_LARGEFILE_ENV
1569 assert(fstat64(fileno(summaryFile), &status) != -1);
1570 #else /* !AFS_LARGEFILE_ENV */
1571 assert(fstat(fileno(summaryFile), &status) != -1);
1572 #endif /* !AFS_LARGEFILE_ENV */
1573 if ( status.st_size != 0 ) {
1575 inodeSummary = (struct InodeSummary *) malloc(status.st_size);
1576 assert(inodeSummary != NULL);
1577 /* For GNU we need to do lseek to get the file pointer moved. */
1578 #ifdef AFS_LARGEFILE_ENV
1579 assert(lseek64(fileno(summaryFile), (off64_t) 0, SEEK_SET) == 0);
1580 #else /* !AFS_LARGEFILE_ENV */
1581 assert(lseek(fileno(summaryFile), (off_t) 0, SEEK_SET) == 0);
1582 #endif /* !AFS_LARGEFILE_ENV */
1583 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1584 assert(ret == status.st_size);
1586 nVolumesInInodeFile = status.st_size / sizeof (struct InodeSummary);
1587 fclose(summaryFile);
1589 unlink(summaryFileName);
1593 /* Comparison routine for volume sort.
1594 This is setup so that a read-write volume comes immediately before
1595 any read-only clones of that volume */
1596 int CompareVolumes(const void *_p1, const void *_p2)
1598 register const struct VolumeSummary *p1 = _p1;
1599 register const struct VolumeSummary *p2 = _p2;
1600 if (p1->header.parent != p2->header.parent)
1601 return p1->header.parent < p2->header.parent? -1: 1;
1602 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1604 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1606 return p1->header.id < p2->header.id ? -1: 1; /* Both read-only */
1609 void GetVolumeSummary(VolumeId singleVolumeNumber)
1612 afs_int32 nvols = 0;
1613 struct VolumeSummary *vsp, vs;
1614 struct VolumeDiskHeader diskHeader;
1617 /* Get headers from volume directory */
1618 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1619 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1620 if (!singleVolumeNumber) {
1621 while (dp = readdir(dirp)) {
1622 char *p = dp->d_name;
1623 p = strrchr(dp->d_name, '.');
1624 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1626 if ((fd = open(dp->d_name, O_RDONLY)) != -1 &&
1627 read(fd, (char*)&diskHeader, sizeof (diskHeader))
1628 == sizeof (diskHeader) &&
1629 diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1630 DiskToVolumeHeader(&vs.header, &diskHeader);
1637 closedir(dirp); dirp = opendir("."); /* No rewinddir for NT */
1641 if (!nvols) nvols = 1;
1642 volumeSummaryp = (struct VolumeSummary *)malloc(nvols * sizeof(struct VolumeSummary));
1644 volumeSummaryp = (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1645 assert(volumeSummaryp != NULL);
1648 vsp = volumeSummaryp;
1649 while (dp = readdir(dirp)) {
1650 char *p = dp->d_name;
1651 p = strrchr(dp->d_name, '.');
1652 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1655 if ((fd = open(dp->d_name, O_RDONLY)) == -1
1656 || read(fd, &diskHeader, sizeof (diskHeader))
1657 != sizeof (diskHeader)
1658 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1663 if (!singleVolumeNumber) {
1664 if (!Showmode) Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName,
1665 dp->d_name, (Testing? "it would have been ":""));
1671 char nameShouldBe[64];
1672 DiskToVolumeHeader(&vsp->header, &diskHeader);
1673 if (singleVolumeNumber && vsp->header.id==singleVolumeNumber && vsp->header.parent!=singleVolumeNumber) {
1674 Log("%u is a read-only volume; not salvaged\n", singleVolumeNumber);
1677 if (!singleVolumeNumber || (vsp->header.id==singleVolumeNumber || vsp->header.parent==singleVolumeNumber)) {
1678 sprintf(nameShouldBe, VFORMAT, vsp->header.id);
1679 if (singleVolumeNumber)
1680 AskOffline(vsp->header.id);
1681 if (strcmp(nameShouldBe, dp->d_name)) {
1682 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 ":""));
1687 vsp->fileName = ToString(dp->d_name);
1697 qsort(volumeSummaryp,nVolumes,sizeof (struct VolumeSummary),CompareVolumes);
1700 /* Find the link table. This should be associated with the RW volume or, if
1701 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1703 Inode FindLinkHandle(register struct InodeSummary *isp, int nVols,
1704 struct ViceInodeInfo *allInodes)
1707 struct ViceInodeInfo *ip;
1709 for (i=0; i<nVols; i++) {
1710 ip = allInodes + isp[i].index;
1711 for (j=0; j<isp[i].nSpecialInodes; j++) {
1712 if (ip[j].u.special.type == VI_LINKTABLE)
1713 return ip[j].inodeNumber;
1719 int CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1721 struct versionStamp version;
1724 if (!VALID_INO(ino))
1725 ino = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
1726 isp->volumeId, INODESPECIAL,
1727 VI_LINKTABLE, isp->RWvolumeId);
1728 if (!VALID_INO(ino))
1729 Abort("Unable to allocate link table inode for volume %u (error = %d)\n",
1730 isp->RWvolumeId, errno);
1731 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1732 fdP = IH_OPEN(VGLinkH);
1734 Abort("Can't open link table for volume %u (error = %d)\n",
1735 isp->RWvolumeId, errno);
1737 if (FDH_TRUNC(fdP, 0)<0)
1738 Abort("Can't truncate link table for volume %u (error = %d)\n",
1739 isp->RWvolumeId, errno);
1741 version.magic = LINKTABLEMAGIC;
1742 version.version = LINKTABLEVERSION;
1744 if (FDH_WRITE(fdP, (char*)&version, sizeof(version))
1746 Abort("Can't truncate link table for volume %u (error = %d)\n",
1747 isp->RWvolumeId, errno);
1749 FDH_REALLYCLOSE(fdP);
1751 /* If the volume summary exits (i.e., the V*.vol header file exists),
1752 * then set this inode there as well.
1754 if (isp->volSummary)
1755 isp->volSummary->header.linkTable = ino;
1761 void *nt_SVG(void *arg)
1763 SVGParms_t *parms = (SVGParms_t*)arg;
1764 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1768 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1771 pthread_attr_t tattr;
1775 /* Initialize per volume global variables, even if later code does so */
1779 memset(&VolInfo, 0, sizeof(VolInfo));
1781 parms.svgp_inodeSummaryp = isp;
1782 parms.svgp_count = nVols;
1783 code = pthread_attr_init(&tattr);
1785 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1789 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1791 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n",
1795 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1797 Log("Failed to create thread to salvage volume group %u\n",
1801 (void) pthread_join(tid, NULL);
1803 #endif /* AFS_NT40_ENV */
1805 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1807 struct ViceInodeInfo *inodes,*allInodes,*ip;
1808 int i, totalInodes, size, salvageTo;
1812 int dec_VGLinkH = 0;
1814 FdHandle_t *fdP = NULL;
1817 haveRWvolume = (isp->volumeId==isp->RWvolumeId && isp->nSpecialInodes>0);
1818 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1819 if (!ForceSalvage && QuickCheck(isp, nVols))
1822 if (ShowMounts && !haveRWvolume)
1824 if (canfork && !debug && Fork() != 0) {
1825 (void) Wait("Salvage volume group");
1828 for (i = 0, totalInodes = 0; i<nVols; i++)
1829 totalInodes += isp[i].nInodes;
1830 size = totalInodes * sizeof (struct ViceInodeInfo);
1831 inodes = (struct ViceInodeInfo *) malloc(size);
1832 allInodes = inodes - isp->index; /* this would the base of all the inodes
1833 for the partition, if all the inodes
1834 had been read into memory */
1835 #ifdef AFS_LARGEFILE_ENV
1836 assert(lseek64(inodeFd,(off64_t)(isp->index*sizeof(struct ViceInodeInfo)),SEEK_SET) != -1);
1837 #else /* !AFS_LARGEFILE_ENV */
1838 assert(lseek(inodeFd,(off_t)(isp->index*sizeof(struct ViceInodeInfo)),SEEK_SET) != -1);
1839 #endif /* !AFS_LARGEFILE_ENV */
1840 assert(read(inodeFd,inodes,size) == size);
1842 /* Don't try to salvage a read write volume if there isn't one on this
1844 salvageTo = haveRWvolume? 0:1;
1846 #ifdef AFS_NAMEI_ENV
1847 ino = FindLinkHandle(isp, nVols, allInodes);
1848 if (VALID_INO(ino)) {
1849 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1850 fdP = IH_OPEN(VGLinkH);
1852 if (!VALID_INO(ino) || fdP == NULL) {
1853 Log("%s link table for volume %u.\n",
1854 Testing ? "Would have recreated" :"Recreating", isp->RWvolumeId);
1856 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1859 CreateLinkTable(isp, ino);
1863 FDH_REALLYCLOSE(fdP);
1865 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1868 /* Salvage in reverse order--read/write volume last; this way any
1869 Inodes not referenced by the time we salvage the read/write volume
1870 can be picked up by the read/write volume */
1871 /* ACTUALLY, that's not done right now--the inodes just vanish */
1872 for (i = nVols-1; i>=salvageTo; i--) {
1874 struct InodeSummary *lisp = &isp[i];
1875 #ifdef AFS_NAMEI_ENV
1876 /* If only the RO is present on this partition, the link table
1877 * shows up as a RW volume special file. Need to make sure the
1878 * salvager doesn't try to salvage the non-existent RW.
1880 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1881 /* If this only special inode is the link table, continue */
1882 if (inodes->u.special.type == VI_LINKTABLE) {
1888 if (!Showmode) Log("%s VOLUME %u%s.\n", rw? "SALVAGING": "CHECKING CLONED",
1889 lisp->volumeId, (Testing?"(READONLY mode)":""));
1890 /* Check inodes twice. The second time do things seriously. This
1891 way the whole RO volume can be deleted, below, if anything goes wrong */
1892 for (check = 1; check>=0; check--) {
1894 if (SalvageVolumeHeaderFile(lisp,allInodes,rw,check, &deleteMe) == -1) {
1895 MaybeZapVolume(lisp,"Volume header",deleteMe, check);
1896 if (rw && deleteMe) {
1897 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1898 volume won't be called */
1904 if (rw && check == 1)
1906 if (SalvageVnodes(isp,lisp,allInodes,check) == -1) {
1907 MaybeZapVolume(lisp,"Vnode index", 0, check);
1913 /* Fix actual inode counts */
1915 for (ip = inodes; totalInodes; ip++,totalInodes--) {
1916 static int TraceBadLinkCounts = 0;
1917 #ifdef AFS_NAMEI_ENV
1918 if (VGLinkH->ih_ino == ip->inodeNumber) {
1919 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
1920 VGLinkH_p1 = ip->u.param[0];
1921 continue; /* Deal with this last. */
1924 if (ip->linkCount != 0 && TraceBadLinkCounts) {
1925 TraceBadLinkCounts--; /* Limit reports, per volume */
1926 #ifdef AFS_LARGEFILE_ENV
1927 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size (0X%x,0x%x), p=(%u,%u,%u,%u)\n",
1928 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1929 (unsigned) ((ip->byteCount) >> 32),
1930 (unsigned) ((ip->byteCount) & 0xffffffff),
1931 ip->u.param[0], ip->u.param[1],
1932 ip->u.param[2], ip->u.param[3]);
1934 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %u, p=(%u,%u,%u,%u)\n",
1935 ip->linkCount, PrintInode(NULL, ip->inodeNumber),
1936 ip->byteCount, ip->u.param[0], ip->u.param[1],
1937 ip->u.param[2], ip->u.param[3]);
1938 #endif /* !AFS_LARGEFILE_ENV */
1940 while (ip->linkCount > 0) {
1941 /* below used to assert, not break */
1943 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
1944 Log ("idec failed. inode %s errno %d\n",
1945 PrintInode(NULL, ip->inodeNumber), errno);
1951 while (ip->linkCount < 0) {
1952 /* these used to be asserts */
1954 if (IH_INC(VGLinkH ,ip->inodeNumber, ip->u.param[0])) {
1955 Log ("iinc failed. inode %s errno %d\n",
1956 PrintInode(NULL, ip->inodeNumber) ,errno);
1963 #ifdef AFS_NAMEI_ENV
1964 while (dec_VGLinkH > 0) {
1965 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1966 Log("idec failed on link table, errno = %d\n", errno);
1970 while (dec_VGLinkH < 0) {
1971 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1)<0) {
1972 Log("iinc failed on link table, errno = %d\n", errno);
1979 /* Directory consistency checks on the rw volume */
1981 SalvageVolume(isp, VGLinkH);
1982 IH_RELEASE(VGLinkH);
1984 if (canfork && !debug) {
1990 int QuickCheck(register struct InodeSummary *isp, int nVols)
1992 /* Check headers BEFORE forking */
1996 for (i = 0; i<nVols; i++) {
1997 struct VolumeSummary *vs = isp[i].volSummary;
1998 VolumeDiskData volHeader;
2000 /* Don't salvage just because phantom rw volume is there... */
2001 /* (If a read-only volume exists, read/write inodes must also exist) */
2002 if (i == 0 && isp->nSpecialInodes == 0 && nVols >1)
2006 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2007 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader))
2008 == sizeof(volHeader)
2009 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2010 && volHeader.dontSalvage == DONT_SALVAGE
2011 && volHeader.needsSalvaged == 0
2012 && volHeader.destroyMe == 0) {
2013 if (volHeader.inUse == 1) {
2014 volHeader.inUse = 0;
2015 volHeader.inService = 1;
2017 if (IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader))
2018 != sizeof(volHeader)) {
2035 /* SalvageVolumeHeaderFile
2037 * Salvage the top level V*.vol header file. Make sure the special files
2038 * exist and that there are no duplicates.
2040 * Calls SalvageHeader for each possible type of volume special file.
2043 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2044 register struct ViceInodeInfo *inodes,
2045 int RW, int check, int *deleteMe)
2049 register struct ViceInodeInfo *ip;
2050 int allinodesobsolete = 1;
2051 struct VolumeDiskHeader diskHeader;
2055 memset(&tempHeader, 0, sizeof(tempHeader));
2056 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2057 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2058 tempHeader.id = isp->volumeId;
2059 tempHeader.parent = isp->RWvolumeId;
2060 /* Check for duplicates (inodes are sorted by type field) */
2061 for (i = 0; i<isp->nSpecialInodes-1; i++) {
2062 ip = &inodes[isp->index+i];
2063 if (ip->u.special.type == (ip+1)->u.special.type) {
2064 if (!Showmode) Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2068 for (i = 0; i<isp->nSpecialInodes; i++) {
2069 ip = &inodes[isp->index+i];
2070 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2072 Log("Rubbish header inode\n");
2075 Log("Rubbish header inode; deleted\n");
2077 else if (!stuff[ip->u.special.type-1].obsolete) {
2078 *(stuff[ip->u.special.type-1].inode) = ip->inodeNumber;
2079 if (!check && ip->u.special.type != VI_LINKTABLE)
2080 ip->linkCount--; /* Keep the inode around */
2081 allinodesobsolete = 0;
2085 if (allinodesobsolete) {
2092 VGLinkH_cnt ++; /* one for every header. */
2094 if (!RW && !check && isp->volSummary) {
2095 ClearROInUseBit(isp->volSummary);
2099 for (i = 0; i< MAXINODETYPE; i++) {
2100 if (stuff[i].inodeType == VI_LINKTABLE) {
2101 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2102 * And we may have recreated the link table earlier, so set the
2103 * RW header as well.
2105 if (VALID_INO(VGLinkH->ih_ino)) {
2106 *stuff[i].inode = VGLinkH->ih_ino;
2110 if (SalvageHeader(&stuff[i],isp,check,deleteMe) == -1 && check)
2114 if (isp->volSummary == NULL) {
2116 sprintf(name, VFORMAT, isp->volumeId);
2118 Log("No header file for volume %u\n", isp->volumeId);
2121 if (!Showmode) Log("No header file for volume %u; %screating %s/%s\n",
2122 isp->volumeId, (Testing?"it would have been ":""),
2123 fileSysPathName, name);
2124 headerFd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
2125 assert(headerFd != -1);
2126 isp->volSummary = (struct VolumeSummary *)
2127 malloc(sizeof(struct VolumeSummary));
2128 isp->volSummary->fileName = ToString(name);
2132 /* hack: these two fields are obsolete... */
2133 isp->volSummary->header.volumeAcl = 0;
2134 isp->volSummary->header.volumeMountTable = 0;
2136 if (memcmp(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader))) {
2137 /* We often remove the name before calling us, so we make a fake one up */
2138 if (isp->volSummary->fileName) {
2139 strcpy(name, isp->volSummary->fileName);
2141 sprintf(name, VFORMAT, isp->volumeId);
2142 isp->volSummary->fileName = ToString(name);
2145 Log("Header file %s is damaged or no longer valid%s\n",
2146 name, (check ? "" : "; repairing"));
2150 headerFd = open(name, O_RDWR|O_TRUNC, 0644);
2151 assert(headerFd != -1);
2155 memcpy(&isp->volSummary->header, &tempHeader, sizeof(struct VolumeHeader));
2157 if (!Showmode) Log("It would have written a new header file for volume %u\n", isp->volumeId);
2159 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2160 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2161 != sizeof(struct VolumeDiskHeader)) {
2162 Log("Couldn't rewrite volume header file!\n");
2169 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice,
2170 isp->RWvolumeId, isp->volSummary->header.volumeInfo);
2174 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
2175 int check, int *deleteMe)
2178 VolumeDiskData volumeInfo;
2179 struct versionStamp fileHeader;
2188 #ifndef AFS_NAMEI_ENV
2189 if ( sp->inodeType == VI_LINKTABLE)
2192 if (*(sp->inode) == 0) {
2194 Log("Missing inode in volume header (%s)\n", sp->description);
2197 if (!Showmode) Log("Missing inode in volume header (%s); %s\n",
2198 sp->description, (Testing ? "it would have recreated it": "recreating"));
2200 *(sp->inode) = IH_CREATE(NULL, fileSysDevice, fileSysPath, 0,
2201 isp->volumeId, INODESPECIAL,
2202 sp->inodeType, isp->RWvolumeId);
2203 if (!VALID_INO(*(sp->inode)))
2204 Abort("Unable to allocate inode (%s) for volume header (error = %d)\n",
2205 sp->description, errno);
2210 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2211 fdP = IH_OPEN(specH);
2212 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2213 /* bail out early and destroy the volume */
2214 if (!Showmode) Log("Still can't open volume header inode (%s), destroying volume\n",
2216 if (deleteMe) *deleteMe = 1;
2221 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2222 sp->description, errno);
2225 (FDH_READ(fdP, (char*)&header, sp->size) != sp->size
2226 || header.fileHeader.magic != sp->stamp.magic)) {
2228 Log("Part of the header (%s) is corrupted\n", sp->description);
2229 FDH_REALLYCLOSE(fdP);
2233 Log("Part of the header (%s) is corrupted; recreating\n",
2237 if (sp->inodeType == VI_VOLINFO && header.volumeInfo.destroyMe == DESTROY_ME) {
2240 FDH_REALLYCLOSE(fdP);
2244 if (recreate && !Testing) {
2246 Abort("Internal error: recreating volume header (%s) in check mode\n",
2248 code = FDH_TRUNC(fdP, 0);
2250 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2251 sp->description, errno);
2253 /* The following code should be moved into vutil.c */
2254 if (sp->inodeType == VI_VOLINFO) {
2256 memset(&header.volumeInfo, 0, sizeof (header.volumeInfo));
2257 header.volumeInfo.stamp = sp->stamp;
2258 header.volumeInfo.id = isp->volumeId;
2259 header.volumeInfo.parentId = isp->RWvolumeId;
2260 sprintf(header.volumeInfo.name, "bogus.%u",isp->volumeId);
2261 Log("Warning: the name of volume %u is now \"bogus.%u\"\n", isp->volumeId, isp->volumeId);
2262 header.volumeInfo.inService = 0;
2263 header.volumeInfo.blessed = 0;
2264 /* The + 1000 is a hack in case there are any files out in venus caches */
2265 header.volumeInfo.uniquifier = (isp->maxUniquifier+1)+1000;
2266 header.volumeInfo.type =
2267 (isp->volumeId == isp->RWvolumeId? readwriteVolume:readonlyVolume); /* XXXX */
2268 header.volumeInfo.needsCallback = 0;
2269 gettimeofday(&tp,0);
2270 header.volumeInfo.creationDate = tp.tv_sec;
2271 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2272 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2274 code = FDH_WRITE(fdP, (char*)&header.volumeInfo,
2275 sizeof(header.volumeInfo));
2276 if (code != sizeof(header.volumeInfo)) {
2278 Abort("Unable to write volume header file (%s) (errno = %d)\n",
2279 sp->description, errno);
2280 Abort("Unable to write entire volume header file (%s)\n",
2285 if(FDH_SEEK(fdP,0,SEEK_SET)<0) {
2286 Abort("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",sp->description,errno);
2288 code = FDH_WRITE(fdP, (char*)&sp->stamp, sizeof(sp->stamp));
2289 if (code != sizeof(sp->stamp)) {
2291 Abort("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2292 sp->description, errno);
2293 Abort("Unable to write entire version stamp in volume header file (%s)\n",
2298 FDH_REALLYCLOSE(fdP);
2300 if (sp->inodeType == VI_VOLINFO) {
2301 VolInfo = header.volumeInfo;
2304 if (VolInfo.updateDate) {
2305 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2306 if (!Showmode) Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2307 (Testing?"it would have been ":""), update);
2309 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2310 if (!Showmode) Log("%s (%u) not updated (created %s)\n", VolInfo.name, VolInfo.id, update);
2319 int SalvageVnodes(register struct InodeSummary *rwIsp,
2320 register struct InodeSummary * thisIsp,
2321 register struct ViceInodeInfo * inodes, int check)
2323 int ilarge, ismall, ioffset, RW, nInodes;
2324 ioffset = rwIsp->index+rwIsp->nSpecialInodes; /* first inode */
2325 if (Showmode) return 0;
2326 RW = (rwIsp == thisIsp);
2327 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2328 ismall = SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex,
2329 vSmall, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2330 if (check && ismall == -1)
2332 ilarge = SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex,
2333 vLarge, RW, &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2334 return (ilarge==0 && ismall==0 ? 0: -1);
2337 int SalvageIndex(Inode ino, VnodeClass class, int RW,
2338 register struct ViceInodeInfo *ip,
2339 int nInodes, struct VolumeSummary *volSummary, int check)
2341 VolumeId volumeNumber;
2342 char buf[SIZEOF_LARGEDISKVNODE];
2343 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
2345 StreamHandle_t *file;
2346 struct VnodeClassInfo *vcp;
2347 afs_size_t size, vnodeSize;
2348 int vnodeIndex, nVnodes;
2349 afs_ino_str_t stmp1, stmp2;
2353 volumeNumber = volSummary->header.id;
2354 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2355 fdP = IH_OPEN(handle);
2356 assert(fdP != NULL);
2357 file = FDH_FDOPEN(fdP, "r+");
2358 assert(file != NULL);
2359 vcp = &VnodeClassInfo[class];
2360 size = OS_SIZE(fdP->fd_fd);
2362 nVnodes = (size / vcp->diskSize) - 1;
2364 assert((nVnodes+1) * vcp->diskSize == size);
2365 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2370 for (vnodeIndex = 0;
2371 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2372 nVnodes--, vnodeIndex++) {
2373 if (vnode->type != vNull) {
2374 int vnodeChanged = 0;
2375 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2376 /* Log programs that belong to root (potentially suid root);
2377 don't bother for read-only or backup volumes */
2378 #ifdef notdef /* This is done elsewhere */
2379 if (ShowRootFiles && RW && vnode->owner==0 && vnodeNumber != 1)
2380 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n",
2381 VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author,
2382 vnode->owner, vnode->modeBits);
2384 if (VNDISK_GET_INO(vnode) == 0) {
2386 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2387 memset(vnode, 0, vcp->diskSize);
2392 if (vcp->magic != vnode->vnodeMagic) {
2393 /* bad magic #, probably partially created vnode */
2394 Log("Partially allocated vnode %d deleted.\n", vnodeNumber);
2395 memset(vnode, 0, vcp->diskSize);
2399 /* ****** Should do a bit more salvage here: e.g. make sure
2400 vnode type matches what it should be given the index */
2401 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2402 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2403 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2404 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2411 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2412 /* The following doesn't work, because the version number
2413 is not maintained correctly by the file server */
2414 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2415 vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2417 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2424 /* For RW volume, look for vnode with matching inode number;
2425 if no such match, take the first determined by our sort
2427 register struct ViceInodeInfo *lip = ip;
2428 register lnInodes = nInodes;
2429 while (lnInodes && lip->u.vnode.vnodeNumber == vnodeNumber) {
2430 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2439 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2440 /* "Matching" inode */
2444 vu = vnode->uniquifier;
2445 iu = ip->u.vnode.vnodeUniquifier;
2446 vd = vnode->dataVersion;
2447 id = ip->u.vnode.inodeDataVersion;
2449 * Because of the possibility of the uniquifier overflows (> 4M)
2450 * we compare them modulo the low 22-bits; we shouldn't worry
2451 * about mismatching since they shouldn't to many old
2452 * uniquifiers of the same vnode...
2454 if (IUnique(vu) != IUnique(iu)) {
2456 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n",
2457 vnodeNumber, IUnique(vu), IUnique(iu));
2460 vnode->uniquifier = iu;
2461 #ifdef AFS_3DISPARES
2462 vnode->dataVersion = (id >= vd ?
2463 /* 90% of 2.1M */ ((id-vd) > 1887437 ? vd:id):
2464 /* 90% of 2.1M */ ((vd-id) > 1887437 ? id:vd));
2466 #if defined(AFS_SGI_EXMAG)
2467 vnode->dataVersion = (id >= vd ?
2468 /* 90% of 16M */ ((id-vd) > 15099494 ? vd:id):
2469 /* 90% of 16M */ ((vd-id) > 15099494 ? id:vd));
2471 vnode->dataVersion = (id>vd ? id:vd);
2472 #endif /* AFS_SGI_EXMAG */
2473 #endif /* AFS_3DISPARES */
2477 /* don't bother checking for vd > id any more, since
2478 partial file transfers always result in this state,
2479 and you can't do much else anyway (you've already
2480 found the best data you can) */
2481 #ifdef AFS_3DISPARES
2482 if (!vnodeIsDirectory(vnodeNumber) &&
2483 ((vd < id && (id-vd) < 1887437) ||
2484 ((vd > id && (vd-id) > 1887437)))) {
2486 #if defined(AFS_SGI_EXMAG)
2487 if (!vnodeIsDirectory(vnodeNumber) &&
2488 ((vd < id && (id-vd) < 15099494) ||
2489 ((vd > id && (vd-id) > 15099494)))) {
2491 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2492 #endif /* AFS_SGI_EXMAG */
2494 if (!Showmode) Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2495 vnode->dataVersion = id;
2500 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2503 #ifdef AFS_LARGEFILE_ENV
2504 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=(0X%x,0X%x)\n",
2506 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2507 PrintInode(stmp2, ip->inodeNumber),
2508 (unsigned) (ip->byteCount >> 32),
2509 (unsigned) (ip->byteCount & 0xffffffff));
2511 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%d\n",
2513 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2514 PrintInode(stmp2, ip->inodeNumber),
2516 #endif /* !AFS_LARGEFILE_ENV */
2518 VNDISK_SET_INO(vnode, ip->inodeNumber);
2523 #ifdef AFS_LARGEFILE_ENV
2524 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=(0X%x,0X%x)\n",
2526 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2527 PrintInode(stmp2, ip->inodeNumber),
2528 (unsigned) (ip->byteCount >> 32),
2529 (unsigned) (ip->byteCount & 0xffffffff));
2531 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%d\n",
2533 PrintInode(stmp1, VNDISK_GET_INO(vnode)),
2534 PrintInode(stmp2, ip->inodeNumber),
2536 #endif /* !AFS_LARGEFILE_ENV */
2538 VNDISK_SET_INO(vnode, ip->inodeNumber);
2541 VNDISK_GET_LEN(vnodeSize, vnode);
2542 if (ip->byteCount != vnodeSize) {
2545 #ifdef AFS_LARGEFILE_ENV
2546 Log("Vnode %d: length incorrect; (is (0X%x,0X%x) should be (0X%x,0X%x))\n",
2548 (unsigned) (vnodeSize >> 32),
2549 (unsigned) (vnodeSize & 0xffffffff),
2550 (unsigned) (ip->byteCount >> 32),
2551 (unsigned) (ip->byteCount & 0xffffffff));
2553 Log("Vnode %d: length incorrect; (is %d should be %d)\n",
2554 vnodeNumber, vnodeSize, ip->byteCount);
2555 #endif /* !AFS_LARGEFILE_ENV */
2560 #ifdef AFS_LARGEFILE_ENV
2561 Log("Vnode %d: length incorrect; changed from (0X%x,0X%x) to (0X%x,0X%x)\n",
2563 (unsigned) (vnodeSize >> 32),
2564 (unsigned) (vnodeSize & 0xffffffff),
2565 (unsigned) (ip->byteCount >> 32),
2566 (unsigned) (ip->byteCount & 0xffffffff));
2568 Log("Vnode %d: length incorrect; changed from %d to %d\n",
2569 vnodeNumber, vnodeSize, ip->byteCount);
2570 #endif /* !AFS_LARGEFILE_ENV */
2571 VNDISK_SET_LEN(vnode, ip->byteCount);
2575 ip->linkCount--; /* Keep the inode around */
2579 else { /* no matching inode */
2580 if (VNDISK_GET_INO(vnode) != 0 || vnode->type == vDirectory) {
2581 /* No matching inode--get rid of the vnode */
2583 if (VNDISK_GET_INO(vnode)) {
2585 Log("Vnode %d (unique %d): corresponding inode %s is missing\n",
2586 vnodeNumber, vnode->uniquifier,
2587 PrintInode(NULL, VNDISK_GET_INO(vnode)));
2590 if (!Showmode) Log("Vnode %d (unique %d): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2595 if (VNDISK_GET_INO(vnode)) {
2597 Log("Vnode %d (unique %d): corresponding inode %s is missing; vnode deleted, vnode mod time=%s",
2598 vnodeNumber, vnode->uniquifier,
2599 PrintInode(NULL, VNDISK_GET_INO(vnode)),
2600 ctime((time_t *)&(vnode->serverModifyTime)));
2603 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)));
2605 memset(vnode, 0, vcp->diskSize);
2608 /* Should not reach here becuase we checked for
2609 * (inodeNumber == 0) above. And where we zero the vnode,
2610 * we also goto vnodeDone.
2614 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2618 } /* VNDISK_GET_INO(vnode) != 0 */
2620 assert(!(vnodeChanged && check));
2621 if (vnodeChanged && !Testing) {
2622 assert(IH_IWRITE(handle, vnodeIndexOffset(vcp,vnodeNumber),
2623 (char*)vnode, vcp->diskSize)
2625 VolumeChanged = 1; /* For break call back */
2636 struct VnodeEssence *CheckVnodeNumber(VnodeId vnodeNumber)
2639 struct VnodeInfo *vip;
2642 class = vnodeIdToClass(vnodeNumber);
2643 vip = &vnodeInfo[class];
2644 offset = vnodeIdToBitNumber(vnodeNumber);
2645 return (offset >= vip->nVnodes? NULL: &vip->vnodes[offset]);
2648 void CopyOnWrite(register struct DirSummary *dir)
2650 /* Copy the directory unconditionally if we are going to change it:
2651 * not just if was cloned.
2653 struct VnodeDiskObject vnode;
2654 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2655 Inode oldinode, newinode;
2658 if (dir->copied || Testing)
2660 DFlush(); /* Well justified paranoia... */
2662 code = IH_IREAD(vnodeInfo[vLarge].handle,
2663 vnodeIndexOffset(vcp, dir->vnodeNumber),
2664 (char*)&vnode, sizeof (vnode));
2665 assert(code == sizeof(vnode));
2666 oldinode = VNDISK_GET_INO(&vnode);
2667 /* Increment the version number by a whole lot to avoid problems with
2668 * clients that were promised new version numbers--but the file server
2669 * crashed before the versions were written to disk.
2671 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2672 dir->rwVid, dir->vnodeNumber,
2673 vnode.uniquifier, vnode.dataVersion += 200);
2674 assert(VALID_INO(newinode));
2675 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2677 VNDISK_SET_INO(&vnode, newinode);
2678 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2679 vnodeIndexOffset(vcp, dir->vnodeNumber),
2680 (char*)&vnode, sizeof (vnode));
2681 assert(code == sizeof (vnode));
2683 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2684 fileSysDevice, newinode);
2685 /* Don't delete the original inode right away, because the directory is
2686 * still being scanned.
2692 * This function should either successfully create a new dir, or give up
2693 * and leave things the way they were. In particular, if it fails to write
2694 * the new dir properly, it should return w/o changing the reference to the
2697 void CopyAndSalvage(register struct DirSummary *dir)
2699 struct VnodeDiskObject vnode;
2700 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2701 Inode oldinode, newinode;
2703 register afs_int32 code;
2704 afs_int32 parentUnique= 1;
2705 struct VnodeEssence *vnodeEssence;
2709 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2710 code = IH_IREAD(vnodeInfo[vLarge].handle,
2711 vnodeIndexOffset(vcp, dir->vnodeNumber),
2712 (char*)&vnode, sizeof (vnode));
2713 assert(code == sizeof (vnode));
2714 oldinode = VNDISK_GET_INO(&vnode);
2715 /* Increment the version number by a whole lot to avoid problems with
2716 * clients that were promised new version numbers--but the file server
2717 * crashed before the versions were written to disk.
2719 newinode = IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0,
2720 dir->rwVid, dir->vnodeNumber,
2722 vnode.dataVersion += 200);
2723 assert(VALID_INO(newinode));
2724 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2726 /* Assign . and .. vnode numbers from dir and vnode.parent.
2727 * The uniquifier for . is in the vnode.
2728 * The uniquifier for .. might be set to a bogus value of 1 and
2729 * the salvager will later clean it up.
2731 if ( vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent)) ) {
2732 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2734 code = DirSalvage(&dir->dirHandle, &newdir,
2735 dir->vnodeNumber, vnode.uniquifier,
2736 (vnode.parent?vnode.parent:dir->vnodeNumber),
2738 if (code == 0) code = DFlush();
2740 /* didn't really build the new directory properly, let's just give up. */
2741 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2743 Log("Directory salvage returned code %d, continuing.\n", code);
2746 Log("Checking the results of the directory salvage...\n");
2747 if (!DirOK(&newdir)) {
2748 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2749 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2754 VNDISK_SET_INO(&vnode, newinode);
2755 VNDISK_SET_LEN(&vnode, (afs_size_t) Length(&newdir));
2756 code = IH_IWRITE(vnodeInfo[vLarge].handle,
2757 vnodeIndexOffset(vcp, dir->vnodeNumber),
2758 (char*)&vnode, sizeof (vnode));
2759 assert(code == sizeof (vnode));
2761 nt_sync(fileSysDevice);
2763 sync(); /* this is slow, but hopefully rarely called. We don't have
2764 * an open FD on the file itself to fsync.
2767 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2769 dir->dirHandle = newdir;
2772 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2775 struct VnodeEssence *vnodeEssence;
2776 afs_int32 dirOrphaned, todelete;
2778 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2780 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2781 if (vnodeEssence == NULL) {
2783 Log("dir vnode %d: invalid entry deleted: %s/%s (vnode %d, unique %d)\n",
2784 dir->vnodeNumber, dir->name?dir->name:"??",
2785 name, vnodeNumber, unique);
2789 assert(Delete(&dir->dirHandle, name) == 0);
2795 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2796 * mount inode for the partition. If this inode were deleted, it would crash
2799 if (vnodeEssence->InodeNumber == 0) {
2800 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n",
2801 dir->vnodeNumber, (dir->name?dir->name:"??"),
2802 name, vnodeNumber, unique,
2803 (Testing?"-- would have deleted":" -- deleted"));
2806 assert(Delete(&dir->dirHandle, name) == 0);
2812 if (!(vnodeNumber & 1) && !Showmode &&
2813 !(vnodeEssence->count || vnodeEssence->unique || vnodeEssence->modeBits)) {
2814 Log("dir vnode %d: invalid entry: %s/%s (vnode %d, unique %d)%s\n",
2815 dir->vnodeNumber, (dir->name?dir->name:"??"),
2816 name, vnodeNumber, unique,
2817 ((!unique)?(Testing?"-- would have deleted":" -- deleted"):""));
2821 assert(Delete(&dir->dirHandle, name) == 0);
2827 /* Check if the Uniquifiers match. If not, change the directory entry
2828 * so its unique matches the vnode unique. Delete if the unique is zero
2829 * or if the directory is orphaned.
2831 if (!vnodeEssence->unique ||
2832 (vnodeEssence->unique) != unique) {
2833 if (!vnodeEssence->unique &&
2834 ((strcmp(name,"..")==0) || (strcmp(name,".")==0)) ) {
2835 /* This is an orphaned directory. Don't delete the . or ..
2836 * entry. Otherwise, it will get created in the next
2837 * salvage and deleted again here. So Just skip it.
2842 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2845 Log("dir vnode %d: %s/%s (vnode %d): unique changed from %d to %d %s\n",
2846 dir->vnodeNumber, (dir->name ? dir->name : "??"),
2847 name, vnodeNumber, unique, vnodeEssence->unique,
2848 (!todelete?"":(Testing?"-- would have deleted":"-- deleted")));
2852 fid.Vnode = vnodeNumber;
2853 fid.Unique = vnodeEssence->unique;
2855 assert(Delete(&dir->dirHandle, name) == 0);
2857 assert(Create(&dir->dirHandle, name, &fid) == 0);
2859 if (todelete) return; /* no need to continue */
2862 if (strcmp(name,".") == 0) {
2863 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2865 if (!Showmode) Log("directory vnode %d.%d: bad '.' entry (was %d.%d); fixed\n",
2866 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2869 assert(Delete(&dir->dirHandle, ".") == 0);
2870 fid.Vnode = dir->vnodeNumber;
2871 fid.Unique = dir->unique;
2872 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2875 vnodeNumber = fid.Vnode; /* Get the new Essence */
2876 unique = fid.Unique;
2877 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2881 else if (strcmp(name,"..") == 0) {
2884 struct VnodeEssence *dotdot;
2885 pa.Vnode = dir->parent;
2886 dotdot = CheckVnodeNumber(pa.Vnode);
2887 assert (dotdot != NULL); /* XXX Should not be assert */
2888 pa.Unique = dotdot->unique;
2891 pa.Vnode = dir->vnodeNumber;
2892 pa.Unique = dir->unique;
2894 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2895 if (!Showmode) Log("directory vnode %d.%d: bad '..' entry (was %d.%d); fixed\n",
2896 dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2899 assert(Delete(&dir->dirHandle, "..") == 0);
2900 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2903 vnodeNumber = pa.Vnode; /* Get the new Essence */
2905 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2907 dir->haveDotDot = 1;
2908 } else if (strncmp(name,".__afs",6) == 0) {
2910 Log("dir vnode %d: special old unlink-while-referenced file %s %s deleted (vnode %d)\n",
2911 dir->vnodeNumber, name, (Testing?"would have been":"is"), vnodeNumber);
2915 assert(Delete(&dir->dirHandle, name) == 0);
2917 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2918 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2922 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2923 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2924 vnodeEssence->owner, vnodeEssence->group,
2925 vnodeEssence->modeBits, vnodeEssence->author,vnodeNumber,
2927 if (ShowMounts && (vnodeEssence->type == vSymlink) &&
2928 !(vnodeEssence->modeBits & 0111)) {
2934 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
2935 vnodeEssence->InodeNumber);
2937 assert(fdP != NULL);
2938 size = FDH_SIZE(fdP);
2940 memset(buf, 0, 1024);
2941 if (size > 1024) size = 1024;
2942 code = FDH_READ(fdP, buf, size);
2943 assert(code == size);
2944 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
2945 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
2946 dir->name?dir->name:"??", name, buf);
2947 FDH_REALLYCLOSE(fdP);
2950 if (ShowRootFiles && vnodeEssence->owner==0 && vnodeNumber != 1)
2951 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name?dir->name:"??", name,
2952 vnodeEssence->owner, vnodeEssence->group,
2953 vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber,
2955 if (vnodeIdToClass(vnodeNumber) == vLarge &&
2956 vnodeEssence->name == NULL) {
2958 if (n = (char*)malloc(strlen(name)+1))
2960 vnodeEssence->name = n;
2963 /* The directory entry points to the vnode. Check to see if the
2964 * vnode points back to the directory. If not, then let the
2965 * directory claim it (else it might end up orphaned). Vnodes
2966 * already claimed by another directory are deleted from this
2967 * directory: hardlinks to the same vnode are not allowed
2968 * from different directories.
2970 if (vnodeEssence->parent != dir->vnodeNumber) {
2971 if (!vnodeEssence->claimed && !dirOrphaned) {
2972 /* Vnode does not point back to this directory.
2973 * Orphaned dirs cannot claim a file (it may belong to
2974 * another non-orphaned dir).
2977 Log("dir vnode %d: %s/%s (vnode %d, unique %d) -- parent vnode %schanged from %d to %d\n",
2978 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2979 vnodeNumber, unique, (Testing?"would have been ":""),
2980 vnodeEssence->parent, dir->vnodeNumber);
2982 vnodeEssence->parent = dir->vnodeNumber;
2983 vnodeEssence->changed = 1;
2985 /* Vnode was claimed by another directory */
2988 Log("dir vnode %d: %s/%s parent vnode is %d (vnode %d, unique %d) -- %sdeleted\n",
2989 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2990 vnodeEssence->parent, vnodeNumber, unique,
2991 (Testing?"would have been ":""));
2993 Log("dir vnode %d: %s/%s already claimed by directory vnode %d (vnode %d, unique %d) -- %sdeleted\n",
2994 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2995 vnodeEssence->parent, vnodeNumber, unique,
2996 (Testing?"would have been ":""));
3001 assert(Delete(&dir->dirHandle, name) == 0);
3006 /* This directory claims the vnode */
3007 vnodeEssence->claimed = 1;
3009 vnodeEssence->count--;
3012 void DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino,
3015 register struct VnodeInfo *vip = &vnodeInfo[class];
3016 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3017 char buf[SIZEOF_LARGEDISKVNODE];
3018 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
3020 StreamHandle_t *file;
3025 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3026 fdP = IH_OPEN(vip->handle);
3027 assert(fdP != NULL);
3028 file = FDH_FDOPEN(fdP, "r+");
3029 assert(file != NULL);
3030 size = OS_SIZE(fdP->fd_fd);
3032 vip->nVnodes = (size / vcp->diskSize) - 1;
3033 if (vip->nVnodes > 0) {
3034 assert((vip->nVnodes+1)*vcp->diskSize == size);
3035 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3036 assert((vip->vnodes = (struct VnodeEssence *)
3037 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3038 if (class == vLarge) {
3039 assert((vip->inodes = (Inode *)
3040 calloc(vip->nVnodes, sizeof (Inode))) != NULL);
3051 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3052 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3053 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3054 nVnodes--, vnodeIndex++) {
3055 if (vnode->type != vNull) {
3056 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3057 afs_size_t vnodeLength;
3058 vip->nAllocatedVnodes++;
3059 vep->count = vnode->linkCount;
3060 VNDISK_GET_LEN(vnodeLength, vnode);
3061 vep->blockCount = nBlocks(vnodeLength);
3062 vip->volumeBlockCount += vep->blockCount;
3063 vep->parent = vnode->parent;
3064 vep->unique = vnode->uniquifier;
3065 if (*maxu < vnode->uniquifier)
3066 *maxu = vnode->uniquifier;
3067 vep->modeBits = vnode->modeBits;
3068 vep->InodeNumber = VNDISK_GET_INO(vnode);
3069 vep->type = vnode->type;
3070 vep->author = vnode->author;
3071 vep->owner = vnode->owner;
3072 vep->group = vnode->group;
3073 if (vnode->type == vDirectory) {
3074 assert(class == vLarge);
3075 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3083 static char *GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3085 struct VnodeEssence *parentvp;
3091 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent)) && GetDirName(vp->parent, parentvp, path)) {
3093 strcat(path, vp->name);
3099 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3100 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3102 static int IsVnodeOrphaned(VnodeId vnode)
3104 struct VnodeEssence *vep;
3106 if (vnode == 0) return(1); /* Vnode zero does not exist */
3107 if (vnode == 1) return(0); /* The root dir vnode is always claimed */
3108 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3109 if (!vep || !vep->claimed) return(1); /* Vnode is not claimed - it is orphaned */
3111 return( IsVnodeOrphaned(vep->parent) );
3114 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3115 IHandle_t *alinkH, int i, struct DirSummary *rootdir,
3118 static struct DirSummary dir;
3119 static struct DirHandle dirHandle;
3120 struct VnodeEssence *parent;
3121 static char path[MAXPATHLEN];
3124 if (dirVnodeInfo->vnodes[i].salvaged)
3125 return; /* already salvaged */
3128 dirVnodeInfo->vnodes[i].salvaged = 1;
3130 if (dirVnodeInfo->inodes[i] == 0)
3131 return; /* Not allocated to a directory */
3133 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3134 if (parent && parent->salvaged == 0)
3135 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3136 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3137 rootdir, rootdirfound);
3138 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3139 dir.unique = dirVnodeInfo->vnodes[i].unique;
3142 dir.parent = dirVnodeInfo->vnodes[i].parent;
3143 dir.haveDot = dir.haveDotDot = 0;
3144 dir.ds_linkH = alinkH;
3145 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice, dirVnodeInfo->inodes[i]);
3147 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3150 Log("Directory bad, vnode %d; %s...\n",
3151 dir.vnodeNumber, (Testing ? "skipping" : "salvaging"));
3154 CopyAndSalvage(&dir);
3158 dirHandle = dir.dirHandle;
3160 dir.name = GetDirName(bitNumberToVnodeNumber(i,vLarge), &dirVnodeInfo->vnodes[i], path);
3163 /* If enumeration failed for random reasons, we will probably delete
3164 * too much stuff, so we guard against this instead.
3166 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3169 /* Delete the old directory if it was copied in order to salvage.
3170 * CopyOnWrite has written the new inode # to the disk, but we still
3171 * have the old one in our local structure here. Thus, we idec the
3175 if (dir.copied && !Testing) {
3176 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3178 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3181 /* Remember rootdir DirSummary _after_ it has been judged */
3182 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3183 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3190 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t *alinkH)
3192 /* This routine, for now, will only be called for read-write volumes */
3194 int BlocksInVolume = 0, FilesInVolume = 0;
3195 register VnodeClass class;
3196 struct DirSummary rootdir, oldrootdir;
3197 struct VnodeInfo *dirVnodeInfo;
3198 struct VnodeDiskObject vnode;
3199 VolumeDiskData volHeader;
3201 int orphaned, rootdirfound = 0;
3202 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3203 afs_int32 ofiles=0, oblocks=0; /* Number of orphaned files/blocks */
3204 struct VnodeEssence *vep;
3209 VnodeId LFVnode, ThisVnode;
3210 Unique LFUnique, ThisUnique;
3213 vid = rwIsp->volSummary->header.id;
3214 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3215 nBytes = IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader));
3216 assert(nBytes == sizeof(volHeader));
3217 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3218 assert (volHeader.destroyMe != DESTROY_ME);
3219 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3221 DistilVnodeEssence(vid, vLarge,
3222 rwIsp->volSummary->header.largeVnodeIndex,
3224 DistilVnodeEssence(vid, vSmall,
3225 rwIsp->volSummary->header.smallVnodeIndex,
3228 dirVnodeInfo = &vnodeInfo[vLarge];
3229 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3230 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i,
3231 &rootdir, &rootdirfound);
3238 /* Parse each vnode looking for orphaned vnodes and
3239 * connect them to the tree as orphaned (if requested).
3241 oldrootdir = rootdir;
3242 for (class=0; class < nVNODECLASSES; class++) {
3243 for (v=0; v < vnodeInfo[class].nVnodes; v++) {
3244 vep = &(vnodeInfo[class].vnodes[v]);
3245 ThisVnode = bitNumberToVnodeNumber(v, class);
3246 ThisUnique = vep->unique;
3248 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3249 continue; /* Ignore unused, claimed, and root vnodes */
3251 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3252 * entry in this vnode had incremented the parent link count (In
3253 * JudgeEntry()). We need to go to the parent and decrement that
3254 * link count. But if the parent's unique is zero, then the parent
3255 * link count was not incremented in JudgeEntry().
3257 if (class == vLarge) { /* directory vnode */
3258 pv = vnodeIdToBitNumber(vep->parent);
3259 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3260 vnodeInfo[vLarge].vnodes[pv].count++;
3264 continue; /* If no rootdir, can't attach orphaned files */
3266 /* Here we attach orphaned files and directories into the
3267 * root directory, LVVnode, making sure link counts stay correct.
3269 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3270 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3271 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3273 /* Update this orphaned vnode's info. Its parent info and
3274 * link count (do for orphaned directories and files).
3276 vep->parent = LFVnode; /* Parent is the root dir */
3277 vep->unique = LFUnique;
3280 vep->count--; /* Inc link count (root dir will pt to it) */
3282 /* If this orphaned vnode is a directory, change '..'.
3283 * The name of the orphaned dir/file is unknown, so we
3284 * build a unique name. No need to CopyOnWrite the directory
3285 * since it is not connected to tree in BK or RO volume and
3286 * won't be visible there.
3288 if (class == vLarge) {
3292 /* Remove and recreate the ".." entry in this orphaned directory */
3293 SetSalvageDirHandle(&dh,vid,fileSysDevice,vnodeInfo[class].inodes[v]);
3295 pa.Unique = LFUnique;
3296 assert(Delete(&dh, "..") == 0);
3297 assert(Create(&dh, "..", &pa) == 0);
3299 /* The original parent's link count was decremented above.
3300 * Here we increment the new parent's link count.
3302 pv = vnodeIdToBitNumber(LFVnode);
3303 vnodeInfo[vLarge].vnodes[pv].count--;
3307 /* Go to the root dir and add this entry. The link count of the
3308 * root dir was incremented when ".." was created. Try 10 times.
3310 for (j=0; j<10; j++) {
3311 pa.Vnode = ThisVnode;
3312 pa.Unique = ThisUnique;
3314 sprintf(npath, "%s.%d.%d",
3315 ((class == vLarge)?"__ORPHANDIR__":"__ORPHANFILE__"),
3316 ThisVnode, ThisUnique);
3318 CopyOnWrite(&rootdir);
3319 code = Create(&rootdir.dirHandle, npath, &pa);
3322 ThisUnique += 50; /* Try creating a different file */
3325 Log("Attaching orphaned %s to volume's root dir as %s\n",
3326 ((class == vLarge)?"directory":"file"), npath);
3328 } /* for each vnode in the class */
3329 } /* for each class of vnode */
3331 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3333 if (!oldrootdir.copied && rootdir.copied) {
3334 code = IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode, oldrootdir.rwVid);
3336 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3339 DFlush(); /* Flush the changes */
3340 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3341 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3342 orphans = ORPH_IGNORE;
3345 /* Write out all changed vnodes. Orphaned files and directories
3346 * will get removed here also (if requested).
3348 for (class = 0; class < nVNODECLASSES; class++){
3349 int nVnodes = vnodeInfo[class].nVnodes;
3350 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3351 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3352 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3353 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3354 for (i = 0; i<nVnodes; i++) {
3355 register struct VnodeEssence *vnp = &vnodes[i];
3356 VnodeId vnodeNumber = bitNumberToVnodeNumber(i,class);
3358 /* If the vnode is good but is unclaimed (not listed in
3359 * any directory entries), then it is orphaned.
3362 if ((vnp->type != 0) && (orphaned=IsVnodeOrphaned(vnodeNumber))) {
3363 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3367 if (vnp->changed || vnp->count) {
3370 nBytes = IH_IREAD(vnodeInfo[class].handle,
3371 vnodeIndexOffset(vcp, vnodeNumber),
3372 (char*)&vnode, sizeof (vnode));
3373 assert(nBytes == sizeof(vnode));
3375 vnode.parent = vnp->parent;
3376 oldCount = vnode.linkCount;
3377 vnode.linkCount = vnode.linkCount - vnp->count;
3380 orphaned = IsVnodeOrphaned(vnodeNumber);
3382 if (!vnp->todelete) {
3383 /* Orphans should have already been attached (if requested) */
3384 assert(orphans != ORPH_ATTACH);
3385 oblocks += vnp->blockCount;
3388 if (((orphans == ORPH_REMOVE) || vnp->todelete) && !Testing) {
3389 BlocksInVolume -= vnp->blockCount;
3391 if (VNDISK_GET_INO(&vnode)) {
3392 code = IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3395 memset(&vnode, 0, sizeof(vnode));
3397 } else if (vnp->count) {
3399 Log("Vnode %d: link count incorrect (was %d, %s %d)\n",
3400 vnodeNumber, oldCount,
3401 (Testing?"would have changed to":"now"), vnode.linkCount);
3405 vnode.dataVersion++;
3407 nBytes = IH_IWRITE(vnodeInfo[class].handle,