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>
101 #include <sys/stat.h>
106 #include <WINNT/afsevent.h>
108 #include <sys/param.h>
109 #include <sys/file.h>
111 #include <sys/time.h>
112 #endif /* ITIMER_REAL */
114 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
115 #define WCOREDUMP(x) (x & 0200)
118 #include <afs/afsint.h>
119 #include <afs/assert.h>
120 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
121 #if defined(AFS_VFSINCL_ENV)
122 #include <sys/vnode.h>
124 #include <sys/fs/ufs_inode.h>
126 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
127 #include <ufs/ufs/dinode.h>
128 #include <ufs/ffs/fs.h>
130 #include <ufs/inode.h>
133 #else /* AFS_VFSINCL_ENV */
135 #include <ufs/inode.h>
136 #else /* AFS_OSF_ENV */
137 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
138 #include <sys/inode.h>
141 #endif /* AFS_VFSINCL_ENV */
142 #endif /* AFS_SGI_ENV */
145 #include <sys/lockf.h>
149 #include <checklist.h>
151 #if defined(AFS_SGI_ENV)
156 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
159 #include <sys/mnttab.h>
160 #include <sys/mntent.h>
165 #endif /* AFS_SGI_ENV */
166 #endif /* AFS_HPUX_ENV */
171 #include <afs/osi_inode.h>
174 #include <afs/afsutil.h>
175 #include <afs/fileutil.h>
176 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
184 #include <afs/afssyscalls.h>
188 #include "partition.h"
190 #include "viceinode.h"
192 #include "volinodes.h" /* header magic number, etc. stuff */
197 /*@+fcnmacros +macrofcndecl@*/
200 extern off64_t afs_lseek(int FD, off64_t O, int F);
201 #endif /*S_SPLINT_S */
202 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
203 #define afs_stat stat64
204 #define afs_fstat fstat64
205 #define afs_open open64
206 #define afs_fopen fopen64
207 #else /* !O_LARGEFILE */
209 extern off_t afs_lseek(int FD, off_t O, int F);
210 #endif /*S_SPLINT_S */
211 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
212 #define afs_stat stat
213 #define afs_fstat fstat
214 #define afs_open open
215 #define afs_fopen fopen
216 #endif /* !O_LARGEFILE */
217 /*@=fcnmacros =macrofcndecl@*/
220 extern void *calloc();
222 static char *TimeStamp(time_t clock, int precision);
224 #define ORPH_IGNORE 0
225 #define ORPH_REMOVE 1
226 #define ORPH_ATTACH 2
229 int debug; /* -d flag */
230 int Testing = 0; /* -n flag */
231 int ListInodeOption; /* -i flag */
232 int ShowRootFiles; /* -r flag */
233 int RebuildDirs; /* -sal flag */
234 int Parallel = 4; /* -para X flag */
235 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
236 int forceR = 0; /* -b flag */
237 int ShowLog = 0; /* -showlog flag */
238 int ShowSuid = 0; /* -showsuid flag */
239 int ShowMounts = 0; /* -showmounts flag */
240 int orphans = ORPH_IGNORE; /* -orphans option */
244 int useSyslog = 0; /* -syslog flag */
245 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
248 #define MAXPARALLEL 32
250 int OKToZap; /* -o flag */
251 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
252 * in the volume header */
254 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
256 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
258 Device fileSysDevice; /* The device number of the current
259 * partition being salvaged */
263 char *fileSysPath; /* The path of the mounted partition currently
264 * being salvaged, i.e. the directory
265 * containing the volume headers */
267 char *fileSysPathName; /* NT needs this to make name pretty in log. */
268 IHandle_t *VGLinkH; /* Link handle for current volume group. */
269 int VGLinkH_cnt; /* # of references to lnk handle. */
270 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
272 char *fileSysDeviceName; /* The block device where the file system
273 * being salvaged was mounted */
274 char *filesysfulldev;
276 int VolumeChanged; /* Set by any routine which would change the volume in
277 * a way which would require callback is to be broken if the
278 * volume was put back on line by an active file server */
280 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
282 struct InodeSummary { /* Inode summary file--an entry for each
283 * volume in the inode file for a partition */
284 VolId volumeId; /* Volume id */
285 VolId RWvolumeId; /* RW volume associated */
286 int index; /* index into inode file (0, 1, 2 ...) */
287 int nInodes; /* Number of inodes for this volume */
288 int nSpecialInodes; /* Number of special inodes, i.e. volume
289 * header, index, etc. These are all
290 * marked (viceinode.h) and will all be sorted
291 * to the beginning of the information for
292 * this volume. Read-only volumes should
293 * ONLY have special inodes (all the other
294 * inodes look as if they belong to the
295 * original RW volume). */
296 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
297 * This is only useful for RW volumes and is used
298 * to compute a new volume uniquifier in the event
299 * that the header needs to be recreated. The inode
300 * uniquifier may be a truncated version of vnode
301 * uniquifier (AFS_3DISPARES). The real maxUniquifer
302 * is from the vnodes and later calcuated from it */
303 struct VolumeSummary *volSummary;
304 /* Either a pointer to the original volume
305 * header summary, or constructed summary
308 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
309 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
310 int inodeFd; /* File descriptor for inode file */
313 struct VolumeSummary { /* Volume summary an entry for each
314 * volume in a volume directory.
315 * Assumption: one volume directory per
317 char *fileName; /* File name on the partition for the volume
319 struct VolumeHeader header;
320 /* volume number, rw volume number, inode
321 * numbers of each major component of
323 IHandle_t *volumeInfoHandle;
324 byte wouldNeedCallback; /* set if the file server should issue
325 * call backs for all the files in this volume when
326 * the volume goes back on line */
330 IHandle_t *handle; /* Inode containing this index */
331 int nVnodes; /* Total number of vnodes in index */
332 int nAllocatedVnodes; /* Total number actually used */
333 int volumeBlockCount; /* Total number of blocks used by volume */
334 Inode *inodes; /* Directory only */
335 struct VnodeEssence {
336 short count; /* Number of references to vnode; MUST BE SIGNED */
337 unsigned claimed:1; /* Set when a parent directory containing an entry
338 * referencing this vnode is found. The claim
339 * is that the parent in "parent" can point to
340 * this vnode, and no other */
341 unsigned changed:1; /* Set if any parameters (other than the count)
342 * in the vnode change. It is determined if the
343 * link count has changed by noting whether it is
344 * 0 after scanning all directories */
345 unsigned salvaged:1; /* Set if this directory vnode has already been salvaged. */
346 unsigned todelete:1; /* Set if this vnode is to be deleted (should not be claimed) */
347 afs_fsize_t blockCount;
348 /* Number of blocks (1K) used by this vnode,
350 VnodeId parent; /* parent in vnode */
351 Unique unique; /* Must match entry! */
352 char *name; /* Name of directory entry */
353 int modeBits; /* File mode bits */
354 Inode InodeNumber; /* file's inode */
355 int type; /* File type */
356 int author; /* File author */
357 int owner; /* File owner */
358 int group; /* File group */
360 } vnodeInfo[nVNODECLASSES];
363 struct DirHandle dirHandle;
366 unsigned haveDot, haveDotDot;
368 int copied; /* If the copy-on-write stuff has been applied */
376 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
377 int nVolumes; /* Number of volumes (read-write and read-only)
378 * in volume summary */
381 /* For NT, we can fork the per partition salvagers to gain the required
382 * safety against Aborts. But there's too many complex data structures at
383 * the per volume salvager layer to easilty copy the data across.
384 * childJobNumber is resset from -1 to the job number if this is a
385 * per partition child of the main salvager. This information is passed
386 * out-of-band in the extra data area setup for the now unused parent/child
389 #define SALVAGER_MAGIC 0x00BBaaDD
390 #define NOT_CHILD -1 /* job numbers start at 0 */
391 /* If new options need to be passed to child, add them here. */
398 /* Child job this process is running. */
399 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
401 int nt_SalvagePartition(char *partName, int jobn);
402 int nt_SetupPartitionSalvage(void *datap, int len);
405 struct InodeSummary *svgp_inodeSummaryp;
415 /* Forward declarations */
416 /*@printflike@*/ void Log(const char *format, ...);
417 /*@printflike@*/ void Abort(const char *format, ...);
420 int Wait(char *prog);
421 char *ToString(char *s);
422 void AskOffline(VolumeId volumeId);
423 void AskOnline(VolumeId volumeId, char *partition);
424 void CheckLogFile(void);
426 void TimeStampLogFile(void);
428 void ClearROInUseBit(struct VolumeSummary *summary);
429 void CopyAndSalvage(register struct DirSummary *dir);
430 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
431 void CopyOnWrite(register struct DirSummary *dir);
432 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
433 register struct InodeSummary *summary);
434 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
435 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
437 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
438 void GetVolumeSummary(VolumeId singleVolumeNumber);
439 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
441 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
442 int deleteMe, int check);
443 void ObtainSalvageLock(void);
444 void PrintInodeList(void);
445 void PrintInodeSummary(void);
446 void PrintVolumeSummary(void);
447 int QuickCheck(register struct InodeSummary *isp, int nVols);
448 void RemoveTheForce(char *path);
449 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
450 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
452 void SalvageFileSysParallel(struct DiskPartition *partP);
453 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
454 void SalvageFileSys1(struct DiskPartition *partP,
455 VolumeId singleVolumeNumber);
456 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
457 int check, int *deleteMe);
458 int SalvageIndex(Inode ino, VnodeClass class, int RW,
459 register struct ViceInodeInfo *ip, int nInodes,
460 struct VolumeSummary *volSummary, int check);
461 int SalvageVnodes(register struct InodeSummary *rwIsp,
462 register struct InodeSummary *thisIsp,
463 register struct ViceInodeInfo *inodes, int check);
464 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH);
465 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
467 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
469 #define SalvageVolumeGroup DoSalvageVolumeGroup
471 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
472 register struct ViceInodeInfo *inodes, int RW,
473 int check, int *deleteMe);
475 int UseTheForceLuke(char *path);
477 static int IsVnodeOrphaned(VnodeId vnode);
479 /* Uniquifier stored in the Inode */
484 return (u & 0x3fffff);
486 #if defined(AFS_SGI_EXMAG)
487 return (u & SGI_UNIQMASK);
490 #endif /* AFS_SGI_EXMAG */
495 BadError(register int aerror)
497 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
499 return 0; /* otherwise may be transient, e.g. EMFILE */
505 handleit(struct cmd_syndesc *as)
507 register struct cmd_item *ti;
508 char pname[100], *temp;
509 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0;
510 struct DiskPartition *partP;
512 #ifdef AFS_SGI_VNODE_GLUE
513 if (afs_init_kernel_config(-1) < 0) {
515 ("Can't determine NUMA configuration, not starting salvager.\n");
523 for (i = 0; i < CMD_MAXPARMS; i++) {
524 if (as->parms[i].items) {
532 "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
541 #endif /* FAST_RESTART */
542 if ((ti = as->parms[0].items)) { /* -partition */
544 strncpy(pname, ti->data, 100);
546 if ((ti = as->parms[1].items)) { /* -volumeid */
549 ("You must also specify '-partition' option with the '-volumeid' option\n");
553 vid = atoi(ti->data);
555 if (as->parms[2].items) /* -debug */
557 if (as->parms[3].items) /* -nowrite */
559 if (as->parms[4].items) /* -inodes */
561 if (as->parms[5].items) /* -force */
563 if (as->parms[6].items) /* -oktozap */
565 if (as->parms[7].items) /* -rootinodes */
567 if (as->parms[8].items) /* -RebuildDirs */
569 if (as->parms[9].items) /* -ForceReads */
571 if ((ti = as->parms[10].items)) { /* -Parallel # */
573 if (strncmp(temp, "all", 3) == 0) {
577 if (strlen(temp) != 0) {
578 Parallel = atoi(temp);
581 if (Parallel > MAXPARALLEL) {
582 printf("Setting parallel salvages to maximum of %d \n",
584 Parallel = MAXPARALLEL;
588 if ((ti = as->parms[11].items)) { /* -tmpdir */
592 dirp = opendir(tmpdir);
595 ("Can't open temporary placeholder dir %s; using current partition \n",
601 if ((ti = as->parms[12].items)) /* -showlog */
603 if ((ti = as->parms[13].items)) { /* -log */
608 if ((ti = as->parms[14].items)) { /* -showmounts */
613 if ((ti = as->parms[15].items)) { /* -orphans */
615 orphans = ORPH_IGNORE;
616 else if (strcmp(ti->data, "remove") == 0
617 || strcmp(ti->data, "r") == 0)
618 orphans = ORPH_REMOVE;
619 else if (strcmp(ti->data, "attach") == 0
620 || strcmp(ti->data, "a") == 0)
621 orphans = ORPH_ATTACH;
623 #ifndef AFS_NT40_ENV /* ignore options on NT */
624 if ((ti = as->parms[16].items)) { /* -syslog */
628 if ((ti = as->parms[17].items)) { /* -syslogfacility */
629 useSyslogFacility = atoi(ti->data);
632 if ((ti = as->parms[18].items)) { /* -datelogs */
638 if (ti = as->parms[19].items) { /* -DontSalvage */
640 "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
648 #endif /* FAST_RESTART */
650 /* Note: if seemvol we initialize this as a standard volume utility: this has the
651 * implication that the file server may be running; negotations have to be made with
652 * the file server in this case to take the read write volume and associated read-only
653 * volumes off line before salvaging */
656 if (afs_winsockInit() < 0) {
657 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
658 AFSDIR_SALVAGER_FILE, 0);
659 Log("Failed to initailize winsock, exiting.\n");
664 VInitVolumePackage(seenvol ? volumeUtility : salvager, 5, 5,
668 if (myjob.cj_number != NOT_CHILD) {
671 (void)strcpy(pname, myjob.cj_part);
676 for (partP = DiskPartitionList; partP; partP = partP->next) {
677 SalvageFileSysParallel(partP);
679 SalvageFileSysParallel(0);
681 partP = VGetPartition(pname, 0);
683 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n", pname);
687 SalvageFileSys(partP, 0);
689 /* Salvage individual volume */
691 Log("salvage: invalid volume id specified; salvage aborted\n");
694 SalvageFileSys(partP, vid);
702 #include "AFS_component_version_number.c"
706 char *save_args[MAX_ARGS];
708 pthread_t main_thread;
712 main(int argc, char **argv)
714 struct cmd_syndesc *ts;
716 char commandLine[150];
719 extern char cml_version_number[];
723 * The following signal action for AIX is necessary so that in case of a
724 * crash (i.e. core is generated) we can include the user's data section
725 * in the core dump. Unfortunately, by default, only a partial core is
726 * generated which, in many cases, isn't too useful.
728 struct sigaction nsa;
730 sigemptyset(&nsa.sa_mask);
731 nsa.sa_handler = SIG_DFL;
732 nsa.sa_flags = SA_FULLDUMP;
733 sigaction(SIGABRT, &nsa, NULL);
734 sigaction(SIGSEGV, &nsa, NULL);
737 /* Initialize directory paths */
738 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
740 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
742 fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
747 main_thread = pthread_self();
748 if (spawnDatap && spawnDataLen) {
749 /* This is a child per partition salvager. Don't setup log or
750 * try to lock the salvager lock.
752 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen) < 0)
756 for (commandLine[0] = '\0', i = 0; i < argc; i++) {
758 strcat(commandLine, " ");
759 strcat(commandLine, argv[i]);
762 /* All entries to the log will be appended. Useful if there are
763 * multiple salvagers appending to the log.
768 #ifdef AFS_LINUX20_ENV
769 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
771 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
777 if (geteuid() != 0) {
778 printf("Salvager must be run as root.\n");
784 /* bad for normal help flag processing, but can do nada */
786 fprintf(logFile, "%s\n", cml_version_number);
787 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
789 /* Get and hold a lock for the duration of the salvage to make sure
790 * that no other salvage runs at the same time. The routine
791 * VInitVolumePackage (called below) makes sure that a file server or
792 * other volume utilities don't interfere with the salvage.
799 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
800 cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL,
801 "Name of partition to salvage");
802 cmd_AddParm(ts, "-volumeid", CMD_SINGLE, CMD_OPTIONAL,
803 "Volume Id to salvage");
804 cmd_AddParm(ts, "-debug", CMD_FLAG, CMD_OPTIONAL,
805 "Run in Debugging mode");
806 cmd_AddParm(ts, "-nowrite", CMD_FLAG, CMD_OPTIONAL,
807 "Run readonly/test mode");
808 cmd_AddParm(ts, "-inodes", CMD_FLAG, CMD_OPTIONAL,
809 "Just list affected afs inodes - debugging flag");
810 cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "Force full salvaging");
811 cmd_AddParm(ts, "-oktozap", CMD_FLAG, CMD_OPTIONAL,
812 "Give permission to destroy bogus inodes/volumes - debugging flag");
813 cmd_AddParm(ts, "-rootinodes", CMD_FLAG, CMD_OPTIONAL,
814 "Show inodes owned by root - debugging flag");
815 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG, CMD_OPTIONAL,
816 "Force rebuild/salvage of all directories");
817 cmd_AddParm(ts, "-blockreads", CMD_FLAG, CMD_OPTIONAL,
818 "Read smaller blocks to handle IO/bad blocks");
819 cmd_AddParm(ts, "-parallel", CMD_SINGLE, CMD_OPTIONAL,
820 "# of max parallel partition salvaging");
821 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE, CMD_OPTIONAL,
822 "Name of dir to place tmp files ");
823 cmd_AddParm(ts, "-showlog", CMD_FLAG, CMD_OPTIONAL,
824 "Show log file upon completion");
825 cmd_AddParm(ts, "-showsuid", CMD_FLAG, CMD_OPTIONAL,
826 "Report on suid/sgid files");
827 cmd_AddParm(ts, "-showmounts", CMD_FLAG, CMD_OPTIONAL,
828 "Report on mountpoints");
829 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL,
830 "ignore | remove | attach");
832 /* note - syslog isn't avail on NT, but if we make it conditional, have
833 * to deal with screwy offsets for cmd params */
834 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL,
835 "Write salvage log to syslogs");
836 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL,
837 "Syslog facility number to use");
838 cmd_AddParm(ts, "-datelogs", CMD_FLAG, CMD_OPTIONAL,
839 "Include timestamp in logfile filename");
842 cmd_AddParm(ts, "-DontSalvage", CMD_FLAG, CMD_OPTIONAL,
843 "Don't salvage. This my be set in BosConfig to let the fileserver restart immediately after a crash. Bad volumes will be taken offline");
844 #endif /* FAST_RESTART */
845 err = cmd_Dispatch(argc, argv);
849 /* Get the salvage lock if not already held. Hold until process exits. */
851 ObtainSalvageLock(void)
857 (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
858 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
859 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
861 "salvager: There appears to be another salvager running! Aborted.\n");
866 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
867 if (salvageLock < 0) {
869 "salvager: can't open salvage lock file %s, aborting\n",
870 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
873 #ifdef AFS_DARWIN_ENV
874 if (flock(salvageLock, LOCK_EX) == -1) {
876 if (lockf(salvageLock, F_LOCK, 0) == -1) {
879 "salvager: There appears to be another salvager running! Aborted.\n");
886 #ifdef AFS_SGI_XFS_IOPS_ENV
887 /* Check if the given partition is mounted. For XFS, the root inode is not a
888 * constant. So we check the hard way.
891 IsPartitionMounted(char *part)
894 struct mntent *mntent;
896 assert(mntfp = setmntent(MOUNTED, "r"));
897 while (mntent = getmntent(mntfp)) {
898 if (!strcmp(part, mntent->mnt_dir))
903 return mntent ? 1 : 1;
906 /* Check if the given inode is the root of the filesystem. */
907 #ifndef AFS_SGI_XFS_IOPS_ENV
909 IsRootInode(struct afs_stat *status)
912 * The root inode is not a fixed value in XFS partitions. So we need to
913 * see if the partition is in the list of mounted partitions. This only
914 * affects the SalvageFileSys path, so we check there.
916 return (status->st_ino == ROOTINODE);
921 #ifndef AFS_NAMEI_ENV
922 /* We don't want to salvage big files filesystems, since we can't put volumes on
926 CheckIfBigFilesFS(char *mountPoint, char *devName)
928 struct superblock fs;
931 if (strncmp(devName, "/dev/", 5)) {
932 (void)sprintf(name, "/dev/%s", devName);
934 (void)strcpy(name, devName);
937 if (ReadSuper(&fs, name) < 0) {
938 Log("Unable to read superblock. Not salvaging partition %s.\n",
942 if (IsBigFilesFileSystem(&fs)) {
943 Log("Partition %s is a big files filesystem, not salvaging.\n",
953 #define HDSTR "\\Device\\Harddisk"
954 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
956 SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
961 static int dowarn = 1;
963 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
965 if (strncmp(res, HDSTR, HDLEN)) {
968 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
969 res, HDSTR, p1->devName);
973 d1 = atoi(&res[HDLEN]);
975 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
977 if (strncmp(res, HDSTR, HDLEN)) {
980 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
981 res, HDSTR, p2->devName);
985 d2 = atoi(&res[HDLEN]);
990 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
993 /* This assumes that two partitions with the same device number divided by
994 * PartsPerDisk are on the same disk.
997 SalvageFileSysParallel(struct DiskPartition *partP)
1000 struct DiskPartition *partP;
1001 int pid; /* Pid for this job */
1002 int jobnumb; /* Log file job number */
1003 struct job *nextjob; /* Next partition on disk to salvage */
1005 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
1006 struct job *thisjob = 0;
1007 static int numjobs = 0;
1008 static int jobcount = 0;
1014 char logFileName[256];
1018 /* We have a partition to salvage. Copy it into thisjob */
1019 thisjob = (struct job *)malloc(sizeof(struct job));
1021 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
1024 memset(thisjob, 0, sizeof(struct job));
1025 thisjob->partP = partP;
1026 thisjob->jobnumb = jobcount;
1028 } else if (jobcount == 0) {
1029 /* We are asking to wait for all jobs (partp == 0), yet we never
1032 Log("No file system partitions named %s* found; not salvaged\n",
1033 VICE_PARTITION_PREFIX);
1037 if (debug || Parallel == 1) {
1039 SalvageFileSys(thisjob->partP, 0);
1046 /* Check to see if thisjob is for a disk that we are already
1047 * salvaging. If it is, link it in as the next job to do. The
1048 * jobs array has 1 entry per disk being salvages. numjobs is
1049 * the total number of disks currently being salvaged. In
1050 * order to keep thejobs array compact, when a disk is
1051 * completed, the hightest element in the jobs array is moved
1052 * down to now open slot.
1054 for (j = 0; j < numjobs; j++) {
1055 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
1056 /* On same disk, add it to this list and return */
1057 thisjob->nextjob = jobs[j]->nextjob;
1058 jobs[j]->nextjob = thisjob;
1065 /* Loop until we start thisjob or until all existing jobs are finished */
1066 while (thisjob || (!partP && (numjobs > 0))) {
1067 startjob = -1; /* No new job to start */
1069 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
1070 /* Either the max jobs are running or we have to wait for all
1071 * the jobs to finish. In either case, we wait for at least one
1072 * job to finish. When it's done, clean up after it.
1074 pid = wait(&wstatus);
1076 for (j = 0; j < numjobs; j++) { /* Find which job it is */
1077 if (pid == jobs[j]->pid)
1080 assert(j < numjobs);
1081 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1082 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1085 numjobs--; /* job no longer running */
1086 oldjob = jobs[j]; /* remember */
1087 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1088 free(oldjob); /* free the old job */
1090 /* If there is another partition on the disk to salvage, then
1091 * say we will start it (startjob). If not, then put thisjob there
1092 * and say we will start it.
1094 if (jobs[j]) { /* Another partitions to salvage */
1095 startjob = j; /* Will start it */
1096 } else { /* There is not another partition to salvage */
1098 jobs[j] = thisjob; /* Add thisjob */
1100 startjob = j; /* Will start it */
1102 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1103 startjob = -1; /* Don't start it - already running */
1107 /* We don't have to wait for a job to complete */
1109 jobs[numjobs] = thisjob; /* Add this job */
1111 startjob = numjobs; /* Will start it */
1115 /* Start up a new salvage job on a partition in job slot "startjob" */
1116 if (startjob != -1) {
1118 Log("Starting salvage of file system partition %s\n",
1119 jobs[startjob]->partP->name);
1121 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1122 * commands and pass the child job number via the data path.
1125 nt_SalvagePartition(jobs[startjob]->partP->name,
1126 jobs[startjob]->jobnumb);
1127 jobs[startjob]->pid = pid;
1132 jobs[startjob]->pid = pid;
1138 for (fd = 0; fd < 16; fd++)
1143 #ifndef AFS_NT40_ENV
1145 openlog("salvager", LOG_PID, useSyslogFacility);
1149 (void)afs_snprintf(logFileName, sizeof logFileName,
1151 AFSDIR_SERVER_SLVGLOG_FILEPATH,
1152 jobs[startjob]->jobnumb);
1153 logFile = afs_fopen(logFileName, "w");
1158 SalvageFileSys1(jobs[startjob]->partP, 0);
1163 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1165 /* If waited for all jobs to complete, now collect log files and return */
1166 #ifndef AFS_NT40_ENV
1167 if (!useSyslog) /* if syslogging - no need to collect */
1170 for (i = 0; i < jobcount; i++) {
1171 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
1172 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1173 if ((passLog = afs_fopen(logFileName, "r"))) {
1174 while (fgets(buf, sizeof(buf), passLog)) {
1175 fputs(buf, logFile);
1179 (void)unlink(logFileName);
1188 SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1190 if (!canfork || debug || Fork() == 0) {
1191 SalvageFileSys1(partP, singleVolumeNumber);
1192 if (canfork && !debug) {
1197 Wait("SalvageFileSys");
1201 get_DevName(char *pbuffer, char *wpath)
1203 char pbuf[128], *ptr;
1204 strcpy(pbuf, pbuffer);
1205 ptr = (char *)strrchr(pbuf, '/');
1208 strcpy(wpath, pbuf);
1211 ptr = (char *)strrchr(pbuffer, '/');
1213 strcpy(pbuffer, ptr + 1);
1220 SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1223 char inodeListPath[256];
1224 static char tmpDevName[100];
1225 static char wpath[100];
1226 struct VolumeSummary *vsp, *esp;
1229 fileSysPartition = partP;
1230 fileSysDevice = fileSysPartition->device;
1231 fileSysPathName = VPartitionPath(fileSysPartition);
1234 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1235 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
1236 name = partP->devName;
1238 fileSysPath = fileSysPathName;
1239 strcpy(tmpDevName, partP->devName);
1240 name = get_DevName(tmpDevName, wpath);
1241 fileSysDeviceName = name;
1242 filesysfulldev = wpath;
1245 VLockPartition(partP->name);
1246 if (singleVolumeNumber || ForceSalvage)
1249 ForceSalvage = UseTheForceLuke(fileSysPath);
1251 if (singleVolumeNumber) {
1252 if (!VConnectFS()) {
1253 Abort("Couldn't connect to file server\n");
1255 AskOffline(singleVolumeNumber);
1258 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
1259 partP->name, name, (Testing ? "(READONLY mode)" : ""));
1261 Log("***Forced salvage of all volumes on this partition***\n");
1266 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1273 assert((dirp = opendir(fileSysPath)) != NULL);
1274 while ((dp = readdir(dirp))) {
1275 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
1276 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
1278 Log("Removing old salvager temp files %s\n", dp->d_name);
1279 strcpy(npath, fileSysPath);
1281 strcat(npath, dp->d_name);
1287 tdir = (tmpdir ? tmpdir : fileSysPath);
1289 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1290 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
1292 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
1295 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1296 unlink(inodeListPath);
1300 /* Using nt_unlink here since we're really using the delete on close
1301 * semantics of unlink. In most places in the salvager, we really do
1302 * mean to unlink the file at that point. Those places have been
1303 * modified to actually do that so that the NT crt can be used there.
1306 _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1307 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1309 inodeFd = afs_open(inodeListPath, O_RDONLY);
1310 unlink(inodeListPath);
1313 Abort("Temporary file %s is missing...\n", inodeListPath);
1314 if (ListInodeOption) {
1318 /* enumerate volumes in the partition.
1319 * figure out sets of read-only + rw volumes.
1320 * salvage each set, read-only volumes first, then read-write.
1321 * Fix up inodes on last volume in set (whether it is read-write
1324 GetVolumeSummary(singleVolumeNumber);
1326 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
1327 i < nVolumesInInodeFile; i = j) {
1328 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1330 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
1332 VolumeId vid = inodeSummary[j].volumeId;
1333 struct VolumeSummary *tsp;
1334 /* Scan volume list (from partition root directory) looking for the
1335 * current rw volume number in the volume list from the inode scan.
1336 * If there is one here that is not in the inode volume list,
1338 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
1340 DeleteExtraVolumeHeaderFile(vsp);
1342 /* Now match up the volume summary info from the root directory with the
1343 * entry in the volume list obtained from scanning inodes */
1344 inodeSummary[j].volSummary = NULL;
1345 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
1346 if (tsp->header.id == vid) {
1347 inodeSummary[j].volSummary = tsp;
1353 /* Salvage the group of volumes (several read-only + 1 read/write)
1354 * starting with the current read-only volume we're looking at.
1356 SalvageVolumeGroup(&inodeSummary[i], j - i);
1359 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1360 for (; vsp < esp; vsp++) {
1362 DeleteExtraVolumeHeaderFile(vsp);
1365 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1366 RemoveTheForce(fileSysPath);
1368 if (!Testing && singleVolumeNumber) {
1369 AskOnline(singleVolumeNumber, fileSysPartition->name);
1371 /* Step through the volumeSummary list and set all volumes on-line.
1372 * The volumes were taken off-line in GetVolumeSummary.
1374 for (j = 0; j < nVolumes; j++) {
1375 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1379 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1380 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
1383 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1387 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1390 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
1392 unlink(vsp->fileName);
1396 CompareInodes(const void *_p1, const void *_p2)
1398 register const struct ViceInodeInfo *p1 = _p1;
1399 register const struct ViceInodeInfo *p2 = _p2;
1400 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1401 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
1402 VolumeId p1rwid, p2rwid;
1404 (p1->u.vnode.vnodeNumber ==
1405 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
1407 (p2->u.vnode.vnodeNumber ==
1408 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
1409 if (p1rwid < p2rwid)
1411 if (p1rwid > p2rwid)
1413 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1414 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1415 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1416 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
1417 if (p1->u.vnode.volumeId == p1rwid)
1419 if (p2->u.vnode.volumeId == p2rwid)
1421 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
1423 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1424 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
1425 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
1427 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1429 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1431 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1433 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1435 /* The following tests are reversed, so that the most desirable
1436 * of several similar inodes comes first */
1437 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1438 #ifdef AFS_3DISPARES
1439 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1440 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1443 #ifdef AFS_SGI_EXMAG
1444 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1445 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1450 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1451 #ifdef AFS_3DISPARES
1452 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1453 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1456 #ifdef AFS_SGI_EXMAG
1457 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1458 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1463 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1464 #ifdef AFS_3DISPARES
1465 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1466 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1469 #ifdef AFS_SGI_EXMAG
1470 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1471 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1476 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1477 #ifdef AFS_3DISPARES
1478 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1479 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1482 #ifdef AFS_SGI_EXMAG
1483 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1484 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1493 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1494 register struct InodeSummary *summary)
1496 int volume = ip->u.vnode.volumeId;
1497 int rwvolume = volume;
1498 register n, nSpecial;
1499 register Unique maxunique;
1502 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1504 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1506 rwvolume = ip->u.special.parentId;
1507 /* This isn't quite right, as there could (in error) be different
1508 * parent inodes in different special vnodes */
1510 if (maxunique < ip->u.vnode.vnodeUniquifier)
1511 maxunique = ip->u.vnode.vnodeUniquifier;
1515 summary->volumeId = volume;
1516 summary->RWvolumeId = rwvolume;
1517 summary->nInodes = n;
1518 summary->nSpecialInodes = nSpecial;
1519 summary->maxUniquifier = maxunique;
1523 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber, void *rock)
1525 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1526 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1527 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1532 * Collect list of inodes in file named by path. If a truly fatal error,
1533 * unlink the file and abort. For lessor errors, return -1. The file will
1534 * be unlinked by the caller.
1537 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1539 struct afs_stat status;
1541 struct ViceInodeInfo *ip;
1542 struct InodeSummary summary;
1543 char summaryFileName[50];
1546 char *dev = fileSysPath;
1547 char *wpath = fileSysPath;
1549 char *dev = fileSysDeviceName;
1550 char *wpath = filesysfulldev;
1552 char *part = fileSysPath;
1555 /* This file used to come from vfsck; cobble it up ourselves now... */
1557 ListViceInodes(dev, fileSysPath, path,
1558 singleVolumeNumber ? OnlyOneVolume : 0,
1559 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1561 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, path, dev);
1565 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1567 if (forceSal && !ForceSalvage) {
1568 Log("***Forced salvage of all volumes on this partition***\n");
1571 inodeFd = afs_open(path, O_RDWR);
1572 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1574 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1576 tdir = (tmpdir ? tmpdir : part);
1578 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1579 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1581 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1582 "%s/salvage.temp.%d", tdir, getpid());
1584 summaryFile = afs_fopen(summaryFileName, "a+");
1585 if (summaryFile == NULL) {
1588 Abort("Unable to create inode summary file\n");
1590 if (!canfork || debug || Fork() == 0) {
1592 unsigned long st_size=(unsigned long) status.st_size;
1593 nInodes = st_size / sizeof(struct ViceInodeInfo);
1595 fclose(summaryFile);
1597 unlink(summaryFileName);
1598 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1599 RemoveTheForce(fileSysPath);
1601 struct VolumeSummary *vsp;
1604 GetVolumeSummary(singleVolumeNumber);
1606 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1608 DeleteExtraVolumeHeaderFile(vsp);
1611 Log("%s vice inodes on %s; not salvaged\n",
1612 singleVolumeNumber ? "No applicable" : "No", dev);
1615 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1617 fclose(summaryFile);
1620 unlink(summaryFileName);
1622 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1625 if (read(inodeFd, ip, st_size) != st_size) {
1626 fclose(summaryFile);
1629 unlink(summaryFileName);
1630 Abort("Unable to read inode table; %s not salvaged\n", dev);
1632 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1633 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1634 || write(inodeFd, ip, st_size) != st_size) {
1635 fclose(summaryFile);
1638 unlink(summaryFileName);
1639 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1643 CountVolumeInodes(ip, nInodes, &summary);
1644 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1645 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1646 fclose(summaryFile);
1650 summary.index += (summary.nInodes);
1651 nInodes -= summary.nInodes;
1652 ip += summary.nInodes;
1654 /* Following fflush is not fclose, because if it was debug mode would not work */
1655 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1656 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1657 fclose(summaryFile);
1661 if (canfork && !debug) {
1666 if (Wait("Inode summary") == -1) {
1667 fclose(summaryFile);
1670 unlink(summaryFileName);
1671 Exit(1); /* salvage of this partition aborted */
1674 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1675 if (status.st_size != 0) {
1677 unsigned long st_status=(unsigned long)status.st_size;
1678 inodeSummary = (struct InodeSummary *)malloc(st_status);
1679 assert(inodeSummary != NULL);
1680 /* For GNU we need to do lseek to get the file pointer moved. */
1681 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1682 ret = read(fileno(summaryFile), inodeSummary, st_status);
1683 assert(ret == st_status);
1685 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1686 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1687 fclose(summaryFile);
1689 unlink(summaryFileName);
1693 /* Comparison routine for volume sort.
1694 This is setup so that a read-write volume comes immediately before
1695 any read-only clones of that volume */
1697 CompareVolumes(const void *_p1, const void *_p2)
1699 register const struct VolumeSummary *p1 = _p1;
1700 register const struct VolumeSummary *p2 = _p2;
1701 if (p1->header.parent != p2->header.parent)
1702 return p1->header.parent < p2->header.parent ? -1 : 1;
1703 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1705 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1707 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1711 GetVolumeSummary(VolumeId singleVolumeNumber)
1714 afs_int32 nvols = 0;
1715 struct VolumeSummary *vsp, vs;
1716 struct VolumeDiskHeader diskHeader;
1719 /* Get headers from volume directory */
1720 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1721 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1722 if (!singleVolumeNumber) {
1723 while ((dp = readdir(dirp))) {
1724 char *p = dp->d_name;
1725 p = strrchr(dp->d_name, '.');
1726 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1728 if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
1729 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1730 == sizeof(diskHeader)
1731 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1732 DiskToVolumeHeader(&vs.header, &diskHeader);
1740 dirp = opendir("."); /* No rewinddir for NT */
1747 (struct VolumeSummary *)malloc(nvols *
1748 sizeof(struct VolumeSummary));
1751 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1752 assert(volumeSummaryp != NULL);
1755 vsp = volumeSummaryp;
1756 while ((dp = readdir(dirp))) {
1757 char *p = dp->d_name;
1758 p = strrchr(dp->d_name, '.');
1759 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1762 if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
1763 || read(fd, &diskHeader, sizeof(diskHeader))
1764 != sizeof(diskHeader)
1765 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1770 if (!singleVolumeNumber) {
1772 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1777 char nameShouldBe[64];
1778 DiskToVolumeHeader(&vsp->header, &diskHeader);
1779 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1780 && vsp->header.parent != singleVolumeNumber) {
1781 Log("%u is a read-only volume; not salvaged\n",
1782 singleVolumeNumber);
1785 if (!singleVolumeNumber
1786 || (vsp->header.id == singleVolumeNumber
1787 || vsp->header.parent == singleVolumeNumber)) {
1788 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1789 VFORMAT, vsp->header.id);
1790 if (singleVolumeNumber)
1791 AskOffline(vsp->header.id);
1792 if (strcmp(nameShouldBe, dp->d_name)) {
1794 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 " : ""));
1798 vsp->fileName = ToString(dp->d_name);
1808 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1812 /* Find the link table. This should be associated with the RW volume or, if
1813 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1816 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1817 struct ViceInodeInfo *allInodes)
1820 struct ViceInodeInfo *ip;
1822 for (i = 0; i < nVols; i++) {
1823 ip = allInodes + isp[i].index;
1824 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1825 if (ip[j].u.special.type == VI_LINKTABLE)
1826 return ip[j].inodeNumber;
1833 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1835 struct versionStamp version;
1838 if (!VALID_INO(ino))
1840 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1841 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1842 if (!VALID_INO(ino))
1844 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1845 isp->RWvolumeId, errno);
1846 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1847 fdP = IH_OPEN(VGLinkH);
1849 Abort("Can't open link table for volume %u (error = %d)\n",
1850 isp->RWvolumeId, errno);
1852 if (FDH_TRUNC(fdP, 0) < 0)
1853 Abort("Can't truncate link table for volume %u (error = %d)\n",
1854 isp->RWvolumeId, errno);
1856 version.magic = LINKTABLEMAGIC;
1857 version.version = LINKTABLEVERSION;
1859 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1861 Abort("Can't truncate link table for volume %u (error = %d)\n",
1862 isp->RWvolumeId, errno);
1864 FDH_REALLYCLOSE(fdP);
1866 /* If the volume summary exits (i.e., the V*.vol header file exists),
1867 * then set this inode there as well.
1869 if (isp->volSummary)
1870 isp->volSummary->header.linkTable = ino;
1879 SVGParms_t *parms = (SVGParms_t *) arg;
1880 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1885 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1888 pthread_attr_t tattr;
1892 /* Initialize per volume global variables, even if later code does so */
1896 memset(&VolInfo, 0, sizeof(VolInfo));
1898 parms.svgp_inodeSummaryp = isp;
1899 parms.svgp_count = nVols;
1900 code = pthread_attr_init(&tattr);
1902 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1906 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1908 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1911 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1913 Log("Failed to create thread to salvage volume group %u\n",
1917 (void)pthread_join(tid, NULL);
1919 #endif /* AFS_NT40_ENV */
1922 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1924 struct ViceInodeInfo *inodes, *allInodes, *ip;
1925 int i, totalInodes, size, salvageTo;
1929 int dec_VGLinkH = 0;
1931 FdHandle_t *fdP = NULL;
1934 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1935 && isp->nSpecialInodes > 0);
1936 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1937 if (!ForceSalvage && QuickCheck(isp, nVols))
1940 if (ShowMounts && !haveRWvolume)
1942 if (canfork && !debug && Fork() != 0) {
1943 (void)Wait("Salvage volume group");
1946 for (i = 0, totalInodes = 0; i < nVols; i++)
1947 totalInodes += isp[i].nInodes;
1948 size = totalInodes * sizeof(struct ViceInodeInfo);
1949 inodes = (struct ViceInodeInfo *)malloc(size);
1950 allInodes = inodes - isp->index; /* this would the base of all the inodes
1951 * for the partition, if all the inodes
1952 * had been read into memory */
1954 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1956 assert(read(inodeFd, inodes, size) == size);
1958 /* Don't try to salvage a read write volume if there isn't one on this
1960 salvageTo = haveRWvolume ? 0 : 1;
1962 #ifdef AFS_NAMEI_ENV
1963 ino = FindLinkHandle(isp, nVols, allInodes);
1964 if (VALID_INO(ino)) {
1965 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1966 fdP = IH_OPEN(VGLinkH);
1968 if (!VALID_INO(ino) || fdP == NULL) {
1969 Log("%s link table for volume %u.\n",
1970 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1972 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1974 CreateLinkTable(isp, ino);
1978 FDH_REALLYCLOSE(fdP);
1980 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1983 /* Salvage in reverse order--read/write volume last; this way any
1984 * Inodes not referenced by the time we salvage the read/write volume
1985 * can be picked up by the read/write volume */
1986 /* ACTUALLY, that's not done right now--the inodes just vanish */
1987 for (i = nVols - 1; i >= salvageTo; i--) {
1989 struct InodeSummary *lisp = &isp[i];
1990 #ifdef AFS_NAMEI_ENV
1991 /* If only the RO is present on this partition, the link table
1992 * shows up as a RW volume special file. Need to make sure the
1993 * salvager doesn't try to salvage the non-existent RW.
1995 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1996 /* If this only special inode is the link table, continue */
1997 if (inodes->u.special.type == VI_LINKTABLE) {
2004 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
2005 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
2006 /* Check inodes twice. The second time do things seriously. This
2007 * way the whole RO volume can be deleted, below, if anything goes wrong */
2008 for (check = 1; check >= 0; check--) {
2010 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
2012 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
2013 if (rw && deleteMe) {
2014 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
2015 * volume won't be called */
2021 if (rw && check == 1)
2023 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
2024 MaybeZapVolume(lisp, "Vnode index", 0, check);
2030 /* Fix actual inode counts */
2032 Log("totalInodes %d\n",totalInodes);
2033 for (ip = inodes; totalInodes; ip++, totalInodes--) {
2034 static int TraceBadLinkCounts = 0;
2035 #ifdef AFS_NAMEI_ENV
2036 if (VGLinkH->ih_ino == ip->inodeNumber) {
2037 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
2038 VGLinkH_p1 = ip->u.param[0];
2039 continue; /* Deal with this last. */
2042 if (ip->linkCount != 0 && TraceBadLinkCounts) {
2043 TraceBadLinkCounts--; /* Limit reports, per volume */
2044 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(NULL, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
2046 while (ip->linkCount > 0) {
2047 /* below used to assert, not break */
2049 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2050 Log("idec failed. inode %s errno %d\n",
2051 PrintInode(NULL, ip->inodeNumber), errno);
2057 while (ip->linkCount < 0) {
2058 /* these used to be asserts */
2060 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2061 Log("iinc failed. inode %s errno %d\n",
2062 PrintInode(NULL, ip->inodeNumber), errno);
2069 #ifdef AFS_NAMEI_ENV
2070 while (dec_VGLinkH > 0) {
2071 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2072 Log("idec failed on link table, errno = %d\n", errno);
2076 while (dec_VGLinkH < 0) {
2077 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2078 Log("iinc failed on link table, errno = %d\n", errno);
2085 /* Directory consistency checks on the rw volume */
2087 SalvageVolume(isp, VGLinkH);
2088 IH_RELEASE(VGLinkH);
2090 if (canfork && !debug) {
2097 QuickCheck(register struct InodeSummary *isp, int nVols)
2099 /* Check headers BEFORE forking */
2103 for (i = 0; i < nVols; i++) {
2104 struct VolumeSummary *vs = isp[i].volSummary;
2105 VolumeDiskData volHeader;
2107 /* Don't salvage just because phantom rw volume is there... */
2108 /* (If a read-only volume exists, read/write inodes must also exist) */
2109 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2113 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2114 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2115 == sizeof(volHeader)
2116 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2117 && volHeader.dontSalvage == DONT_SALVAGE
2118 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2119 if (volHeader.inUse == 1) {
2120 volHeader.inUse = 0;
2121 volHeader.inService = 1;
2123 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2124 != sizeof(volHeader)) {
2140 /* SalvageVolumeHeaderFile
2142 * Salvage the top level V*.vol header file. Make sure the special files
2143 * exist and that there are no duplicates.
2145 * Calls SalvageHeader for each possible type of volume special file.
2149 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2150 register struct ViceInodeInfo *inodes, int RW,
2151 int check, int *deleteMe)
2155 register struct ViceInodeInfo *ip;
2156 int allinodesobsolete = 1;
2157 struct VolumeDiskHeader diskHeader;
2161 memset(&tempHeader, 0, sizeof(tempHeader));
2162 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2163 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2164 tempHeader.id = isp->volumeId;
2165 tempHeader.parent = isp->RWvolumeId;
2166 /* Check for duplicates (inodes are sorted by type field) */
2167 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2168 ip = &inodes[isp->index + i];
2169 if (ip->u.special.type == (ip + 1)->u.special.type) {
2171 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2175 for (i = 0; i < isp->nSpecialInodes; i++) {
2176 ip = &inodes[isp->index + i];
2177 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2179 Log("Rubbish header inode\n");
2182 Log("Rubbish header inode; deleted\n");
2183 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2184 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2185 if (!check && ip->u.special.type != VI_LINKTABLE)
2186 ip->linkCount--; /* Keep the inode around */
2187 allinodesobsolete = 0;
2191 if (allinodesobsolete) {
2198 VGLinkH_cnt++; /* one for every header. */
2200 if (!RW && !check && isp->volSummary) {
2201 ClearROInUseBit(isp->volSummary);
2205 for (i = 0; i < MAXINODETYPE; i++) {
2206 if (stuff[i].inodeType == VI_LINKTABLE) {
2207 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2208 * And we may have recreated the link table earlier, so set the
2209 * RW header as well.
2211 if (VALID_INO(VGLinkH->ih_ino)) {
2212 *stuff[i].inode = VGLinkH->ih_ino;
2216 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2220 if (isp->volSummary == NULL) {
2222 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2224 Log("No header file for volume %u\n", isp->volumeId);
2228 Log("No header file for volume %u; %screating %s/%s\n",
2229 isp->volumeId, (Testing ? "it would have been " : ""),
2230 fileSysPathName, name);
2231 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
2232 assert(headerFd != -1);
2233 isp->volSummary = (struct VolumeSummary *)
2234 malloc(sizeof(struct VolumeSummary));
2235 isp->volSummary->fileName = ToString(name);
2238 /* hack: these two fields are obsolete... */
2239 isp->volSummary->header.volumeAcl = 0;
2240 isp->volSummary->header.volumeMountTable = 0;
2243 (&isp->volSummary->header, &tempHeader,
2244 sizeof(struct VolumeHeader))) {
2245 /* We often remove the name before calling us, so we make a fake one up */
2246 if (isp->volSummary->fileName) {
2247 strcpy(name, isp->volSummary->fileName);
2249 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2250 isp->volSummary->fileName = ToString(name);
2253 Log("Header file %s is damaged or no longer valid%s\n", name,
2254 (check ? "" : "; repairing"));
2258 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
2259 assert(headerFd != -1);
2263 memcpy(&isp->volSummary->header, &tempHeader,
2264 sizeof(struct VolumeHeader));
2267 Log("It would have written a new header file for volume %u\n",
2270 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2271 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2272 != sizeof(struct VolumeDiskHeader)) {
2273 Log("Couldn't rewrite volume header file!\n");
2280 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2281 isp->volSummary->header.volumeInfo);
2286 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2290 VolumeDiskData volumeInfo;
2291 struct versionStamp fileHeader;
2300 #ifndef AFS_NAMEI_ENV
2301 if (sp->inodeType == VI_LINKTABLE)
2304 if (*(sp->inode) == 0) {
2306 Log("Missing inode in volume header (%s)\n", sp->description);
2310 Log("Missing inode in volume header (%s); %s\n", sp->description,
2311 (Testing ? "it would have recreated it" : "recreating"));
2314 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2315 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2316 if (!VALID_INO(*(sp->inode)))
2318 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2319 sp->description, errno);
2324 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2325 fdP = IH_OPEN(specH);
2326 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2327 /* bail out early and destroy the volume */
2329 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2336 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2337 sp->description, errno);
2340 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2341 || header.fileHeader.magic != sp->stamp.magic)) {
2343 Log("Part of the header (%s) is corrupted\n", sp->description);
2344 FDH_REALLYCLOSE(fdP);
2348 Log("Part of the header (%s) is corrupted; recreating\n",
2352 if (sp->inodeType == VI_VOLINFO
2353 && header.volumeInfo.destroyMe == DESTROY_ME) {
2356 FDH_REALLYCLOSE(fdP);
2360 if (recreate && !Testing) {
2363 ("Internal error: recreating volume header (%s) in check mode\n",
2365 code = FDH_TRUNC(fdP, 0);
2367 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2368 sp->description, errno);
2370 /* The following code should be moved into vutil.c */
2371 if (sp->inodeType == VI_VOLINFO) {
2373 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2374 header.volumeInfo.stamp = sp->stamp;
2375 header.volumeInfo.id = isp->volumeId;
2376 header.volumeInfo.parentId = isp->RWvolumeId;
2377 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2378 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2379 isp->volumeId, isp->volumeId);
2380 header.volumeInfo.inService = 0;
2381 header.volumeInfo.blessed = 0;
2382 /* The + 1000 is a hack in case there are any files out in venus caches */
2383 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2384 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2385 header.volumeInfo.needsCallback = 0;
2386 gettimeofday(&tp, 0);
2387 header.volumeInfo.creationDate = tp.tv_sec;
2388 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2390 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2391 sp->description, errno);
2394 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2395 sizeof(header.volumeInfo));
2396 if (code != sizeof(header.volumeInfo)) {
2399 ("Unable to write volume header file (%s) (errno = %d)\n",
2400 sp->description, errno);
2401 Abort("Unable to write entire volume header file (%s)\n",
2405 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2407 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2408 sp->description, errno);
2410 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2411 if (code != sizeof(sp->stamp)) {
2414 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2415 sp->description, errno);
2417 ("Unable to write entire version stamp in volume header file (%s)\n",
2422 FDH_REALLYCLOSE(fdP);
2424 if (sp->inodeType == VI_VOLINFO) {
2425 VolInfo = header.volumeInfo;
2428 if (VolInfo.updateDate) {
2429 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2431 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2432 (Testing ? "it would have been " : ""), update);
2434 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2436 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2437 VolInfo.id, update);
2447 SalvageVnodes(register struct InodeSummary *rwIsp,
2448 register struct InodeSummary *thisIsp,
2449 register struct ViceInodeInfo *inodes, int check)
2451 int ilarge, ismall, ioffset, RW, nInodes;
2452 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2455 RW = (rwIsp == thisIsp);
2456 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2458 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2459 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2460 if (check && ismall == -1)
2463 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2464 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2465 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2469 SalvageIndex(Inode ino, VnodeClass class, int RW,
2470 register struct ViceInodeInfo *ip, int nInodes,
2471 struct VolumeSummary *volSummary, int check)
2473 VolumeId volumeNumber;
2474 char buf[SIZEOF_LARGEDISKVNODE];
2475 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2477 StreamHandle_t *file;
2478 struct VnodeClassInfo *vcp;
2480 afs_fsize_t vnodeLength;
2481 int vnodeIndex, nVnodes;
2482 afs_ino_str_t stmp1, stmp2;
2486 volumeNumber = volSummary->header.id;
2487 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2488 fdP = IH_OPEN(handle);
2489 assert(fdP != NULL);
2490 file = FDH_FDOPEN(fdP, "r+");
2491 assert(file != NULL);
2492 vcp = &VnodeClassInfo[class];
2493 size = OS_SIZE(fdP->fd_fd);
2495 nVnodes = (size / vcp->diskSize) - 1;
2497 assert((nVnodes + 1) * vcp->diskSize == size);
2498 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2502 for (vnodeIndex = 0;
2503 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2504 nVnodes--, vnodeIndex++) {
2505 if (vnode->type != vNull) {
2506 int vnodeChanged = 0;
2507 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2508 /* Log programs that belong to root (potentially suid root);
2509 * don't bother for read-only or backup volumes */
2510 #ifdef notdef /* This is done elsewhere */
2511 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2512 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n", VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author, vnode->owner, vnode->modeBits);
2514 if (VNDISK_GET_INO(vnode) == 0) {
2516 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2517 memset(vnode, 0, vcp->diskSize);
2521 if (vcp->magic != vnode->vnodeMagic) {
2522 /* bad magic #, probably partially created vnode */
2523 Log("Partially allocated vnode %d deleted.\n",
2525 memset(vnode, 0, vcp->diskSize);
2529 /* ****** Should do a bit more salvage here: e.g. make sure
2530 * vnode type matches what it should be given the index */
2531 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2532 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2533 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2534 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2541 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2542 /* The following doesn't work, because the version number
2543 * is not maintained correctly by the file server */
2544 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2545 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2547 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2553 /* For RW volume, look for vnode with matching inode number;
2554 * if no such match, take the first determined by our sort
2556 register struct ViceInodeInfo *lip = ip;
2557 register lnInodes = nInodes;
2559 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2560 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2569 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2570 /* "Matching" inode */
2574 vu = vnode->uniquifier;
2575 iu = ip->u.vnode.vnodeUniquifier;
2576 vd = vnode->dataVersion;
2577 id = ip->u.vnode.inodeDataVersion;
2579 * Because of the possibility of the uniquifier overflows (> 4M)
2580 * we compare them modulo the low 22-bits; we shouldn't worry
2581 * about mismatching since they shouldn't to many old
2582 * uniquifiers of the same vnode...
2584 if (IUnique(vu) != IUnique(iu)) {
2586 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2589 vnode->uniquifier = iu;
2590 #ifdef AFS_3DISPARES
2591 vnode->dataVersion = (id >= vd ?
2594 1887437 ? vd : id) :
2597 1887437 ? id : vd));
2599 #if defined(AFS_SGI_EXMAG)
2600 vnode->dataVersion = (id >= vd ?
2603 15099494 ? vd : id) :
2606 15099494 ? id : vd));
2608 vnode->dataVersion = (id > vd ? id : vd);
2609 #endif /* AFS_SGI_EXMAG */
2610 #endif /* AFS_3DISPARES */
2613 /* don't bother checking for vd > id any more, since
2614 * partial file transfers always result in this state,
2615 * and you can't do much else anyway (you've already
2616 * found the best data you can) */
2617 #ifdef AFS_3DISPARES
2618 if (!vnodeIsDirectory(vnodeNumber)
2619 && ((vd < id && (id - vd) < 1887437)
2620 || ((vd > id && (vd - id) > 1887437)))) {
2622 #if defined(AFS_SGI_EXMAG)
2623 if (!vnodeIsDirectory(vnodeNumber)
2624 && ((vd < id && (id - vd) < 15099494)
2625 || ((vd > id && (vd - id) > 15099494)))) {
2627 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2628 #endif /* AFS_SGI_EXMAG */
2631 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2632 vnode->dataVersion = id;
2637 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2640 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2642 VNDISK_SET_INO(vnode, ip->inodeNumber);
2647 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2649 VNDISK_SET_INO(vnode, ip->inodeNumber);
2652 VNDISK_GET_LEN(vnodeLength, vnode);
2653 if (ip->byteCount != vnodeLength) {
2656 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2661 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2662 VNDISK_SET_LEN(vnode, ip->byteCount);
2666 ip->linkCount--; /* Keep the inode around */
2669 } else { /* no matching inode */
2670 if (VNDISK_GET_INO(vnode) != 0
2671 || vnode->type == vDirectory) {
2672 /* No matching inode--get rid of the vnode */
2674 if (VNDISK_GET_INO(vnode)) {
2676 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2680 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2685 if (VNDISK_GET_INO(vnode)) {
2687 Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime((time_t *) & (vnode->serverModifyTime)));
2691 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime((time_t *) & (vnode->serverModifyTime)));
2693 memset(vnode, 0, vcp->diskSize);
2696 /* Should not reach here becuase we checked for
2697 * (inodeNumber == 0) above. And where we zero the vnode,
2698 * we also goto vnodeDone.
2702 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2706 } /* VNDISK_GET_INO(vnode) != 0 */
2708 assert(!(vnodeChanged && check));
2709 if (vnodeChanged && !Testing) {
2711 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2712 (char *)vnode, vcp->diskSize)
2714 VolumeChanged = 1; /* For break call back */
2725 struct VnodeEssence *
2726 CheckVnodeNumber(VnodeId vnodeNumber)
2729 struct VnodeInfo *vip;
2732 class = vnodeIdToClass(vnodeNumber);
2733 vip = &vnodeInfo[class];
2734 offset = vnodeIdToBitNumber(vnodeNumber);
2735 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2739 CopyOnWrite(register struct DirSummary *dir)
2741 /* Copy the directory unconditionally if we are going to change it:
2742 * not just if was cloned.
2744 struct VnodeDiskObject vnode;
2745 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2746 Inode oldinode, newinode;
2749 if (dir->copied || Testing)
2751 DFlush(); /* Well justified paranoia... */
2754 IH_IREAD(vnodeInfo[vLarge].handle,
2755 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2757 assert(code == sizeof(vnode));
2758 oldinode = VNDISK_GET_INO(&vnode);
2759 /* Increment the version number by a whole lot to avoid problems with
2760 * clients that were promised new version numbers--but the file server
2761 * crashed before the versions were written to disk.
2764 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2765 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2767 assert(VALID_INO(newinode));
2768 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2770 VNDISK_SET_INO(&vnode, newinode);
2772 IH_IWRITE(vnodeInfo[vLarge].handle,
2773 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2775 assert(code == sizeof(vnode));
2777 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2778 fileSysDevice, newinode);
2779 /* Don't delete the original inode right away, because the directory is
2780 * still being scanned.
2786 * This function should either successfully create a new dir, or give up
2787 * and leave things the way they were. In particular, if it fails to write
2788 * the new dir properly, it should return w/o changing the reference to the
2792 CopyAndSalvage(register struct DirSummary *dir)
2794 struct VnodeDiskObject vnode;
2795 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2796 Inode oldinode, newinode;
2798 register afs_int32 code;
2799 afs_int32 parentUnique = 1;
2800 struct VnodeEssence *vnodeEssence;
2804 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2806 IH_IREAD(vnodeInfo[vLarge].handle,
2807 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2809 assert(code == sizeof(vnode));
2810 oldinode = VNDISK_GET_INO(&vnode);
2811 /* Increment the version number by a whole lot to avoid problems with
2812 * clients that were promised new version numbers--but the file server
2813 * crashed before the versions were written to disk.
2816 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2817 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2819 assert(VALID_INO(newinode));
2820 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2822 /* Assign . and .. vnode numbers from dir and vnode.parent.
2823 * The uniquifier for . is in the vnode.
2824 * The uniquifier for .. might be set to a bogus value of 1 and
2825 * the salvager will later clean it up.
2827 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2828 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2831 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2833 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2838 /* didn't really build the new directory properly, let's just give up. */
2839 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2841 Log("Directory salvage returned code %d, continuing.\n", code);
2844 Log("Checking the results of the directory salvage...\n");
2845 if (!DirOK(&newdir)) {
2846 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2847 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2852 VNDISK_SET_INO(&vnode, newinode);
2853 VNDISK_SET_LEN(&vnode, Length(&newdir));
2855 IH_IWRITE(vnodeInfo[vLarge].handle,
2856 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2858 assert(code == sizeof(vnode));
2860 nt_sync(fileSysDevice);
2862 sync(); /* this is slow, but hopefully rarely called. We don't have
2863 * an open FD on the file itself to fsync.
2866 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2868 dir->dirHandle = newdir;
2872 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2875 struct VnodeEssence *vnodeEssence;
2876 afs_int32 dirOrphaned, todelete;
2878 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2880 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2881 if (vnodeEssence == NULL) {
2883 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2887 assert(Delete(&dir->dirHandle, name) == 0);
2892 #ifndef AFS_NAMEI_ENV
2893 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2894 * mount inode for the partition. If this inode were deleted, it would crash
2897 if (vnodeEssence->InodeNumber == 0) {
2898 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
2901 assert(Delete(&dir->dirHandle, name) == 0);
2908 if (!(vnodeNumber & 1) && !Showmode
2909 && !(vnodeEssence->count || vnodeEssence->unique
2910 || vnodeEssence->modeBits)) {
2911 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2912 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2913 vnodeNumber, unique,
2914 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2919 assert(Delete(&dir->dirHandle, name) == 0);
2925 /* Check if the Uniquifiers match. If not, change the directory entry
2926 * so its unique matches the vnode unique. Delete if the unique is zero
2927 * or if the directory is orphaned.
2929 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2930 if (!vnodeEssence->unique
2931 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2932 /* This is an orphaned directory. Don't delete the . or ..
2933 * entry. Otherwise, it will get created in the next
2934 * salvage and deleted again here. So Just skip it.
2939 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2942 Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
2946 fid.Vnode = vnodeNumber;
2947 fid.Unique = vnodeEssence->unique;
2949 assert(Delete(&dir->dirHandle, name) == 0);
2951 assert(Create(&dir->dirHandle, name, &fid) == 0);
2954 return; /* no need to continue */
2957 if (strcmp(name, ".") == 0) {
2958 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2961 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2964 assert(Delete(&dir->dirHandle, ".") == 0);
2965 fid.Vnode = dir->vnodeNumber;
2966 fid.Unique = dir->unique;
2967 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2970 vnodeNumber = fid.Vnode; /* Get the new Essence */
2971 unique = fid.Unique;
2972 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2975 } else if (strcmp(name, "..") == 0) {
2978 struct VnodeEssence *dotdot;
2979 pa.Vnode = dir->parent;
2980 dotdot = CheckVnodeNumber(pa.Vnode);
2981 assert(dotdot != NULL); /* XXX Should not be assert */
2982 pa.Unique = dotdot->unique;
2984 pa.Vnode = dir->vnodeNumber;
2985 pa.Unique = dir->unique;
2987 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2989 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2992 assert(Delete(&dir->dirHandle, "..") == 0);
2993 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2996 vnodeNumber = pa.Vnode; /* Get the new Essence */
2998 vnodeEssence = CheckVnodeNumber(vnodeNumber);
3000 dir->haveDotDot = 1;
3001 } else if (strncmp(name, ".__afs", 6) == 0) {
3003 Log("dir vnode %u: special old unlink-while-referenced file %s %s deleted (vnode %u)\n", dir->vnodeNumber, name, (Testing ? "would have been" : "is"), vnodeNumber);
3007 assert(Delete(&dir->dirHandle, name) == 0);
3009 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
3010 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
3013 if (ShowSuid && (vnodeEssence->modeBits & 06000))
3014 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3015 if (ShowMounts && (vnodeEssence->type == vSymlink)
3016 && !(vnodeEssence->modeBits & 0111)) {
3022 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3023 vnodeEssence->InodeNumber);
3025 assert(fdP != NULL);
3026 size = FDH_SIZE(fdP);
3028 memset(buf, 0, 1024);
3031 code = FDH_READ(fdP, buf, size);
3032 assert(code == size);
3033 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3034 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3035 dir->name ? dir->name : "??", name, buf);
3036 FDH_REALLYCLOSE(fdP);
3039 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3040 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3041 if (vnodeIdToClass(vnodeNumber) == vLarge
3042 && vnodeEssence->name == NULL) {
3044 if ((n = (char *)malloc(strlen(name) + 1)))
3046 vnodeEssence->name = n;
3049 /* The directory entry points to the vnode. Check to see if the
3050 * vnode points back to the directory. If not, then let the
3051 * directory claim it (else it might end up orphaned). Vnodes
3052 * already claimed by another directory are deleted from this
3053 * directory: hardlinks to the same vnode are not allowed
3054 * from different directories.
3056 if (vnodeEssence->parent != dir->vnodeNumber) {
3057 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3058 /* Vnode does not point back to this directory.
3059 * Orphaned dirs cannot claim a file (it may belong to
3060 * another non-orphaned dir).
3063 Log("dir vnode %u: %s/%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
3065 vnodeEssence->parent = dir->vnodeNumber;
3066 vnodeEssence->changed = 1;
3068 /* Vnode was claimed by another directory */
3071 Log("dir vnode %u: %s/%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3072 } else if (vnodeNumber == 1) {
3073 Log("dir vnode %d: %s/%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
3075 Log("dir vnode %u: %s/%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3080 assert(Delete(&dir->dirHandle, name) == 0);
3085 /* This directory claims the vnode */
3086 vnodeEssence->claimed = 1;
3088 vnodeEssence->count--;
3092 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3094 register struct VnodeInfo *vip = &vnodeInfo[class];
3095 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3096 char buf[SIZEOF_LARGEDISKVNODE];
3097 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3099 StreamHandle_t *file;
3104 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3105 fdP = IH_OPEN(vip->handle);
3106 assert(fdP != NULL);
3107 file = FDH_FDOPEN(fdP, "r+");
3108 assert(file != NULL);
3109 size = OS_SIZE(fdP->fd_fd);
3111 vip->nVnodes = (size / vcp->diskSize) - 1;
3112 if (vip->nVnodes > 0) {
3113 assert((vip->nVnodes + 1) * vcp->diskSize == size);
3114 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3115 assert((vip->vnodes = (struct VnodeEssence *)
3116 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3117 if (class == vLarge) {
3118 assert((vip->inodes = (Inode *)
3119 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3128 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3129 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3130 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3131 nVnodes--, vnodeIndex++) {
3132 if (vnode->type != vNull) {
3133 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3134 afs_fsize_t vnodeLength;
3135 vip->nAllocatedVnodes++;
3136 vep->count = vnode->linkCount;
3137 VNDISK_GET_LEN(vnodeLength, vnode);
3138 vep->blockCount = nBlocks(vnodeLength);
3139 vip->volumeBlockCount += vep->blockCount;
3140 vep->parent = vnode->parent;
3141 vep->unique = vnode->uniquifier;
3142 if (*maxu < vnode->uniquifier)
3143 *maxu = vnode->uniquifier;
3144 vep->modeBits = vnode->modeBits;
3145 vep->InodeNumber = VNDISK_GET_INO(vnode);
3146 vep->type = vnode->type;
3147 vep->author = vnode->author;
3148 vep->owner = vnode->owner;
3149 vep->group = vnode->group;
3150 if (vnode->type == vDirectory) {
3151 assert(class == vLarge);
3152 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3161 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3163 struct VnodeEssence *parentvp;
3169 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3170 && GetDirName(vp->parent, parentvp, path)) {
3172 strcat(path, vp->name);
3178 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3179 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3182 IsVnodeOrphaned(VnodeId vnode)
3184 struct VnodeEssence *vep;
3187 return (1); /* Vnode zero does not exist */
3189 return (0); /* The root dir vnode is always claimed */
3190 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3191 if (!vep || !vep->claimed)
3192 return (1); /* Vnode is not claimed - it is orphaned */
3194 return (IsVnodeOrphaned(vep->parent));
3198 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3199 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3202 static struct DirSummary dir;
3203 static struct DirHandle dirHandle;
3204 struct VnodeEssence *parent;
3205 static char path[MAXPATHLEN];
3208 if (dirVnodeInfo->vnodes[i].salvaged)
3209 return; /* already salvaged */
3212 dirVnodeInfo->vnodes[i].salvaged = 1;
3214 if (dirVnodeInfo->inodes[i] == 0)
3215 return; /* Not allocated to a directory */
3217 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3218 if (dirVnodeInfo->vnodes[i].parent) {
3219 Log("Bad parent, vnode 1; %s...\n",
3220 (Testing ? "skipping" : "salvaging"));
3221 dirVnodeInfo->vnodes[i].parent = 0;
3222 dirVnodeInfo->vnodes[i].changed = 1;
3225 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3226 if (parent && parent->salvaged == 0)
3227 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3228 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3229 rootdir, rootdirfound);
3232 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3233 dir.unique = dirVnodeInfo->vnodes[i].unique;
3236 dir.parent = dirVnodeInfo->vnodes[i].parent;
3237 dir.haveDot = dir.haveDotDot = 0;
3238 dir.ds_linkH = alinkH;
3239 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3240 dirVnodeInfo->inodes[i]);
3242 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3245 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3246 (Testing ? "skipping" : "salvaging"));
3249 CopyAndSalvage(&dir);
3253 dirHandle = dir.dirHandle;
3256 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3257 &dirVnodeInfo->vnodes[i], path);
3260 /* If enumeration failed for random reasons, we will probably delete
3261 * too much stuff, so we guard against this instead.
3263 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3266 /* Delete the old directory if it was copied in order to salvage.
3267 * CopyOnWrite has written the new inode # to the disk, but we still
3268 * have the old one in our local structure here. Thus, we idec the
3272 if (dir.copied && !Testing) {
3273 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3275 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3278 /* Remember rootdir DirSummary _after_ it has been judged */
3279 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3280 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3288 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3290 /* This routine, for now, will only be called for read-write volumes */
3292 int BlocksInVolume = 0, FilesInVolume = 0;
3293 register VnodeClass class;
3294 struct DirSummary rootdir, oldrootdir;
3295 struct VnodeInfo *dirVnodeInfo;
3296 struct VnodeDiskObject vnode;
3297 VolumeDiskData volHeader;
3299 int orphaned, rootdirfound = 0;
3300 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3301 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3302 struct VnodeEssence *vep;
3307 VnodeId LFVnode, ThisVnode;
3308 Unique LFUnique, ThisUnique;
3311 vid = rwIsp->volSummary->header.id;
3312 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3313 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3314 assert(nBytes == sizeof(volHeader));
3315 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3316 assert(volHeader.destroyMe != DESTROY_ME);
3317 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3319 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3321 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3324 dirVnodeInfo = &vnodeInfo[vLarge];
3325 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3326 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3334 /* Parse each vnode looking for orphaned vnodes and
3335 * connect them to the tree as orphaned (if requested).
3337 oldrootdir = rootdir;
3338 for (class = 0; class < nVNODECLASSES; class++) {
3339 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3340 vep = &(vnodeInfo[class].vnodes[v]);
3341 ThisVnode = bitNumberToVnodeNumber(v, class);
3342 ThisUnique = vep->unique;
3344 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3345 continue; /* Ignore unused, claimed, and root vnodes */
3347 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3348 * entry in this vnode had incremented the parent link count (In
3349 * JudgeEntry()). We need to go to the parent and decrement that
3350 * link count. But if the parent's unique is zero, then the parent
3351 * link count was not incremented in JudgeEntry().
3353 if (class == vLarge) { /* directory vnode */
3354 pv = vnodeIdToBitNumber(vep->parent);
3355 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3356 vnodeInfo[vLarge].vnodes[pv].count++;
3360 continue; /* If no rootdir, can't attach orphaned files */
3362 /* Here we attach orphaned files and directories into the
3363 * root directory, LVVnode, making sure link counts stay correct.
3365 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3366 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3367 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3369 /* Update this orphaned vnode's info. Its parent info and
3370 * link count (do for orphaned directories and files).
3372 vep->parent = LFVnode; /* Parent is the root dir */
3373 vep->unique = LFUnique;
3376 vep->count--; /* Inc link count (root dir will pt to it) */
3378 /* If this orphaned vnode is a directory, change '..'.
3379 * The name of the orphaned dir/file is unknown, so we
3380 * build a unique name. No need to CopyOnWrite the directory
3381 * since it is not connected to tree in BK or RO volume and
3382 * won't be visible there.
3384 if (class == vLarge) {
3388 /* Remove and recreate the ".." entry in this orphaned directory */
3389 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3390 vnodeInfo[class].inodes[v]);
3392 pa.Unique = LFUnique;
3393 assert(Delete(&dh, "..") == 0);
3394 assert(Create(&dh, "..", &pa) == 0);
3396 /* The original parent's link count was decremented above.
3397 * Here we increment the new parent's link count.
3399 pv = vnodeIdToBitNumber(LFVnode);
3400 vnodeInfo[vLarge].vnodes[pv].count--;
3404 /* Go to the root dir and add this entry. The link count of the
3405 * root dir was incremented when ".." was created. Try 10 times.
3407 for (j = 0; j < 10; j++) {
3408 pa.Vnode = ThisVnode;
3409 pa.Unique = ThisUnique;
3411 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3413 vLarge) ? "__ORPHANDIR__" :
3414 "__ORPHANFILE__"), ThisVnode,