Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / vol / vol-salvage.c
blobbfe60edd042ee60c8e2d65d6b86bfd7bfc2e3069
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
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
8 */
11 * System: VICE-TWO
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
16 /* 1.2 features:
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.
22 1.3 features:
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.
28 1.4 features:
29 Fixed bug which was causing inode link counts to go bad (thus leaking
30 disk blocks).
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.
37 1.5 features:
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.
45 1.6 features:
46 It now deletes obsolete volume inodes without complaining
48 1.7 features:
49 Repairs rw volume headers (again).
51 1.8 features:
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
55 at some other site!)
56 Some of the messages are cleaned up or made more explicit. One or two added.
57 Logging cleaned up.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
59 read/write volume.
61 1.9 features:
62 When a volume header is recreated, the new name will be "bogus.volume#"
64 2.0 features:
65 Directory salvaging turned on!!!
67 2.1 features:
68 Prints warning messages for setuid programs.
70 2.2 features:
71 Logs missing inode numbers.
73 2.3 features:
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.
76 2.4 features:
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 #include <afsconfig.h>
87 #include <afs/param.h>
90 #ifndef AFS_NT40_ENV
91 #include <sys/param.h>
92 #include <sys/file.h>
93 #ifndef ITIMER_REAL
94 #include <sys/time.h>
95 #endif /* ITIMER_REAL */
96 #endif
97 #include <stdlib.h>
98 #include <stdio.h>
99 #include <string.h>
100 #include <dirent.h>
101 #include <sys/stat.h>
102 #include <time.h>
103 #include <errno.h>
104 #ifdef AFS_NT40_ENV
105 #include <io.h>
106 #include <WINNT/afsevent.h>
107 #endif
108 #ifndef WCOREDUMP
109 #define WCOREDUMP(x) ((x) & 0200)
110 #endif
111 #include <rx/xdr.h>
112 #include <afs/afsint.h>
113 #include <afs/afs_assert.h>
114 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
115 #if defined(AFS_VFSINCL_ENV)
116 #include <sys/vnode.h>
117 #ifdef AFS_SUN5_ENV
118 #include <sys/fs/ufs_inode.h>
119 #else
120 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
121 #include <ufs/ufs/dinode.h>
122 #include <ufs/ffs/fs.h>
123 #else
124 #include <ufs/inode.h>
125 #endif
126 #endif
127 #else /* AFS_VFSINCL_ENV */
128 #ifdef AFS_OSF_ENV
129 #include <ufs/inode.h>
130 #else /* AFS_OSF_ENV */
131 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
132 #include <sys/inode.h>
133 #endif
134 #endif
135 #endif /* AFS_VFSINCL_ENV */
136 #endif /* AFS_SGI_ENV */
137 #ifdef AFS_AIX_ENV
138 #include <sys/vfs.h>
139 #include <sys/lockf.h>
140 #else
141 #ifdef AFS_HPUX_ENV
142 #include <unistd.h>
143 #include <checklist.h>
144 #else
145 #if defined(AFS_SGI_ENV)
146 #include <unistd.h>
147 #include <fcntl.h>
148 #include <mntent.h>
149 #else
150 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
151 #ifdef AFS_SUN5_ENV
152 #include <unistd.h>
153 #include <sys/mnttab.h>
154 #include <sys/mntent.h>
155 #else
156 #include <mntent.h>
157 #endif
158 #else
159 #endif /* AFS_SGI_ENV */
160 #endif /* AFS_HPUX_ENV */
161 #endif
162 #endif
163 #include <fcntl.h>
164 #ifndef AFS_NT40_ENV
165 #include <afs/osi_inode.h>
166 #endif
167 #include <afs/cmd.h>
168 #include <afs/dir.h>
169 #include <afs/afsutil.h>
170 #include <afs/fileutil.h>
171 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
172 #ifndef AFS_NT40_ENV
173 #include <syslog.h>
174 #endif
176 #include "nfs.h"
177 #include "lwp.h"
178 #include "lock.h"
179 #include <afs/afssyscalls.h>
180 #include "ihandle.h"
181 #include "vnode.h"
182 #include "volume.h"
183 #include "partition.h"
184 #include "daemon_com.h"
185 #include "daemon_com_inline.h"
186 #include "fssync.h"
187 #include "fssync_inline.h"
188 #include "volume_inline.h"
189 #include "salvsync.h"
190 #include "viceinode.h"
191 #include "salvage.h"
192 #include "volinodes.h" /* header magic number, etc. stuff */
193 #include "vol-salvage.h"
194 #include "common.h"
195 #include "vol_internal.h"
196 #include <afs/acl.h>
197 #include <afs/prs_fs.h>
199 #ifdef FSSYNC_BUILD_CLIENT
200 #include "vg_cache.h"
201 #endif
203 #ifdef AFS_NT40_ENV
204 #include <pthread.h>
205 #endif
207 /*@+fcnmacros +macrofcndecl@*/
208 #ifdef O_LARGEFILE
209 #ifdef S_SPLINT_S
210 extern off64_t afs_lseek(int FD, off64_t O, int F);
211 #endif /*S_SPLINT_S */
212 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
213 #define afs_stat stat64
214 #define afs_fstat fstat64
215 #define afs_open open64
216 #define afs_fopen fopen64
217 #else /* !O_LARGEFILE */
218 #ifdef S_SPLINT_S
219 extern off_t afs_lseek(int FD, off_t O, int F);
220 #endif /*S_SPLINT_S */
221 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
222 #define afs_stat stat
223 #define afs_fstat fstat
224 #define afs_open open
225 #define afs_fopen fopen
226 #endif /* !O_LARGEFILE */
227 /*@=fcnmacros =macrofcndecl@*/
229 #ifdef AFS_OSF_ENV
230 extern void *calloc();
231 #endif
232 static char *TimeStamp(time_t clock, int precision);
235 int debug; /* -d flag */
236 extern int Testing; /* -n flag */
237 int ListInodeOption; /* -i flag */
238 int ShowRootFiles; /* -r flag */
239 int RebuildDirs; /* -sal flag */
240 int Parallel = 4; /* -para X flag */
241 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
242 int forceR = 0; /* -b flag */
243 int ShowLog = 0; /* -showlog flag */
244 int ShowSuid = 0; /* -showsuid flag */
245 int ShowMounts = 0; /* -showmounts flag */
246 int orphans = ORPH_IGNORE; /* -orphans option */
247 int Showmode = 0;
250 #ifndef AFS_NT40_ENV
251 int useSyslog = 0; /* -syslog flag */
252 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
253 #endif
255 #ifdef AFS_NT40_ENV
256 int canfork = 0;
257 #else
258 int canfork = 1;
259 #endif
261 #define MAXPARALLEL 32
263 int OKToZap; /* -o flag */
264 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
265 * in the volume header */
267 FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
269 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
270 * partition */
272 * information that is 'global' to a particular salvage job.
274 struct SalvInfo {
275 Device fileSysDevice; /**< The device number of the current partition
276 * being salvaged */
277 char fileSysPath[9]; /**< The path of the mounted partition currently
278 * being salvaged, i.e. the directory containing
279 * the volume headers */
280 char *fileSysPathName; /**< NT needs this to make name pretty log. */
281 IHandle_t *VGLinkH; /**< Link handle for current volume group. */
282 int VGLinkH_cnt; /**< # of references to lnk handle. */
283 struct DiskPartition64 *fileSysPartition; /**< Partition being salvaged */
285 #ifndef AFS_NT40_ENV
286 char *fileSysDeviceName; /**< The block device where the file system being
287 * salvaged was mounted */
288 char *filesysfulldev;
289 #endif
290 int VolumeChanged; /**< Set by any routine which would change the
291 * volume in a way which would require callbacks
292 * to be broken if the volume was put back on
293 * on line by an active file server */
295 VolumeDiskData VolInfo; /**< A copy of the last good or salvaged volume
296 * header dealt with */
298 int nVolumesInInodeFile; /**< Number of read-write volumes summarized */
299 int inodeFd; /**< File descriptor for inode file */
301 struct VolumeSummary *volumeSummaryp; /**< Holds all the volumes in a part */
302 int nVolumes; /**< Number of volumes (read-write and read-only)
303 * in volume summary */
304 struct InodeSummary *inodeSummary; /**< contains info on all the relevant
305 * inodes */
307 struct VnodeInfo vnodeInfo[nVNODECLASSES]; /**< contains info on all of the
308 * vnodes in the volume that
309 * we are currently looking
310 * at */
311 int useFSYNC; /**< 0 if the fileserver is unavailable; 1 if we should try
312 * to contact the fileserver over FSYNC */
315 char *tmpdir = NULL;
319 /* Forward declarations */
320 static int IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode);
321 static int AskVolumeSummary(struct SalvInfo *salvinfo,
322 VolumeId singleVolumeNumber);
323 static void MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId);
324 static void AskError(struct SalvInfo *salvinfo, VolumeId volumeId);
326 #ifdef AFS_DEMAND_ATTACH_FS
327 static int LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId);
328 #endif /* AFS_DEMAND_ATTACH_FS */
330 /* Uniquifier stored in the Inode */
331 static Unique
332 IUnique(Unique u)
334 #ifdef AFS_3DISPARES
335 return (u & 0x3fffff);
336 #else
337 #if defined(AFS_SGI_EXMAG)
338 return (u & SGI_UNIQMASK);
339 #else
340 return (u);
341 #endif /* AFS_SGI_EXMAG */
342 #endif
345 static int
346 BadError(int aerror)
348 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
349 return 1;
350 return 0; /* otherwise may be transient, e.g. EMFILE */
353 #define MAX_ARGS 128
354 #ifdef AFS_NT40_ENV
355 char *save_args[MAX_ARGS];
356 int n_save_args = 0;
357 extern pthread_t main_thread;
358 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
359 #endif
362 * Get the salvage lock if not already held. Hold until process exits.
364 * @param[in] locktype READ_LOCK or WRITE_LOCK
366 static void
367 _ObtainSalvageLock(int locktype)
369 struct VLockFile salvageLock;
370 int offset = 0;
371 int nonblock = 1;
372 int code;
374 VLockFileInit(&salvageLock, AFSDIR_SERVER_SLVGLOCK_FILEPATH);
376 code = VLockFileLock(&salvageLock, offset, locktype, nonblock);
377 if (code == EBUSY) {
378 fprintf(stderr,
379 "salvager: There appears to be another salvager running! "
380 "Aborted.\n");
381 Exit(1);
382 } else if (code) {
383 fprintf(stderr,
384 "salvager: Error %d trying to acquire salvage lock! "
385 "Aborted.\n", code);
386 Exit(1);
389 void
390 ObtainSalvageLock(void)
392 _ObtainSalvageLock(WRITE_LOCK);
394 void
395 ObtainSharedSalvageLock(void)
397 _ObtainSalvageLock(READ_LOCK);
401 #ifdef AFS_SGI_XFS_IOPS_ENV
402 /* Check if the given partition is mounted. For XFS, the root inode is not a
403 * constant. So we check the hard way.
406 IsPartitionMounted(char *part)
408 FILE *mntfp;
409 struct mntent *mntent;
411 osi_Assert(mntfp = setmntent(MOUNTED, "r"));
412 while (mntent = getmntent(mntfp)) {
413 if (!strcmp(part, mntent->mnt_dir))
414 break;
416 endmntent(mntfp);
418 return mntent ? 1 : 1;
420 #endif
421 /* Check if the given inode is the root of the filesystem. */
422 #ifndef AFS_SGI_XFS_IOPS_ENV
424 IsRootInode(struct afs_stat *status)
427 * The root inode is not a fixed value in XFS partitions. So we need to
428 * see if the partition is in the list of mounted partitions. This only
429 * affects the SalvageFileSys path, so we check there.
431 return (status->st_ino == ROOTINODE);
433 #endif
435 #ifdef AFS_AIX42_ENV
436 #ifndef AFS_NAMEI_ENV
437 /* We don't want to salvage big files filesystems, since we can't put volumes on
438 * them.
441 CheckIfBigFilesFS(char *mountPoint, char *devName)
443 struct superblock fs;
444 char name[128];
446 if (strncmp(devName, "/dev/", 5)) {
447 (void)sprintf(name, "/dev/%s", devName);
448 } else {
449 (void)strcpy(name, devName);
452 if (ReadSuper(&fs, name) < 0) {
453 Log("Unable to read superblock. Not salvaging partition %s.\n",
454 mountPoint);
455 return 1;
457 if (IsBigFilesFileSystem(&fs)) {
458 Log("Partition %s is a big files filesystem, not salvaging.\n",
459 mountPoint);
460 return 1;
462 return 0;
464 #endif
465 #endif
467 #ifdef AFS_NT40_ENV
468 #define HDSTR "\\Device\\Harddisk"
469 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
471 SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
473 #define RES_LEN 256
474 char res1[RES_LEN];
475 char res2[RES_LEN];
477 static int dowarn = 1;
479 if (!QueryDosDevice(p1->devName, res1, RES_LEN - 1))
480 return 1;
481 if (strncmp(res1, HDSTR, HDLEN)) {
482 if (dowarn) {
483 dowarn = 0;
484 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
485 res1, HDSTR, p1->devName);
488 if (!QueryDosDevice(p2->devName, res2, RES_LEN - 1))
489 return 1;
490 if (strncmp(res2, HDSTR, HDLEN)) {
491 if (dowarn) {
492 dowarn = 0;
493 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
494 res2, HDSTR, p2->devName);
498 return (0 == _strnicmp(res1, res2, RES_LEN - 1));
500 #else
501 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
502 #endif
504 /* This assumes that two partitions with the same device number divided by
505 * PartsPerDisk are on the same disk.
507 void
508 SalvageFileSysParallel(struct DiskPartition64 *partP)
510 struct job {
511 struct DiskPartition64 *partP;
512 int pid; /* Pid for this job */
513 int jobnumb; /* Log file job number */
514 struct job *nextjob; /* Next partition on disk to salvage */
516 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
517 struct job *thisjob = 0;
518 static int numjobs = 0;
519 static int jobcount = 0;
520 char buf[1024];
521 int wstatus;
522 struct job *oldjob;
523 int startjob;
524 FILE *passLog;
525 char logFileName[256];
526 int i, j, pid;
528 if (partP) {
529 /* We have a partition to salvage. Copy it into thisjob */
530 thisjob = (struct job *)malloc(sizeof(struct job));
531 if (!thisjob) {
532 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
533 return;
535 memset(thisjob, 0, sizeof(struct job));
536 thisjob->partP = partP;
537 thisjob->jobnumb = jobcount;
538 jobcount++;
539 } else if (jobcount == 0) {
540 /* We are asking to wait for all jobs (partp == 0), yet we never
541 * started any.
543 Log("No file system partitions named %s* found; not salvaged\n",
544 VICE_PARTITION_PREFIX);
545 return;
548 if (debug || Parallel == 1) {
549 if (thisjob) {
550 SalvageFileSys(thisjob->partP, 0);
551 free(thisjob);
553 return;
556 if (thisjob) {
557 /* Check to see if thisjob is for a disk that we are already
558 * salvaging. If it is, link it in as the next job to do. The
559 * jobs array has 1 entry per disk being salvages. numjobs is
560 * the total number of disks currently being salvaged. In
561 * order to keep thejobs array compact, when a disk is
562 * completed, the hightest element in the jobs array is moved
563 * down to now open slot.
565 for (j = 0; j < numjobs; j++) {
566 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
567 /* On same disk, add it to this list and return */
568 thisjob->nextjob = jobs[j]->nextjob;
569 jobs[j]->nextjob = thisjob;
570 thisjob = 0;
571 break;
576 /* Loop until we start thisjob or until all existing jobs are finished */
577 while (thisjob || (!partP && (numjobs > 0))) {
578 startjob = -1; /* No new job to start */
580 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
581 /* Either the max jobs are running or we have to wait for all
582 * the jobs to finish. In either case, we wait for at least one
583 * job to finish. When it's done, clean up after it.
585 pid = wait(&wstatus);
586 osi_Assert(pid != -1);
587 for (j = 0; j < numjobs; j++) { /* Find which job it is */
588 if (pid == jobs[j]->pid)
589 break;
591 osi_Assert(j < numjobs);
592 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
593 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
596 numjobs--; /* job no longer running */
597 oldjob = jobs[j]; /* remember */
598 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
599 free(oldjob); /* free the old job */
601 /* If there is another partition on the disk to salvage, then
602 * say we will start it (startjob). If not, then put thisjob there
603 * and say we will start it.
605 if (jobs[j]) { /* Another partitions to salvage */
606 startjob = j; /* Will start it */
607 } else { /* There is not another partition to salvage */
608 if (thisjob) {
609 jobs[j] = thisjob; /* Add thisjob */
610 thisjob = 0;
611 startjob = j; /* Will start it */
612 } else {
613 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
614 startjob = -1; /* Don't start it - already running */
617 } else {
618 /* We don't have to wait for a job to complete */
619 if (thisjob) {
620 jobs[numjobs] = thisjob; /* Add this job */
621 thisjob = 0;
622 startjob = numjobs; /* Will start it */
626 /* Start up a new salvage job on a partition in job slot "startjob" */
627 if (startjob != -1) {
628 if (!Showmode)
629 Log("Starting salvage of file system partition %s\n",
630 jobs[startjob]->partP->name);
631 #ifdef AFS_NT40_ENV
632 /* For NT, we not only fork, but re-exec the salvager. Pass in the
633 * commands and pass the child job number via the data path.
635 pid =
636 nt_SalvagePartition(jobs[startjob]->partP->name,
637 jobs[startjob]->jobnumb);
638 jobs[startjob]->pid = pid;
639 numjobs++;
640 #else
641 pid = Fork();
642 if (pid) {
643 jobs[startjob]->pid = pid;
644 numjobs++;
645 } else {
646 int fd;
648 ShowLog = 0;
649 for (fd = 0; fd < 16; fd++)
650 close(fd);
651 open(OS_DIRSEP, 0);
652 dup2(0, 1);
653 dup2(0, 2);
654 #ifndef AFS_NT40_ENV
655 if (useSyslog) {
656 openlog("salvager", LOG_PID, useSyslogFacility);
657 } else
658 #endif
660 (void)afs_snprintf(logFileName, sizeof logFileName,
661 "%s.%d",
662 AFSDIR_SERVER_SLVGLOG_FILEPATH,
663 jobs[startjob]->jobnumb);
664 logFile = afs_fopen(logFileName, "w");
666 if (!logFile)
667 logFile = stdout;
669 SalvageFileSys1(jobs[startjob]->partP, 0);
670 Exit(0);
672 #endif
674 } /* while ( thisjob || (!partP && numjobs > 0) ) */
676 /* If waited for all jobs to complete, now collect log files and return */
677 #ifndef AFS_NT40_ENV
678 if (!useSyslog) /* if syslogging - no need to collect */
679 #endif
680 if (!partP) {
681 for (i = 0; i < jobcount; i++) {
682 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
683 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
684 if ((passLog = afs_fopen(logFileName, "r"))) {
685 while (fgets(buf, sizeof(buf), passLog)) {
686 fputs(buf, logFile);
688 fclose(passLog);
690 (void)unlink(logFileName);
692 fflush(logFile);
694 return;
698 void
699 SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
701 if (!canfork || debug || Fork() == 0) {
702 SalvageFileSys1(partP, singleVolumeNumber);
703 if (canfork && !debug) {
704 ShowLog = 0;
705 Exit(0);
707 } else
708 Wait("SalvageFileSys");
711 char *
712 get_DevName(char *pbuffer, char *wpath)
714 char pbuf[128], *ptr;
715 strcpy(pbuf, pbuffer);
716 ptr = (char *)strrchr(pbuf, OS_DIRSEPC);
717 if (ptr) {
718 *ptr = '\0';
719 strcpy(wpath, pbuf);
720 } else
721 return NULL;
722 ptr = (char *)strrchr(pbuffer, OS_DIRSEPC);
723 if (ptr) {
724 strcpy(pbuffer, ptr + 1);
725 return pbuffer;
726 } else
727 return NULL;
730 void
731 SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
733 char *name, *tdir;
734 char inodeListPath[256];
735 FILE *inodeFile = NULL;
736 static char tmpDevName[100];
737 static char wpath[100];
738 struct VolumeSummary *vsp, *esp;
739 int i, j;
740 int code;
741 int tries = 0;
742 struct SalvInfo l_salvinfo;
743 struct SalvInfo *salvinfo = &l_salvinfo;
745 retry:
746 memset(salvinfo, 0, sizeof(*salvinfo));
748 tries++;
749 if (inodeFile) {
750 fclose(inodeFile);
751 inodeFile = NULL;
753 if (tries > VOL_MAX_CHECKOUT_RETRIES) {
754 Abort("Raced too many times with fileserver restarts while trying to "
755 "checkout/lock volumes; Aborted\n");
757 #ifdef AFS_DEMAND_ATTACH_FS
758 if (tries > 1) {
759 /* unlock all previous volume locks, since we're about to lock them
760 * again */
761 VLockFileReinit(&partP->volLockFile);
763 #endif /* AFS_DEMAND_ATTACH_FS */
765 salvinfo->fileSysPartition = partP;
766 salvinfo->fileSysDevice = salvinfo->fileSysPartition->device;
767 salvinfo->fileSysPathName = VPartitionPath(salvinfo->fileSysPartition);
769 #ifdef AFS_NT40_ENV
770 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
771 (void)sprintf(salvinfo->fileSysPath, "%s" OS_DIRSEP, salvinfo->fileSysPathName);
772 name = partP->devName;
773 #else
774 strlcpy(salvinfo->fileSysPath, salvinfo->fileSysPathName, sizeof(salvinfo->fileSysPath));
775 strcpy(tmpDevName, partP->devName);
776 name = get_DevName(tmpDevName, wpath);
777 salvinfo->fileSysDeviceName = name;
778 salvinfo->filesysfulldev = wpath;
779 #endif
781 if (singleVolumeNumber) {
782 #ifndef AFS_DEMAND_ATTACH_FS
783 /* only non-DAFS locks the partition when salvaging a single volume;
784 * DAFS will lock the individual volumes in the VG */
785 VLockPartition(partP->name);
786 #endif /* !AFS_DEMAND_ATTACH_FS */
788 ForceSalvage = 1;
790 /* salvageserver already setup fssync conn for us */
791 if ((programType != salvageServer) && !VConnectFS()) {
792 Abort("Couldn't connect to file server\n");
795 salvinfo->useFSYNC = 1;
796 AskOffline(salvinfo, singleVolumeNumber);
797 #ifdef AFS_DEMAND_ATTACH_FS
798 if (LockVolume(salvinfo, singleVolumeNumber)) {
799 goto retry;
801 #endif /* AFS_DEMAND_ATTACH_FS */
803 } else {
804 salvinfo->useFSYNC = 0;
805 VLockPartition(partP->name);
806 if (ForceSalvage) {
807 ForceSalvage = 1;
808 } else {
809 ForceSalvage = UseTheForceLuke(salvinfo->fileSysPath);
811 if (!Showmode)
812 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
813 partP->name, name, (Testing ? "(READONLY mode)" : ""));
814 if (ForceSalvage)
815 Log("***Forced salvage of all volumes on this partition***\n");
820 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
821 * files
824 DIR *dirp;
825 struct dirent *dp;
827 osi_Assert((dirp = opendir(salvinfo->fileSysPath)) != NULL);
828 while ((dp = readdir(dirp))) {
829 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
830 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
831 char npath[1024];
832 Log("Removing old salvager temp files %s\n", dp->d_name);
833 strcpy(npath, salvinfo->fileSysPath);
834 strcat(npath, OS_DIRSEP);
835 strcat(npath, dp->d_name);
836 OS_UNLINK(npath);
839 closedir(dirp);
841 tdir = (tmpdir ? tmpdir : salvinfo->fileSysPath);
842 #ifdef AFS_NT40_ENV
843 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
844 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
845 #else
846 snprintf(inodeListPath, 255, "%s" OS_DIRSEP "salvage.inodes.%s.%d", tdir, name,
847 getpid());
848 #endif
850 inodeFile = fopen(inodeListPath, "w+b");
851 if (!inodeFile) {
852 Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
854 #ifdef AFS_NT40_ENV
855 /* Using nt_unlink here since we're really using the delete on close
856 * semantics of unlink. In most places in the salvager, we really do
857 * mean to unlink the file at that point. Those places have been
858 * modified to actually do that so that the NT crt can be used there.
860 * jaltman - On NT delete on close cannot be applied to a file while the
861 * process has an open file handle that does not have DELETE file
862 * access and FILE_SHARE_DELETE. fopen() calls CreateFile() without
863 * delete privileges. As a result the nt_unlink() call will always
864 * fail.
866 code = nt_unlink(inodeListPath);
867 #else
868 code = unlink(inodeListPath);
869 #endif
870 if (code < 0) {
871 Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
874 if (GetInodeSummary(salvinfo, inodeFile, singleVolumeNumber) < 0) {
875 fclose(inodeFile);
876 if (singleVolumeNumber) {
877 /* the volume group -- let alone the volume -- does not exist,
878 * but we checked it out, so give it back to the fileserver */
879 AskDelete(salvinfo, singleVolumeNumber);
881 return;
883 salvinfo->inodeFd = fileno(inodeFile);
884 if (salvinfo->inodeFd == -1)
885 Abort("Temporary file %s is missing...\n", inodeListPath);
886 afs_lseek(salvinfo->inodeFd, 0L, SEEK_SET);
887 if (ListInodeOption) {
888 PrintInodeList(salvinfo);
889 if (singleVolumeNumber) {
890 /* We've checked out the volume from the fileserver, and we need
891 * to give it back. We don't know if the volume exists or not,
892 * so we don't know whether to AskOnline or not. Try to determine
893 * if the volume exists by trying to read the volume header, and
894 * AskOnline if it is readable. */
895 MaybeAskOnline(salvinfo, singleVolumeNumber);
897 return;
899 /* enumerate volumes in the partition.
900 * figure out sets of read-only + rw volumes.
901 * salvage each set, read-only volumes first, then read-write.
902 * Fix up inodes on last volume in set (whether it is read-write
903 * or read-only).
905 if (GetVolumeSummary(salvinfo, singleVolumeNumber)) {
906 goto retry;
909 if (singleVolumeNumber) {
910 /* If we delete a volume during the salvage, we indicate as such by
911 * setting the volsummary->deleted field. We need to know if we
912 * deleted a volume or not in order to know which volumes to bring
913 * back online after the salvage. If we fork, we will lose this
914 * information, since volsummary->deleted will not get set in the
915 * parent. So, don't fork. */
916 canfork = 0;
919 for (i = j = 0, vsp = salvinfo->volumeSummaryp, esp = vsp + salvinfo->nVolumes;
920 i < salvinfo->nVolumesInInodeFile; i = j) {
921 VolumeId rwvid = salvinfo->inodeSummary[i].RWvolumeId;
922 for (j = i;
923 j < salvinfo->nVolumesInInodeFile && salvinfo->inodeSummary[j].RWvolumeId == rwvid;
924 j++) {
925 VolumeId vid = salvinfo->inodeSummary[j].volumeId;
926 struct VolumeSummary *tsp;
927 /* Scan volume list (from partition root directory) looking for the
928 * current rw volume number in the volume list from the inode scan.
929 * If there is one here that is not in the inode volume list,
930 * delete it now. */
931 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
932 if (vsp->unused)
933 DeleteExtraVolumeHeaderFile(salvinfo, vsp);
935 /* Now match up the volume summary info from the root directory with the
936 * entry in the volume list obtained from scanning inodes */
937 salvinfo->inodeSummary[j].volSummary = NULL;
938 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
939 if (tsp->header.id == vid) {
940 salvinfo->inodeSummary[j].volSummary = tsp;
941 tsp->unused = 0;
942 break;
946 /* Salvage the group of volumes (several read-only + 1 read/write)
947 * starting with the current read-only volume we're looking at.
949 SalvageVolumeGroup(salvinfo, &salvinfo->inodeSummary[i], j - i);
952 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
953 for (; vsp < esp; vsp++) {
954 if (vsp->unused)
955 DeleteExtraVolumeHeaderFile(salvinfo, vsp);
958 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
959 RemoveTheForce(salvinfo->fileSysPath);
961 if (!Testing && singleVolumeNumber) {
962 int foundSVN = 0;
963 #ifdef AFS_DEMAND_ATTACH_FS
964 /* unlock vol headers so the fs can attach them when we AskOnline */
965 VLockFileReinit(&salvinfo->fileSysPartition->volLockFile);
966 #endif /* AFS_DEMAND_ATTACH_FS */
968 /* Step through the volumeSummary list and set all volumes on-line.
969 * Most volumes were taken off-line in GetVolumeSummary.
970 * If a volume was deleted, don't tell the fileserver anything, since
971 * we already told the fileserver the volume was deleted back when we
972 * we destroyed the volume header.
973 * Also, make sure we bring the singleVolumeNumber back online first.
976 for (j = 0; j < salvinfo->nVolumes; j++) {
977 if (salvinfo->volumeSummaryp[j].header.id == singleVolumeNumber) {
978 foundSVN = 1;
979 if (!salvinfo->volumeSummaryp[j].deleted) {
980 AskOnline(salvinfo, singleVolumeNumber);
985 if (!foundSVN) {
986 /* If singleVolumeNumber is not in our volumeSummary, it means that
987 * at least one other volume in the VG is on the partition, but the
988 * RW volume is not. We've already AskOffline'd it by now, though,
989 * so make sure we don't still have the volume checked out. */
990 AskDelete(salvinfo, singleVolumeNumber);
993 for (j = 0; j < salvinfo->nVolumes; j++) {
994 if (salvinfo->volumeSummaryp[j].header.id != singleVolumeNumber) {
995 if (!salvinfo->volumeSummaryp[j].deleted) {
996 AskOnline(salvinfo, salvinfo->volumeSummaryp[j].header.id);
1000 } else {
1001 if (!Showmode)
1002 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1003 salvinfo->fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
1006 fclose(inodeFile); /* SalvageVolumeGroup was the last which needed it. */
1009 void
1010 DeleteExtraVolumeHeaderFile(struct SalvInfo *salvinfo, struct VolumeSummary *vsp)
1012 char path[64];
1013 char filename[VMAXPATHLEN];
1015 if (vsp->deleted) {
1016 return;
1019 VolumeExternalName_r(vsp->header.id, filename, sizeof(filename));
1020 sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
1022 if (!Showmode)
1023 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
1024 if (!Testing) {
1025 afs_int32 code;
1026 code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, vsp->header.id, vsp->header.parent);
1027 if (code) {
1028 Log("Error %ld destroying volume disk header for volume %lu\n",
1029 afs_printable_int32_ld(code),
1030 afs_printable_uint32_lu(vsp->header.id));
1033 /* make sure we actually delete the header file; ENOENT
1034 * is fine, since VDestroyVolumeDiskHeader probably already
1035 * unlinked it */
1036 if (unlink(path) && errno != ENOENT) {
1037 Log("Unable to unlink %s (errno = %d)\n", path, errno);
1039 if (salvinfo->useFSYNC) {
1040 AskDelete(salvinfo, vsp->header.id);
1042 vsp->deleted = 1;
1047 CompareInodes(const void *_p1, const void *_p2)
1049 const struct ViceInodeInfo *p1 = _p1;
1050 const struct ViceInodeInfo *p2 = _p2;
1051 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1052 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
1053 VolumeId p1rwid, p2rwid;
1054 p1rwid =
1055 (p1->u.vnode.vnodeNumber ==
1056 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
1057 p2rwid =
1058 (p2->u.vnode.vnodeNumber ==
1059 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
1060 if (p1rwid < p2rwid)
1061 return -1;
1062 if (p1rwid > p2rwid)
1063 return 1;
1064 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1065 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1066 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1067 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
1068 if (p1->u.vnode.volumeId == p1rwid)
1069 return -1;
1070 if (p2->u.vnode.volumeId == p2rwid)
1071 return 1;
1072 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
1074 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1075 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
1076 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
1078 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1079 return -1;
1080 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1081 return 1;
1082 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1083 return -1;
1084 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1085 return 1;
1086 /* The following tests are reversed, so that the most desirable
1087 * of several similar inodes comes first */
1088 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1089 #ifdef AFS_3DISPARES
1090 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1091 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1092 return 1;
1093 #endif
1094 #ifdef AFS_SGI_EXMAG
1095 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1096 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1097 return 1;
1098 #endif
1099 return -1;
1101 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1102 #ifdef AFS_3DISPARES
1103 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1104 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1105 return -1;
1106 #endif
1107 #ifdef AFS_SGI_EXMAG
1108 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1109 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1110 return 1;
1111 #endif
1112 return 1;
1114 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1115 #ifdef AFS_3DISPARES
1116 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1117 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1118 return 1;
1119 #endif
1120 #ifdef AFS_SGI_EXMAG
1121 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1122 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1123 return 1;
1124 #endif
1125 return -1;
1127 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1128 #ifdef AFS_3DISPARES
1129 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1130 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1131 return -1;
1132 #endif
1133 #ifdef AFS_SGI_EXMAG
1134 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1135 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1136 return 1;
1137 #endif
1138 return 1;
1140 return 0;
1143 void
1144 CountVolumeInodes(struct ViceInodeInfo *ip, int maxInodes,
1145 struct InodeSummary *summary)
1147 VolumeId volume = ip->u.vnode.volumeId;
1148 VolumeId rwvolume = volume;
1149 int n, nSpecial;
1150 Unique maxunique;
1151 n = nSpecial = 0;
1152 maxunique = 0;
1153 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1154 n++;
1155 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1156 nSpecial++;
1157 rwvolume = ip->u.special.parentId;
1158 /* This isn't quite right, as there could (in error) be different
1159 * parent inodes in different special vnodes */
1160 } else {
1161 if (maxunique < ip->u.vnode.vnodeUniquifier)
1162 maxunique = ip->u.vnode.vnodeUniquifier;
1164 ip++;
1166 summary->volumeId = volume;
1167 summary->RWvolumeId = rwvolume;
1168 summary->nInodes = n;
1169 summary->nSpecialInodes = nSpecial;
1170 summary->maxUniquifier = maxunique;
1174 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
1176 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1177 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1178 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1181 /* GetInodeSummary
1183 * Collect list of inodes in file named by path. If a truly fatal error,
1184 * unlink the file and abort. For lessor errors, return -1. The file will
1185 * be unlinked by the caller.
1188 GetInodeSummary(struct SalvInfo *salvinfo, FILE *inodeFile, VolumeId singleVolumeNumber)
1190 struct afs_stat status;
1191 int forceSal, err;
1192 int code;
1193 struct ViceInodeInfo *ip, *ip_save;
1194 struct InodeSummary summary;
1195 char summaryFileName[50];
1196 FILE *summaryFile;
1197 #ifdef AFS_NT40_ENV
1198 char *dev = salvinfo->fileSysPath;
1199 char *wpath = salvinfo->fileSysPath;
1200 #else
1201 char *dev = salvinfo->fileSysDeviceName;
1202 char *wpath = salvinfo->filesysfulldev;
1203 #endif
1204 char *part = salvinfo->fileSysPath;
1205 char *tdir;
1206 int i;
1207 int retcode = 0;
1208 int deleted = 0;
1210 /* This file used to come from vfsck; cobble it up ourselves now... */
1211 if ((err =
1212 ListViceInodes(dev, salvinfo->fileSysPath, inodeFile,
1213 singleVolumeNumber ? OnlyOneVolume : 0,
1214 singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
1215 if (err == -2) {
1216 Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
1217 retcode = -1;
1218 goto error;
1220 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1222 if (forceSal && !ForceSalvage) {
1223 Log("***Forced salvage of all volumes on this partition***\n");
1224 ForceSalvage = 1;
1226 fseek(inodeFile, 0L, SEEK_SET);
1227 salvinfo->inodeFd = fileno(inodeFile);
1228 if (salvinfo->inodeFd == -1 || afs_fstat(salvinfo->inodeFd, &status) == -1) {
1229 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1231 tdir = (tmpdir ? tmpdir : part);
1232 #ifdef AFS_NT40_ENV
1233 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1234 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp."));
1235 #else
1236 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1237 "%s" OS_DIRSEP "salvage.temp.%d", tdir, getpid());
1238 #endif
1239 summaryFile = afs_fopen(summaryFileName, "a+");
1240 if (summaryFile == NULL) {
1241 Abort("Unable to create inode summary file\n");
1244 #ifdef AFS_NT40_ENV
1245 /* Using nt_unlink here since we're really using the delete on close
1246 * semantics of unlink. In most places in the salvager, we really do
1247 * mean to unlink the file at that point. Those places have been
1248 * modified to actually do that so that the NT crt can be used there.
1250 * jaltman - As commented elsewhere, this cannot work because fopen()
1251 * does not open files with DELETE and FILE_SHARE_DELETE.
1253 code = nt_unlink(summaryFileName);
1254 #else
1255 code = unlink(summaryFileName);
1256 #endif
1257 if (code < 0) {
1258 Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
1261 if (!canfork || debug || Fork() == 0) {
1262 int nInodes;
1263 unsigned long st_size=(unsigned long) status.st_size;
1264 nInodes = st_size / sizeof(struct ViceInodeInfo);
1265 if (nInodes == 0) {
1266 fclose(summaryFile);
1267 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1268 RemoveTheForce(salvinfo->fileSysPath);
1269 else {
1270 struct VolumeSummary *vsp;
1271 int i;
1272 int foundSVN = 0;
1274 GetVolumeSummary(salvinfo, singleVolumeNumber);
1276 for (i = 0, vsp = salvinfo->volumeSummaryp; i < salvinfo->nVolumes; i++) {
1277 if (vsp->unused) {
1278 if (vsp->header.id == singleVolumeNumber) {
1279 foundSVN = 1;
1281 DeleteExtraVolumeHeaderFile(salvinfo, vsp);
1285 if (!foundSVN) {
1286 if (Testing) {
1287 MaybeAskOnline(salvinfo, singleVolumeNumber);
1288 } else {
1289 /* make sure we get rid of stray .vol headers, even if
1290 * they're not in our volume summary (might happen if
1291 * e.g. something else created them and they're not in the
1292 * fileserver VGC) */
1293 VDestroyVolumeDiskHeader(salvinfo->fileSysPartition,
1294 singleVolumeNumber, 0 /*parent*/);
1295 AskDelete(salvinfo, singleVolumeNumber);
1299 Log("%s vice inodes on %s; not salvaged\n",
1300 singleVolumeNumber ? "No applicable" : "No", dev);
1301 retcode = -1;
1302 deleted = 1;
1303 goto error;
1305 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1306 if (ip == NULL) {
1307 fclose(summaryFile);
1308 Abort
1309 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1310 dev);
1312 if (read(salvinfo->inodeFd, ip, st_size) != st_size) {
1313 fclose(summaryFile);
1314 Abort("Unable to read inode table; %s not salvaged\n", dev);
1316 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1317 if (afs_lseek(salvinfo->inodeFd, 0, SEEK_SET) == -1
1318 || write(salvinfo->inodeFd, ip, st_size) != st_size) {
1319 fclose(summaryFile);
1320 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1322 summary.index = 0;
1323 ip_save = ip;
1324 while (nInodes) {
1325 CountVolumeInodes(ip, nInodes, &summary);
1326 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1327 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1328 fclose(summaryFile);
1329 retcode = -1;
1330 goto error;
1332 summary.index += (summary.nInodes);
1333 nInodes -= summary.nInodes;
1334 ip += summary.nInodes;
1336 free(ip_save);
1337 ip = ip_save = NULL;
1338 /* Following fflush is not fclose, because if it was debug mode would not work */
1339 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1340 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1341 fclose(summaryFile);
1342 retcode = -1;
1343 goto error;
1345 if (canfork && !debug) {
1346 ShowLog = 0;
1347 Exit(0);
1349 } else {
1350 if (Wait("Inode summary") == -1) {
1351 fclose(summaryFile);
1352 Exit(1); /* salvage of this partition aborted */
1355 osi_Assert(afs_fstat(fileno(summaryFile), &status) != -1);
1356 if (status.st_size != 0) {
1357 int ret;
1358 unsigned long st_status=(unsigned long)status.st_size;
1359 salvinfo->inodeSummary = (struct InodeSummary *)malloc(st_status);
1360 osi_Assert(salvinfo->inodeSummary != NULL);
1361 /* For GNU we need to do lseek to get the file pointer moved. */
1362 osi_Assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1363 ret = read(fileno(summaryFile), salvinfo->inodeSummary, st_status);
1364 osi_Assert(ret == st_status);
1366 salvinfo->nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1367 for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
1368 salvinfo->inodeSummary[i].volSummary = NULL;
1370 Log("%d nVolumesInInodeFile %lu \n",salvinfo->nVolumesInInodeFile,(unsigned long)(status.st_size));
1371 fclose(summaryFile);
1373 error:
1374 if (retcode && singleVolumeNumber && !deleted) {
1375 AskError(salvinfo, singleVolumeNumber);
1378 return retcode;
1381 /* Comparison routine for volume sort.
1382 This is setup so that a read-write volume comes immediately before
1383 any read-only clones of that volume */
1385 CompareVolumes(const void *_p1, const void *_p2)
1387 const struct VolumeSummary *p1 = _p1;
1388 const struct VolumeSummary *p2 = _p2;
1389 if (p1->header.parent != p2->header.parent)
1390 return p1->header.parent < p2->header.parent ? -1 : 1;
1391 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1392 return -1;
1393 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1394 return 1;
1395 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1399 * Gleans volumeSummary information by asking the fileserver
1401 * @param[in] singleVolumeNumber the volume we're salvaging. 0 if we're
1402 * salvaging a whole partition
1404 * @return whether we obtained the volume summary information or not
1405 * @retval 0 success; we obtained the volume summary information
1406 * @retval -1 we raced with a fileserver restart; volume locks and checkout
1407 * must be retried
1408 * @retval 1 we did not get the volume summary information; either the
1409 * fileserver responded with an error, or we are not supposed to
1410 * ask the fileserver for the information (e.g. we are salvaging
1411 * the entire partition or we are not the salvageserver)
1413 * @note for non-DAFS, always returns 1
1415 static int
1416 AskVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1418 afs_int32 code = 1;
1419 #if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
1420 if (programType == salvageServer) {
1421 if (singleVolumeNumber) {
1422 FSSYNC_VGQry_response_t q_res;
1423 SYNC_response res;
1424 struct VolumeSummary *vsp;
1425 int i;
1426 struct VolumeDiskHeader diskHdr;
1428 memset(&res, 0, sizeof(res));
1430 code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1433 * We must wait for the partition to finish scanning before
1434 * can continue, since we will not know if we got the entire
1435 * VG membership unless the partition is fully scanned.
1436 * We could, in theory, just scan the partition ourselves if
1437 * the VG cache is not ready, but we would be doing the exact
1438 * same scan the fileserver is doing; it will almost always
1439 * be faster to wait for the fileserver. The only exceptions
1440 * are if the partition does not take very long to scan, and
1441 * in that case it's fast either way, so who cares?
1443 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
1444 Log("waiting for fileserver to finish scanning partition %s...\n",
1445 salvinfo->fileSysPartition->name);
1447 for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
1448 /* linearly ramp up from 1 to 10 seconds; nothing fancy,
1449 * just so small partitions don't need to wait over 10
1450 * seconds every time, and large partitions are generally
1451 * polled only once every ten seconds. */
1452 sleep((i > 10) ? (i = 10) : i);
1454 code = FSYNC_VGCQuery(salvinfo->fileSysPartition->name, singleVolumeNumber, &q_res, &res);
1458 if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
1459 /* This can happen if there's no header for the volume
1460 * we're salvaging, or no headers exist for the VG (if
1461 * we're salvaging an RW). Act as if we got a response
1462 * with no VG members. The headers may be created during
1463 * salvaging, if there are inodes in this VG. */
1464 code = 0;
1465 memset(&q_res, 0, sizeof(q_res));
1466 q_res.rw = singleVolumeNumber;
1469 if (code) {
1470 Log("fileserver refused VGCQuery request for volume %lu on "
1471 "partition %s, code %ld reason %ld\n",
1472 afs_printable_uint32_lu(singleVolumeNumber),
1473 salvinfo->fileSysPartition->name,
1474 afs_printable_int32_ld(code),
1475 afs_printable_int32_ld(res.hdr.reason));
1476 goto done;
1479 if (q_res.rw != singleVolumeNumber) {
1480 Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
1481 afs_printable_uint32_lu(singleVolumeNumber),
1482 afs_printable_uint32_lu(q_res.rw));
1483 #ifdef SALVSYNC_BUILD_CLIENT
1484 if (SALVSYNC_LinkVolume(q_res.rw,
1485 singleVolumeNumber,
1486 salvinfo->fileSysPartition->name,
1487 NULL) != SYNC_OK) {
1488 Log("schedule request failed\n");
1490 #endif /* SALVSYNC_BUILD_CLIENT */
1491 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1494 salvinfo->volumeSummaryp = calloc(VOL_VG_MAX_VOLS, sizeof(struct VolumeSummary));
1495 osi_Assert(salvinfo->volumeSummaryp != NULL);
1497 salvinfo->nVolumes = 0;
1498 vsp = salvinfo->volumeSummaryp;
1500 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
1501 char name[VMAXPATHLEN];
1503 if (!q_res.children[i]) {
1504 continue;
1507 /* AskOffline for singleVolumeNumber was called much earlier */
1508 if (q_res.children[i] != singleVolumeNumber) {
1509 AskOffline(salvinfo, q_res.children[i]);
1510 if (LockVolume(salvinfo, q_res.children[i])) {
1511 /* need to retry */
1512 return -1;
1516 code = VReadVolumeDiskHeader(q_res.children[i], salvinfo->fileSysPartition, &diskHdr);
1517 if (code) {
1518 Log("Cannot read header for %lu; trying to salvage group anyway\n",
1519 afs_printable_uint32_lu(q_res.children[i]));
1520 code = 0;
1521 continue;
1524 DiskToVolumeHeader(&vsp->header, &diskHdr);
1525 VolumeExternalName_r(q_res.children[i], name, sizeof(name));
1526 vsp->unused = 1;
1527 salvinfo->nVolumes++;
1528 vsp++;
1531 qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1532 CompareVolumes);
1534 done:
1535 if (code) {
1536 Log("Cannot get volume summary from fileserver; falling back to scanning "
1537 "entire partition\n");
1540 #endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
1541 return code;
1545 * count how many volume headers are found by VWalkVolumeHeaders.
1547 * @param[in] dp the disk partition (unused)
1548 * @param[in] name full path to the .vol header (unused)
1549 * @param[in] hdr the header data (unused)
1550 * @param[in] last whether this is the last try or not (unused)
1551 * @param[in] rock actually an afs_int32*; the running count of how many
1552 * volumes we have found
1554 * @retval 0 always
1556 static int
1557 CountHeader(struct DiskPartition64 *dp, const char *name,
1558 struct VolumeDiskHeader *hdr, int last, void *rock)
1560 afs_int32 *nvols = (afs_int32 *)rock;
1561 (*nvols)++;
1562 return 0;
1566 * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
1567 * data.
1569 struct SalvageScanParams {
1570 VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
1571 * vol id of the VG we're salvaging */
1572 struct VolumeSummary *vsp; /**< ptr to the current volume summary object
1573 * we're filling in */
1574 afs_int32 nVolumes; /**< # of vols we've encountered */
1575 afs_int32 totalVolumes; /**< max # of vols we should encounter (the
1576 * # of vols we've alloc'd memory for) */
1577 int retry; /**< do we need to retry vol lock/checkout? */
1578 struct SalvInfo *salvinfo; /**< salvage job info */
1582 * records volume summary info found from VWalkVolumeHeaders.
1584 * Found volumes are also taken offline if they are in the specific volume
1585 * group we are looking for.
1587 * @param[in] dp the disk partition
1588 * @param[in] name full path to the .vol header
1589 * @param[in] hdr the header data
1590 * @param[in] last 1 if this is the last try to read the header, 0 otherwise
1591 * @param[in] rock actually a struct SalvageScanParams*, containing the
1592 * information needed to record the volume summary data
1594 * @return operation status
1595 * @retval 0 success
1596 * @retval -1 volume locking raced with fileserver restart; checking out
1597 * and locking volumes needs to be retried
1598 * @retval 1 volume header is mis-named and should be deleted
1600 static int
1601 RecordHeader(struct DiskPartition64 *dp, const char *name,
1602 struct VolumeDiskHeader *hdr, int last, void *rock)
1604 char nameShouldBe[64];
1605 struct SalvageScanParams *params;
1606 struct VolumeSummary summary;
1607 VolumeId singleVolumeNumber;
1608 struct SalvInfo *salvinfo;
1610 params = (struct SalvageScanParams *)rock;
1612 memset(&summary, 0, sizeof(summary));
1614 singleVolumeNumber = params->singleVolumeNumber;
1615 salvinfo = params->salvinfo;
1617 DiskToVolumeHeader(&summary.header, hdr);
1619 if (singleVolumeNumber && summary.header.id == singleVolumeNumber
1620 && summary.header.parent != singleVolumeNumber) {
1622 if (programType == salvageServer) {
1623 #ifdef SALVSYNC_BUILD_CLIENT
1624 Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
1625 summary.header.id, summary.header.parent);
1626 if (SALVSYNC_LinkVolume(summary.header.parent,
1627 summary.header.id,
1628 dp->name,
1629 NULL) != SYNC_OK) {
1630 Log("schedule request failed\n");
1632 #endif
1633 Exit(SALSRV_EXIT_VOLGROUP_LINK);
1635 } else {
1636 Log("%u is a read-only volume; not salvaged\n",
1637 singleVolumeNumber);
1638 Exit(1);
1642 if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
1643 || summary.header.parent == singleVolumeNumber) {
1645 /* check if the header file is incorrectly named */
1646 int badname = 0;
1647 const char *base = strrchr(name, OS_DIRSEPC);
1648 if (base) {
1649 base++;
1650 } else {
1651 base = name;
1654 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1655 VFORMAT, afs_printable_uint32_lu(summary.header.id));
1658 if (strcmp(nameShouldBe, base)) {
1659 /* .vol file has wrong name; retry/delete */
1660 badname = 1;
1663 if (!badname || last) {
1664 /* only offline the volume if the header is good, or if this is
1665 * the last try looking at it; avoid AskOffline'ing the same vol
1666 * multiple times */
1668 if (singleVolumeNumber
1669 && summary.header.id != singleVolumeNumber) {
1670 /* don't offline singleVolumeNumber; we already did that
1671 * earlier */
1673 AskOffline(salvinfo, summary.header.id);
1675 #ifdef AFS_DEMAND_ATTACH_FS
1676 if (!badname) {
1677 /* don't lock the volume if the header is bad, since we're
1678 * about to delete it anyway. */
1679 if (LockVolume(salvinfo, summary.header.id)) {
1680 params->retry = 1;
1681 return -1;
1684 #endif /* AFS_DEMAND_ATTACH_FS */
1687 if (badname) {
1688 if (last && !Showmode) {
1689 Log("Volume header file %s is incorrectly named (should be %s "
1690 "not %s); %sdeleted (it will be recreated later, if "
1691 "necessary)\n", name, nameShouldBe, base,
1692 (Testing ? "it would have been " : ""));
1694 return 1;
1697 summary.unused = 1;
1698 params->nVolumes++;
1700 if (params->nVolumes > params->totalVolumes) {
1701 /* We found more volumes than we found on the first partition walk;
1702 * apparently something created a volume while we were
1703 * partition-salvaging, or we found more than 20 vols when salvaging a
1704 * particular volume. Abort if we detect this, since other programs
1705 * supposed to not touch the partition while it is partition-salvaging,
1706 * and we shouldn't find more than 20 vols in a VG.
1708 Abort("Found %ld vol headers, but should have found at most %ld! "
1709 "Make sure the volserver/fileserver are not running at the "
1710 "same time as a partition salvage\n",
1711 afs_printable_int32_ld(params->nVolumes),
1712 afs_printable_int32_ld(params->totalVolumes));
1715 memcpy(params->vsp, &summary, sizeof(summary));
1716 params->vsp++;
1719 return 0;
1723 * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
1725 * If the header could not be read in at all, the header is always unlinked.
1726 * If instead RecordHeader said the header was bad (that is, the header file
1727 * is mis-named), we only unlink if we are doing a partition salvage, as
1728 * opposed to salvaging a specific volume group.
1730 * @param[in] dp the disk partition
1731 * @param[in] name full path to the .vol header
1732 * @param[in] hdr header data, or NULL if the header could not be read
1733 * @param[in] rock actually a struct SalvageScanParams*, with some information
1734 * about the scan
1736 static void
1737 UnlinkHeader(struct DiskPartition64 *dp, const char *name,
1738 struct VolumeDiskHeader *hdr, void *rock)
1740 struct SalvageScanParams *params;
1741 int dounlink = 0;
1743 params = (struct SalvageScanParams *)rock;
1745 if (!hdr) {
1746 /* no header; header is too bogus to read in at all */
1747 if (!Showmode) {
1748 Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
1750 if (!Testing) {
1751 dounlink = 1;
1754 } else if (!params->singleVolumeNumber) {
1755 /* We were able to read in a header, but RecordHeader said something
1756 * was wrong with it. We only unlink those if we are doing a partition
1757 * salvage. */
1758 if (!Testing) {
1759 dounlink = 1;
1763 if (dounlink && unlink(name)) {
1764 Log("Error %d while trying to unlink %s\n", errno, name);
1769 * Populates salvinfo->volumeSummaryp with volume summary information, either by asking
1770 * the fileserver for VG information, or by scanning the /vicepX partition.
1772 * @param[in] singleVolumeNumber the volume ID of the single volume group we
1773 * are salvaging, or 0 if this is a partition
1774 * salvage
1776 * @return operation status
1777 * @retval 0 success
1778 * @retval -1 we raced with a fileserver restart; checking out and locking
1779 * volumes must be retried
1782 GetVolumeSummary(struct SalvInfo *salvinfo, VolumeId singleVolumeNumber)
1784 afs_int32 nvols = 0;
1785 struct SalvageScanParams params;
1786 int code;
1788 code = AskVolumeSummary(salvinfo, singleVolumeNumber);
1789 if (code == 0) {
1790 /* we successfully got the vol information from the fileserver; no
1791 * need to scan the partition */
1792 return 0;
1794 if (code < 0) {
1795 /* we need to retry volume checkout */
1796 return code;
1799 if (!singleVolumeNumber) {
1800 /* Count how many volumes we have in /vicepX */
1801 code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, CountHeader,
1802 NULL, &nvols);
1803 if (code < 0) {
1804 Abort("Can't read directory %s; not salvaged\n", salvinfo->fileSysPath);
1806 if (!nvols)
1807 nvols = 1;
1808 } else {
1809 nvols = VOL_VG_MAX_VOLS;
1812 salvinfo->volumeSummaryp = calloc(nvols, sizeof(struct VolumeSummary));
1813 osi_Assert(salvinfo->volumeSummaryp != NULL);
1815 params.singleVolumeNumber = singleVolumeNumber;
1816 params.vsp = salvinfo->volumeSummaryp;
1817 params.nVolumes = 0;
1818 params.totalVolumes = nvols;
1819 params.retry = 0;
1820 params.salvinfo = salvinfo;
1822 /* walk the partition directory of volume headers and record the info
1823 * about them; unlinking invalid headers */
1824 code = VWalkVolumeHeaders(salvinfo->fileSysPartition, salvinfo->fileSysPath, RecordHeader,
1825 UnlinkHeader, &params);
1826 if (params.retry) {
1827 /* we apparently need to retry checking-out/locking volumes */
1828 return -1;
1830 if (code < 0) {
1831 Abort("Failed to get volume header summary\n");
1833 salvinfo->nVolumes = params.nVolumes;
1835 qsort(salvinfo->volumeSummaryp, salvinfo->nVolumes, sizeof(struct VolumeSummary),
1836 CompareVolumes);
1838 return 0;
1841 /* Find the link table. This should be associated with the RW volume, even
1842 * if there is only an RO volume at this site.
1844 Inode
1845 FindLinkHandle(struct InodeSummary *isp, int nVols,
1846 struct ViceInodeInfo *allInodes)
1848 int i, j;
1849 struct ViceInodeInfo *ip;
1851 for (i = 0; i < nVols; i++) {
1852 ip = allInodes + isp[i].index;
1853 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1854 if (ip[j].u.special.volumeId == isp->RWvolumeId &&
1855 ip[j].u.special.parentId == isp->RWvolumeId &&
1856 ip[j].u.special.type == VI_LINKTABLE) {
1857 return ip[j].inodeNumber;
1861 return (Inode) - 1;
1864 #ifdef AFS_NAMEI_ENV
1865 static int
1866 CheckDupLinktable(struct SalvInfo *salvinfo, struct InodeSummary *isp, struct ViceInodeInfo *ip)
1868 afs_ino_str_t stmp;
1869 if (ip->u.vnode.vnodeNumber != INODESPECIAL) {
1870 /* not a linktable; process as a normal file */
1871 return 0;
1873 if (ip->u.special.type != VI_LINKTABLE) {
1874 /* not a linktable; process as a normal file */
1875 return 0;
1878 /* make sure nothing inc/decs it */
1879 ip->linkCount = 0;
1881 if (ip->u.special.volumeId == ip->u.special.parentId) {
1882 /* This is a little weird, but shouldn't break anything, and there is
1883 * no known way that this can happen; just do nothing, in case deleting
1884 * it would screw something up. */
1885 Log("Inode %s appears to be a valid linktable for id (%u), but it's not\n",
1886 PrintInode(stmp, ip->inodeNumber), ip->u.special.parentId);
1887 Log("the linktable for our volume group (%u). This is unusual, since\n",
1888 isp->RWvolumeId);
1889 Log("there should only be one linktable per volume group. I'm leaving\n");
1890 Log("it alone, just to be safe.\n");
1891 return -1;
1894 Log("Linktable %s appears to be invalid (parentid/volumeid mismatch: %u != %u)\n",
1895 PrintInode(stmp, ip->inodeNumber), ip->u.special.parentId, ip->u.special.volumeId);
1896 if (Testing) {
1897 Log("Would have deleted linktable inode %s\n", PrintInode(stmp, ip->inodeNumber));
1898 } else {
1899 IHandle_t *tmpH;
1900 namei_t ufs_name;
1902 Log("Deleting linktable inode %s\n", PrintInode(stmp, ip->inodeNumber));
1903 IH_INIT(tmpH, salvinfo->fileSysDevice, isp->RWvolumeId, ip->inodeNumber);
1904 namei_HandleToName(&ufs_name, tmpH);
1905 if (unlink(ufs_name.n_path) < 0) {
1906 Log("Error %d unlinking path %s\n", errno, ufs_name.n_path);
1910 return -1;
1912 #endif
1915 CreateLinkTable(struct SalvInfo *salvinfo, struct InodeSummary *isp, Inode ino)
1917 struct versionStamp version;
1918 FdHandle_t *fdP;
1920 if (!VALID_INO(ino))
1921 ino =
1922 IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->RWvolumeId,
1923 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1924 if (!VALID_INO(ino))
1925 Abort
1926 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1927 isp->RWvolumeId, errno);
1928 IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
1929 fdP = IH_OPEN(salvinfo->VGLinkH);
1930 if (fdP == NULL)
1931 Abort("Can't open link table for volume %u (error = %d)\n",
1932 isp->RWvolumeId, errno);
1934 if (FDH_TRUNC(fdP, sizeof(version) + sizeof(short)) < 0)
1935 Abort("Can't truncate link table for volume %u (error = %d)\n",
1936 isp->RWvolumeId, errno);
1938 version.magic = LINKTABLEMAGIC;
1939 version.version = LINKTABLEVERSION;
1941 if (FDH_PWRITE(fdP, (char *)&version, sizeof(version), 0)
1942 != sizeof(version))
1943 Abort("Can't truncate link table for volume %u (error = %d)\n",
1944 isp->RWvolumeId, errno);
1946 FDH_REALLYCLOSE(fdP);
1948 /* If the volume summary exits (i.e., the V*.vol header file exists),
1949 * then set this inode there as well.
1951 if (isp->volSummary)
1952 isp->volSummary->header.linkTable = ino;
1954 return 0;
1957 #ifdef AFS_NT40_ENV
1958 void *
1959 nt_SVG(void *arg)
1961 SVGParms_t *parms = (SVGParms_t *) arg;
1962 DoSalvageVolumeGroup(parms->svgp_salvinfo, parms->svgp_inodeSummaryp, parms->svgp_count);
1963 return NULL;
1966 void
1967 SalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
1969 pthread_t tid;
1970 pthread_attr_t tattr;
1971 int code;
1972 SVGParms_t parms;
1974 /* Initialize per volume global variables, even if later code does so */
1975 salvinfo->VolumeChanged = 0;
1976 salvinfo->VGLinkH = NULL;
1977 salvinfo->VGLinkH_cnt = 0;
1978 memset(&salvinfo->VolInfo, 0, sizeof(salvinfo->VolInfo));
1980 parms.svgp_inodeSummaryp = isp;
1981 parms.svgp_count = nVols;
1982 parms.svgp_salvinfo = salvinfo;
1983 code = pthread_attr_init(&tattr);
1984 if (code) {
1985 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1986 isp->RWvolumeId);
1987 return;
1989 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1990 if (code) {
1991 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1992 return;
1994 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1995 if (code) {
1996 Log("Failed to create thread to salvage volume group %u\n",
1997 isp->RWvolumeId);
1998 return;
2000 (void)pthread_join(tid, NULL);
2002 #endif /* AFS_NT40_ENV */
2004 void
2005 DoSalvageVolumeGroup(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
2007 struct ViceInodeInfo *inodes, *allInodes, *ip;
2008 int i, totalInodes, size, salvageTo;
2009 int haveRWvolume;
2010 int check;
2011 Inode ino;
2012 int dec_VGLinkH = 0;
2013 int VGLinkH_p1 =0;
2014 FdHandle_t *fdP = NULL;
2016 salvinfo->VGLinkH_cnt = 0;
2017 haveRWvolume = (isp->volumeId == isp->RWvolumeId
2018 && isp->nSpecialInodes > 0);
2019 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
2020 if (!ForceSalvage && QuickCheck(salvinfo, isp, nVols))
2021 return;
2023 if (ShowMounts && !haveRWvolume)
2024 return;
2025 if (canfork && !debug && Fork() != 0) {
2026 (void)Wait("Salvage volume group");
2027 return;
2029 for (i = 0, totalInodes = 0; i < nVols; i++)
2030 totalInodes += isp[i].nInodes;
2031 size = totalInodes * sizeof(struct ViceInodeInfo);
2032 inodes = (struct ViceInodeInfo *)malloc(size);
2033 allInodes = inodes - isp->index; /* this would the base of all the inodes
2034 * for the partition, if all the inodes
2035 * had been read into memory */
2036 osi_Assert(afs_lseek
2037 (salvinfo->inodeFd, isp->index * sizeof(struct ViceInodeInfo),
2038 SEEK_SET) != -1);
2039 osi_Assert(read(salvinfo->inodeFd, inodes, size) == size);
2041 /* Don't try to salvage a read write volume if there isn't one on this
2042 * partition */
2043 salvageTo = haveRWvolume ? 0 : 1;
2045 #ifdef AFS_NAMEI_ENV
2046 ino = FindLinkHandle(isp, nVols, allInodes);
2047 if (VALID_INO(ino)) {
2048 IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, isp->RWvolumeId, ino);
2049 fdP = IH_OPEN(salvinfo->VGLinkH);
2051 if (VALID_INO(ino) && fdP != NULL) {
2052 struct versionStamp header;
2053 afs_sfsize_t nBytes;
2055 nBytes = FDH_PREAD(fdP, (char *)&header, sizeof(struct versionStamp), 0);
2056 if (nBytes != sizeof(struct versionStamp)
2057 || header.magic != LINKTABLEMAGIC) {
2058 Log("Bad linktable header for volume %u.\n", isp->RWvolumeId);
2059 FDH_REALLYCLOSE(fdP);
2060 fdP = NULL;
2063 if (!VALID_INO(ino) || fdP == NULL) {
2064 Log("%s link table for volume %u.\n",
2065 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
2066 if (Testing) {
2067 IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
2068 } else {
2069 int i, j;
2070 struct ViceInodeInfo *ip;
2071 CreateLinkTable(salvinfo, isp, ino);
2072 fdP = IH_OPEN(salvinfo->VGLinkH);
2073 /* Sync fake 1 link counts to the link table, now that it exists */
2074 if (fdP) {
2075 for (i = 0; i < nVols; i++) {
2076 ip = allInodes + isp[i].index;
2077 for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
2078 namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
2079 ip[j].linkCount = 1;
2085 if (fdP)
2086 FDH_REALLYCLOSE(fdP);
2087 #else
2088 IH_INIT(salvinfo->VGLinkH, salvinfo->fileSysDevice, -1, -1);
2089 #endif
2091 /* Salvage in reverse order--read/write volume last; this way any
2092 * Inodes not referenced by the time we salvage the read/write volume
2093 * can be picked up by the read/write volume */
2094 /* ACTUALLY, that's not done right now--the inodes just vanish */
2095 for (i = nVols - 1; i >= salvageTo; i--) {
2096 int rw = (i == 0);
2097 struct InodeSummary *lisp = &isp[i];
2098 #ifdef AFS_NAMEI_ENV
2099 if (rw && (nVols > 1 || isp[i].nSpecialInodes == isp[i].nInodes)) {
2100 /* If nVols > 1, we have more than one vol in this volgroup, so
2101 * the RW inodes we detected may just be for the linktable, and
2102 * there is no actual RW volume.
2104 * Additionally, if we only have linktable inodes (no other
2105 * special inodes, no data inodes), there is also no actual RW
2106 * volume to salvage; this is just cruft left behind by something
2107 * else. In that case nVols will only be 1, though, so also
2108 * perform this linktables-only check if we don't have any
2109 * non-special inodes. */
2110 int inode_i;
2111 int all_linktables = 1;
2112 for (inode_i = 0; inode_i < isp[i].nSpecialInodes; inode_i++) {
2113 if (inodes[inode_i].u.special.type != VI_LINKTABLE) {
2114 all_linktables = 0;
2115 break;
2118 if (all_linktables) {
2119 /* All we have are linktable special inodes, so skip salvaging
2120 * the RW; there was never an RW volume here. If we don't do
2121 * this, we risk creating a new "phantom" RW that the VLDB
2122 * doesn't know about, which is confusing and can cause
2123 * problems. */
2124 haveRWvolume = 0;
2125 continue;
2128 #endif
2129 if (!Showmode)
2130 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
2131 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
2132 /* Check inodes twice. The second time do things seriously. This
2133 * way the whole RO volume can be deleted, below, if anything goes wrong */
2134 for (check = 1; check >= 0; check--) {
2135 int deleteMe;
2136 if (SalvageVolumeHeaderFile(salvinfo, lisp, allInodes, rw, check, &deleteMe)
2137 == -1) {
2138 MaybeZapVolume(salvinfo, lisp, "Volume header", deleteMe, check);
2139 if (rw && deleteMe) {
2140 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
2141 * volume won't be called */
2142 break;
2144 if (!rw)
2145 break;
2147 if (rw && check == 1)
2148 continue;
2149 if (SalvageVnodes(salvinfo, isp, lisp, allInodes, check) == -1) {
2150 MaybeZapVolume(salvinfo, lisp, "Vnode index", 0, check);
2151 break;
2156 /* Fix actual inode counts */
2157 if (!Showmode) {
2158 afs_ino_str_t stmp;
2159 Log("totalInodes %d\n",totalInodes);
2160 for (ip = inodes; totalInodes; ip++, totalInodes--) {
2161 static int TraceBadLinkCounts = 0;
2162 #ifdef AFS_NAMEI_ENV
2163 if (salvinfo->VGLinkH->ih_ino == ip->inodeNumber) {
2164 dec_VGLinkH = ip->linkCount - salvinfo->VGLinkH_cnt;
2165 VGLinkH_p1 = ip->u.param[0];
2166 continue; /* Deal with this last. */
2167 } else if (CheckDupLinktable(salvinfo, isp, ip)) {
2168 /* Don't touch this inode; CheckDupLinktable has handled it */
2169 continue;
2171 #endif
2172 if (ip->linkCount != 0 && TraceBadLinkCounts) {
2173 TraceBadLinkCounts--; /* Limit reports, per volume */
2174 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(stmp, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
2176 while (ip->linkCount > 0) {
2177 /* below used to assert, not break */
2178 if (!Testing) {
2179 if (IH_DEC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2180 Log("idec failed. inode %s errno %d\n",
2181 PrintInode(stmp, ip->inodeNumber), errno);
2182 break;
2185 ip->linkCount--;
2187 while (ip->linkCount < 0) {
2188 /* these used to be asserts */
2189 if (!Testing) {
2190 if (IH_INC(salvinfo->VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2191 Log("iinc failed. inode %s errno %d\n",
2192 PrintInode(stmp, ip->inodeNumber), errno);
2193 break;
2196 ip->linkCount++;
2199 #ifdef AFS_NAMEI_ENV
2200 while (dec_VGLinkH > 0) {
2201 if (IH_DEC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2202 Log("idec failed on link table, errno = %d\n", errno);
2204 dec_VGLinkH--;
2206 while (dec_VGLinkH < 0) {
2207 if (IH_INC(salvinfo->VGLinkH, salvinfo->VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2208 Log("iinc failed on link table, errno = %d\n", errno);
2210 dec_VGLinkH++;
2212 #endif
2214 free(inodes);
2215 /* Directory consistency checks on the rw volume */
2216 if (haveRWvolume)
2217 SalvageVolume(salvinfo, isp, salvinfo->VGLinkH);
2218 IH_RELEASE(salvinfo->VGLinkH);
2220 if (canfork && !debug) {
2221 ShowLog = 0;
2222 Exit(0);
2227 QuickCheck(struct SalvInfo *salvinfo, struct InodeSummary *isp, int nVols)
2229 /* Check headers BEFORE forking */
2230 int i;
2231 IHandle_t *h;
2233 for (i = 0; i < nVols; i++) {
2234 struct VolumeSummary *vs = isp[i].volSummary;
2235 VolumeDiskData volHeader;
2236 if (!vs) {
2237 /* Don't salvage just because phantom rw volume is there... */
2238 /* (If a read-only volume exists, read/write inodes must also exist) */
2239 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2240 continue;
2241 return 0;
2243 IH_INIT(h, salvinfo->fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2244 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2245 == sizeof(volHeader)
2246 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2247 && volHeader.dontSalvage == DONT_SALVAGE
2248 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2249 if (volHeader.inUse != 0) {
2250 volHeader.inUse = 0;
2251 volHeader.inService = 1;
2252 if (!Testing) {
2253 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2254 != sizeof(volHeader)) {
2255 IH_RELEASE(h);
2256 return 0;
2260 IH_RELEASE(h);
2261 } else {
2262 IH_RELEASE(h);
2263 return 0;
2266 return 1;
2270 /* SalvageVolumeHeaderFile
2272 * Salvage the top level V*.vol header file. Make sure the special files
2273 * exist and that there are no duplicates.
2275 * Calls SalvageHeader for each possible type of volume special file.
2279 SalvageVolumeHeaderFile(struct SalvInfo *salvinfo, struct InodeSummary *isp,
2280 struct ViceInodeInfo *inodes, int RW,
2281 int check, int *deleteMe)
2283 int i;
2284 struct ViceInodeInfo *ip;
2285 int allinodesobsolete = 1;
2286 struct VolumeDiskHeader diskHeader;
2287 afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
2288 int *skip;
2289 struct VolumeHeader tempHeader;
2290 struct afs_inode_info stuff[MAXINODETYPE];
2292 /* keeps track of special inodes that are probably 'good'; they are
2293 * referenced in the vol header, and are included in the given inodes
2294 * array */
2295 struct {
2296 int valid;
2297 Inode inode;
2298 } goodspecial[MAXINODETYPE];
2300 if (deleteMe)
2301 *deleteMe = 0;
2303 memset(goodspecial, 0, sizeof(goodspecial));
2305 skip = malloc(isp->nSpecialInodes * sizeof(*skip));
2306 if (skip) {
2307 memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
2308 } else {
2309 Log("cannot allocate memory for inode skip array when salvaging "
2310 "volume %lu; not performing duplicate special inode recovery\n",
2311 afs_printable_uint32_lu(isp->volumeId));
2312 /* still try to perform the salvage; the skip array only does anything
2313 * if we detect duplicate special inodes */
2316 init_inode_info(&tempHeader, stuff);
2319 * First, look at the special inodes and see if any are referenced by
2320 * the existing volume header. If we find duplicate special inodes, we
2321 * can use this information to use the referenced inode (it's more
2322 * likely to be the 'good' one), and throw away the duplicates.
2324 if (isp->volSummary && skip) {
2325 /* use tempHeader, so we can use the stuff[] array to easily index
2326 * into the isp->volSummary special inodes */
2327 memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
2329 for (i = 0; i < isp->nSpecialInodes; i++) {
2330 ip = &inodes[isp->index + i];
2331 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2332 /* will get taken care of in a later loop */
2333 continue;
2335 if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
2336 goodspecial[ip->u.special.type-1].valid = 1;
2337 goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
2342 memset(&tempHeader, 0, sizeof(tempHeader));
2343 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2344 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2345 tempHeader.id = isp->volumeId;
2346 tempHeader.parent = isp->RWvolumeId;
2348 /* Check for duplicates (inodes are sorted by type field) */
2349 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2350 ip = &inodes[isp->index + i];
2351 if (ip->u.special.type == (ip + 1)->u.special.type) {
2352 afs_ino_str_t stmp1, stmp2;
2354 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2355 /* Will be caught in the loop below */
2356 continue;
2358 if (!Showmode) {
2359 Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
2360 ip->u.special.type, isp->volumeId,
2361 PrintInode(stmp1, ip->inodeNumber),
2362 PrintInode(stmp2, (ip+1)->inodeNumber));
2364 if (skip && goodspecial[ip->u.special.type-1].valid) {
2365 Inode gi = goodspecial[ip->u.special.type-1].inode;
2367 if (!Showmode) {
2368 Log("using special inode referenced by vol header (%s)\n",
2369 PrintInode(stmp1, gi));
2372 /* the volume header references some special inode of
2373 * this type in the inodes array; are we it? */
2374 if (ip->inodeNumber != gi) {
2375 skip[i] = 1;
2376 } else if ((ip+1)->inodeNumber != gi) {
2377 /* in case this is the last iteration; we need to
2378 * make sure we check ip+1, too */
2379 skip[i+1] = 1;
2381 } else {
2382 if (!Showmode)
2383 Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
2384 if (skip) {
2385 free(skip);
2387 return -1;
2391 for (i = 0; i < isp->nSpecialInodes; i++) {
2392 afs_ino_str_t stmp;
2393 ip = &inodes[isp->index + i];
2394 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2395 if (check) {
2396 Log("Rubbish header inode %s of type %d\n",
2397 PrintInode(stmp, ip->inodeNumber),
2398 ip->u.special.type);
2399 if (skip) {
2400 free(skip);
2402 return -1;
2404 Log("Rubbish header inode %s of type %d; deleted\n",
2405 PrintInode(stmp, ip->inodeNumber),
2406 ip->u.special.type);
2407 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2408 if (skip && skip[i]) {
2409 if (orphans == ORPH_REMOVE) {
2410 Log("Removing orphan special inode %s of type %d\n",
2411 PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2412 continue;
2413 } else {
2414 Log("Ignoring orphan special inode %s of type %d\n",
2415 PrintInode(stmp, ip->inodeNumber), ip->u.special.type);
2416 /* fall through to the ip->linkCount--; line below */
2418 } else {
2419 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2420 allinodesobsolete = 0;
2422 if (!check && ip->u.special.type != VI_LINKTABLE)
2423 ip->linkCount--; /* Keep the inode around */
2426 if (skip) {
2427 free(skip);
2429 skip = NULL;
2431 if (allinodesobsolete) {
2432 if (deleteMe)
2433 *deleteMe = 1;
2434 return -1;
2437 if (!check)
2438 salvinfo->VGLinkH_cnt++; /* one for every header. */
2440 if (!RW && !check && isp->volSummary) {
2441 ClearROInUseBit(isp->volSummary);
2442 return 0;
2445 for (i = 0; i < MAXINODETYPE; i++) {
2446 if (stuff[i].inodeType == VI_LINKTABLE) {
2447 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2448 * And we may have recreated the link table earlier, so set the
2449 * RW header as well. The header magic was already checked.
2451 if (VALID_INO(salvinfo->VGLinkH->ih_ino)) {
2452 *stuff[i].inode = salvinfo->VGLinkH->ih_ino;
2454 continue;
2456 if (SalvageHeader(salvinfo, &stuff[i], isp, check, deleteMe) == -1 && check)
2457 return -1;
2460 if (isp->volSummary == NULL) {
2461 char path[64];
2462 char headerName[64];
2463 (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
2464 (void)afs_snprintf(path, sizeof path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, headerName);
2465 if (check) {
2466 Log("No header file for volume %u\n", isp->volumeId);
2467 return -1;
2469 if (!Showmode)
2470 Log("No header file for volume %u; %screating %s\n",
2471 isp->volumeId, (Testing ? "it would have been " : ""),
2472 path);
2473 isp->volSummary = calloc(1, sizeof(struct VolumeSummary));
2475 writefunc = VCreateVolumeDiskHeader;
2476 } else {
2477 char path[64];
2478 char headerName[64];
2479 /* hack: these two fields are obsolete... */
2480 isp->volSummary->header.volumeAcl = 0;
2481 isp->volSummary->header.volumeMountTable = 0;
2483 if (memcmp
2484 (&isp->volSummary->header, &tempHeader,
2485 sizeof(struct VolumeHeader))) {
2486 VolumeExternalName_r(isp->volumeId, headerName, sizeof(headerName));
2487 (void)afs_snprintf(path, sizeof path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, headerName);
2489 Log("Header file %s is damaged or no longer valid%s\n", path,
2490 (check ? "" : "; repairing"));
2491 if (check)
2492 return -1;
2494 writefunc = VWriteVolumeDiskHeader;
2497 if (writefunc) {
2498 memcpy(&isp->volSummary->header, &tempHeader,
2499 sizeof(struct VolumeHeader));
2500 if (Testing) {
2501 if (!Showmode)
2502 Log("It would have written a new header file for volume %u\n",
2503 isp->volumeId);
2504 } else {
2505 afs_int32 code;
2506 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2507 code = (*writefunc)(&diskHeader, salvinfo->fileSysPartition);
2508 if (code) {
2509 Log("Error %ld writing volume header file for volume %lu\n",
2510 afs_printable_int32_ld(code),
2511 afs_printable_uint32_lu(diskHeader.id));
2512 return -1;
2516 IH_INIT(isp->volSummary->volumeInfoHandle, salvinfo->fileSysDevice, isp->RWvolumeId,
2517 isp->volSummary->header.volumeInfo);
2518 return 0;
2522 SalvageHeader(struct SalvInfo *salvinfo, struct afs_inode_info *sp,
2523 struct InodeSummary *isp, int check, int *deleteMe)
2525 union {
2526 VolumeDiskData volumeInfo;
2527 struct versionStamp fileHeader;
2528 } header;
2529 IHandle_t *specH;
2530 int recreate = 0;
2531 ssize_t nBytes;
2532 FdHandle_t *fdP;
2534 if (sp->obsolete)
2535 return 0;
2536 #ifndef AFS_NAMEI_ENV
2537 if (sp->inodeType == VI_LINKTABLE)
2538 return 0; /* header magic was already checked */
2539 #endif
2540 if (*(sp->inode) == 0) {
2541 if (check) {
2542 Log("Missing inode in volume header (%s)\n", sp->description);
2543 return -1;
2545 if (!Showmode)
2546 Log("Missing inode in volume header (%s); %s\n", sp->description,
2547 (Testing ? "it would have recreated it" : "recreating"));
2548 if (!Testing) {
2549 *(sp->inode) =
2550 IH_CREATE(NULL, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, isp->volumeId,
2551 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2552 if (!VALID_INO(*(sp->inode)))
2553 Abort
2554 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2555 sp->description, errno);
2557 recreate = 1;
2560 IH_INIT(specH, salvinfo->fileSysDevice, isp->RWvolumeId, *(sp->inode));
2561 fdP = IH_OPEN(specH);
2562 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2563 /* bail out early and destroy the volume */
2564 if (!Showmode)
2565 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2566 if (deleteMe)
2567 *deleteMe = 1;
2568 IH_RELEASE(specH);
2569 return -1;
2571 if (fdP == NULL)
2572 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2573 sp->description, errno);
2575 if (!recreate
2576 && (FDH_PREAD(fdP, (char *)&header, sp->size, 0) != sp->size
2577 || header.fileHeader.magic != sp->stamp.magic)) {
2578 if (check) {
2579 Log("Part of the header (%s) is corrupted\n", sp->description);
2580 FDH_REALLYCLOSE(fdP);
2581 IH_RELEASE(specH);
2582 return -1;
2584 Log("Part of the header (%s) is corrupted; recreating\n",
2585 sp->description);
2586 recreate = 1;
2587 /* header can be garbage; make sure we don't read garbage data from
2588 * it below */
2589 memset(&header, 0, sizeof(header));
2591 #ifdef AFS_NAMEI_ENV
2592 if (namei_FixSpecialOGM(fdP, check)) {
2593 Log("Error with namei header OGM data (%s)\n", sp->description);
2594 FDH_REALLYCLOSE(fdP);
2595 IH_RELEASE(specH);
2596 return -1;
2598 #endif
2599 if (sp->inodeType == VI_VOLINFO
2600 && header.volumeInfo.destroyMe == DESTROY_ME) {
2601 if (deleteMe)
2602 *deleteMe = 1;
2603 FDH_REALLYCLOSE(fdP);
2604 IH_RELEASE(specH);
2605 return -1;
2607 if (recreate && !Testing) {
2608 if (check)
2609 Abort
2610 ("Internal error: recreating volume header (%s) in check mode\n",
2611 sp->description);
2612 nBytes = FDH_TRUNC(fdP, 0);
2613 if (nBytes == -1)
2614 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2615 sp->description, errno);
2617 /* The following code should be moved into vutil.c */
2618 if (sp->inodeType == VI_VOLINFO) {
2619 struct timeval tp;
2620 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2621 header.volumeInfo.stamp = sp->stamp;
2622 header.volumeInfo.id = isp->volumeId;
2623 header.volumeInfo.parentId = isp->RWvolumeId;
2624 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2625 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2626 isp->volumeId, isp->volumeId);
2627 header.volumeInfo.inService = 0;
2628 header.volumeInfo.blessed = 0;
2629 /* The + 1000 is a hack in case there are any files out in venus caches */
2630 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2631 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2632 header.volumeInfo.needsCallback = 0;
2633 gettimeofday(&tp, 0);
2634 header.volumeInfo.creationDate = tp.tv_sec;
2635 nBytes =
2636 FDH_PWRITE(fdP, (char *)&header.volumeInfo,
2637 sizeof(header.volumeInfo), 0);
2638 if (nBytes != sizeof(header.volumeInfo)) {
2639 if (nBytes < 0)
2640 Abort
2641 ("Unable to write volume header file (%s) (errno = %d)\n",
2642 sp->description, errno);
2643 Abort("Unable to write entire volume header file (%s)\n",
2644 sp->description);
2646 } else {
2647 nBytes = FDH_PWRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp), 0);
2648 if (nBytes != sizeof(sp->stamp)) {
2649 if (nBytes < 0)
2650 Abort
2651 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2652 sp->description, errno);
2653 Abort
2654 ("Unable to write entire version stamp in volume header file (%s)\n",
2655 sp->description);
2659 FDH_REALLYCLOSE(fdP);
2660 IH_RELEASE(specH);
2661 if (sp->inodeType == VI_VOLINFO) {
2662 salvinfo->VolInfo = header.volumeInfo;
2663 if (check) {
2664 char update[25];
2666 if (salvinfo->VolInfo.updateDate) {
2667 strcpy(update, TimeStamp(salvinfo->VolInfo.updateDate, 0));
2668 if (!Showmode)
2669 Log("%s (%u) %supdated %s\n", salvinfo->VolInfo.name,
2670 salvinfo->VolInfo.id,
2671 (Testing ? "it would have been " : ""), update);
2672 } else {
2673 strcpy(update, TimeStamp(salvinfo->VolInfo.creationDate, 0));
2674 if (!Showmode)
2675 Log("%s (%u) not updated (created %s)\n",
2676 salvinfo->VolInfo.name, salvinfo->VolInfo.id, update);
2682 return 0;
2686 SalvageVnodes(struct SalvInfo *salvinfo,
2687 struct InodeSummary *rwIsp,
2688 struct InodeSummary *thisIsp,
2689 struct ViceInodeInfo *inodes, int check)
2691 int ilarge, ismall, ioffset, RW, nInodes;
2692 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2693 if (Showmode)
2694 return 0;
2695 RW = (rwIsp == thisIsp);
2696 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2697 ismall =
2698 SalvageIndex(salvinfo, thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2699 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2700 if (check && ismall == -1)
2701 return -1;
2702 ilarge =
2703 SalvageIndex(salvinfo, thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2704 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2705 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2709 SalvageIndex(struct SalvInfo *salvinfo, Inode ino, VnodeClass class, int RW,
2710 struct ViceInodeInfo *ip, int nInodes,
2711 struct VolumeSummary *volSummary, int check)
2713 char buf[SIZEOF_LARGEDISKVNODE];
2714 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2715 int err = 0;
2716 StreamHandle_t *file;
2717 struct VnodeClassInfo *vcp;
2718 afs_sfsize_t size;
2719 afs_sfsize_t nVnodes;
2720 afs_fsize_t vnodeLength;
2721 int vnodeIndex;
2722 afs_ino_str_t stmp1, stmp2;
2723 IHandle_t *handle;
2724 FdHandle_t *fdP;
2726 IH_INIT(handle, salvinfo->fileSysDevice, volSummary->header.parent, ino);
2727 fdP = IH_OPEN(handle);
2728 osi_Assert(fdP != NULL);
2729 file = FDH_FDOPEN(fdP, "r+");
2730 osi_Assert(file != NULL);
2731 vcp = &VnodeClassInfo[class];
2732 size = OS_SIZE(fdP->fd_fd);
2733 osi_Assert(size != -1);
2734 nVnodes = (size / vcp->diskSize) - 1;
2735 if (nVnodes > 0) {
2736 osi_Assert((nVnodes + 1) * vcp->diskSize == size);
2737 osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
2738 } else {
2739 nVnodes = 0;
2741 for (vnodeIndex = 0;
2742 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2743 nVnodes--, vnodeIndex++) {
2744 if (vnode->type != vNull) {
2745 int vnodeChanged = 0;
2746 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2747 /* Log programs that belong to root (potentially suid root);
2748 * don't bother for read-only or backup volumes */
2749 #ifdef notdef /* This is done elsewhere */
2750 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2751 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n", salvinfo->VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author, vnode->owner, vnode->modeBits);
2752 #endif
2753 if (VNDISK_GET_INO(vnode) == 0) {
2754 if (RW) {
2755 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2756 memset(vnode, 0, vcp->diskSize);
2757 vnodeChanged = 1;
2759 } else {
2760 if (vcp->magic != vnode->vnodeMagic) {
2761 /* bad magic #, probably partially created vnode */
2762 if (check) {
2763 Log("Partially allocated vnode %d: bad magic (is %lx should be %lx)\n",
2764 vnodeNumber, afs_printable_uint32_lu(vnode->vnodeMagic),
2765 afs_printable_uint32_lu(vcp->magic));
2766 memset(vnode, 0, vcp->diskSize);
2767 err = -1;
2768 goto zooks;
2770 Log("Partially allocated vnode %d deleted.\n",
2771 vnodeNumber);
2772 memset(vnode, 0, vcp->diskSize);
2773 vnodeChanged = 1;
2774 goto vnodeDone;
2776 /* ****** Should do a bit more salvage here: e.g. make sure
2777 * vnode type matches what it should be given the index */
2778 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2779 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2780 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2781 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2784 ip++;
2785 nInodes--;
2787 if (!RW) {
2788 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2789 /* The following doesn't work, because the version number
2790 * is not maintained correctly by the file server */
2791 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2792 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2793 * break; */
2794 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2795 break;
2796 ip++;
2797 nInodes--;
2799 } else {
2800 /* For RW volume, look for vnode with matching inode number;
2801 * if no such match, take the first determined by our sort
2802 * order */
2803 struct ViceInodeInfo *lip = ip;
2804 int lnInodes = nInodes;
2805 while (lnInodes
2806 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2807 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2808 ip = lip;
2809 nInodes = lnInodes;
2810 break;
2812 lip++;
2813 lnInodes--;
2816 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2817 /* "Matching" inode */
2818 if (RW) {
2819 Unique vu, iu;
2820 FileVersion vd, id;
2821 vu = vnode->uniquifier;
2822 iu = ip->u.vnode.vnodeUniquifier;
2823 vd = vnode->dataVersion;
2824 id = ip->u.vnode.inodeDataVersion;
2826 * Because of the possibility of the uniquifier overflows (> 4M)
2827 * we compare them modulo the low 22-bits; we shouldn't worry
2828 * about mismatching since they shouldn't to many old
2829 * uniquifiers of the same vnode...
2831 if (IUnique(vu) != IUnique(iu)) {
2832 if (!Showmode) {
2833 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2836 vnode->uniquifier = iu;
2837 #ifdef AFS_3DISPARES
2838 vnode->dataVersion = (id >= vd ?
2839 /* 90% of 2.1M */
2840 ((id - vd) >
2841 1887437 ? vd : id) :
2842 /* 90% of 2.1M */
2843 ((vd - id) >
2844 1887437 ? id : vd));
2845 #else
2846 #if defined(AFS_SGI_EXMAG)
2847 vnode->dataVersion = (id >= vd ?
2848 /* 90% of 16M */
2849 ((id - vd) >
2850 15099494 ? vd : id) :
2851 /* 90% of 16M */
2852 ((vd - id) >
2853 15099494 ? id : vd));
2854 #else
2855 vnode->dataVersion = (id > vd ? id : vd);
2856 #endif /* AFS_SGI_EXMAG */
2857 #endif /* AFS_3DISPARES */
2858 vnodeChanged = 1;
2859 } else {
2860 /* don't bother checking for vd > id any more, since
2861 * partial file transfers always result in this state,
2862 * and you can't do much else anyway (you've already
2863 * found the best data you can) */
2864 #ifdef AFS_3DISPARES
2865 if (!vnodeIsDirectory(vnodeNumber)
2866 && ((vd < id && (id - vd) < 1887437)
2867 || ((vd > id && (vd - id) > 1887437)))) {
2868 #else
2869 #if defined(AFS_SGI_EXMAG)
2870 if (!vnodeIsDirectory(vnodeNumber)
2871 && ((vd < id && (id - vd) < 15099494)
2872 || ((vd > id && (vd - id) > 15099494)))) {
2873 #else
2874 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2875 #endif /* AFS_SGI_EXMAG */
2876 #endif
2877 if (!Showmode)
2878 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2879 vnode->dataVersion = id;
2880 vnodeChanged = 1;
2884 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2885 if (check) {
2886 if (!Showmode) {
2887 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);
2889 VNDISK_SET_INO(vnode, ip->inodeNumber);
2890 err = -1;
2891 goto zooks;
2893 if (!Showmode) {
2894 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);
2896 VNDISK_SET_INO(vnode, ip->inodeNumber);
2897 vnodeChanged = 1;
2899 VNDISK_GET_LEN(vnodeLength, vnode);
2900 if (ip->byteCount != vnodeLength) {
2901 if (check) {
2902 if (!Showmode)
2903 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2904 err = -1;
2905 goto zooks;
2907 if (!Showmode)
2908 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2909 VNDISK_SET_LEN(vnode, ip->byteCount);
2910 vnodeChanged = 1;
2912 if (!check)
2913 ip->linkCount--; /* Keep the inode around */
2914 ip++;
2915 nInodes--;
2916 } else { /* no matching inode */
2917 afs_ino_str_t stmp;
2918 if (VNDISK_GET_INO(vnode) != 0
2919 || vnode->type == vDirectory) {
2920 /* No matching inode--get rid of the vnode */
2921 if (check) {
2922 if (VNDISK_GET_INO(vnode)) {
2923 if (!Showmode) {
2924 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)));
2926 } else {
2927 if (!Showmode)
2928 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2930 err = -1;
2931 goto zooks;
2933 if (VNDISK_GET_INO(vnode)) {
2934 if (!Showmode) {
2935 time_t serverModifyTime = vnode->serverModifyTime;
2936 Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(stmp, VNDISK_GET_INO(vnode)), ctime(&serverModifyTime));
2938 } else {
2939 if (!Showmode) {
2940 time_t serverModifyTime = vnode->serverModifyTime;
2941 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime(&serverModifyTime));
2944 memset(vnode, 0, vcp->diskSize);
2945 vnodeChanged = 1;
2946 } else {
2947 /* Should not reach here becuase we checked for
2948 * (inodeNumber == 0) above. And where we zero the vnode,
2949 * we also goto vnodeDone.
2953 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2954 ip++;
2955 nInodes--;
2957 } /* VNDISK_GET_INO(vnode) != 0 */
2958 vnodeDone:
2959 osi_Assert(!(vnodeChanged && check));
2960 if (vnodeChanged && !Testing) {
2961 osi_Assert(IH_IWRITE
2962 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2963 (char *)vnode, vcp->diskSize)
2964 == vcp->diskSize);
2965 salvinfo->VolumeChanged = 1; /* For break call back */
2969 zooks:
2970 STREAM_CLOSE(file);
2971 FDH_CLOSE(fdP);
2972 IH_RELEASE(handle);
2973 return err;
2976 struct VnodeEssence *
2977 CheckVnodeNumber(struct SalvInfo *salvinfo, VnodeId vnodeNumber)
2979 VnodeClass class;
2980 struct VnodeInfo *vip;
2981 int offset;
2983 class = vnodeIdToClass(vnodeNumber);
2984 vip = &salvinfo->vnodeInfo[class];
2985 offset = vnodeIdToBitNumber(vnodeNumber);
2986 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2989 void
2990 CopyOnWrite(struct SalvInfo *salvinfo, struct DirSummary *dir)
2992 /* Copy the directory unconditionally if we are going to change it:
2993 * not just if was cloned.
2995 struct VnodeDiskObject vnode;
2996 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2997 Inode oldinode, newinode;
2998 afs_sfsize_t code;
3000 if (dir->copied || Testing)
3001 return;
3002 DFlush(); /* Well justified paranoia... */
3004 code =
3005 IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
3006 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
3007 sizeof(vnode));
3008 osi_Assert(code == sizeof(vnode));
3009 oldinode = VNDISK_GET_INO(&vnode);
3010 /* Increment the version number by a whole lot to avoid problems with
3011 * clients that were promised new version numbers--but the file server
3012 * crashed before the versions were written to disk.
3014 newinode =
3015 IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
3016 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
3017 200);
3018 osi_Assert(VALID_INO(newinode));
3019 osi_Assert(CopyInode(salvinfo->fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
3020 vnode.cloned = 0;
3021 VNDISK_SET_INO(&vnode, newinode);
3022 code =
3023 IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
3024 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
3025 sizeof(vnode));
3026 osi_Assert(code == sizeof(vnode));
3028 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
3029 salvinfo->fileSysDevice, newinode,
3030 &salvinfo->VolumeChanged);
3031 /* Don't delete the original inode right away, because the directory is
3032 * still being scanned.
3034 dir->copied = 1;
3038 * This function should either successfully create a new dir, or give up
3039 * and leave things the way they were. In particular, if it fails to write
3040 * the new dir properly, it should return w/o changing the reference to the
3041 * old dir.
3043 void
3044 CopyAndSalvage(struct SalvInfo *salvinfo, struct DirSummary *dir)
3046 struct VnodeDiskObject vnode;
3047 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
3048 Inode oldinode, newinode;
3049 DirHandle newdir;
3050 FdHandle_t *fdP;
3051 afs_int32 code;
3052 afs_sfsize_t lcode;
3053 afs_int32 parentUnique = 1;
3054 struct VnodeEssence *vnodeEssence;
3055 afs_fsize_t length;
3057 if (Testing)
3058 return;
3059 Log("Salvaging directory %u...\n", dir->vnodeNumber);
3060 lcode =
3061 IH_IREAD(salvinfo->vnodeInfo[vLarge].handle,
3062 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
3063 sizeof(vnode));
3064 osi_Assert(lcode == sizeof(vnode));
3065 oldinode = VNDISK_GET_INO(&vnode);
3066 /* Increment the version number by a whole lot to avoid problems with
3067 * clients that were promised new version numbers--but the file server
3068 * crashed before the versions were written to disk.
3070 newinode =
3071 IH_CREATE(dir->ds_linkH, salvinfo->fileSysDevice, salvinfo->fileSysPath, 0, dir->rwVid,
3072 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
3073 200);
3074 osi_Assert(VALID_INO(newinode));
3075 SetSalvageDirHandle(&newdir, dir->rwVid, salvinfo->fileSysDevice, newinode,
3076 &salvinfo->VolumeChanged);
3078 /* Assign . and .. vnode numbers from dir and vnode.parent.
3079 * The uniquifier for . is in the vnode.
3080 * The uniquifier for .. might be set to a bogus value of 1 and
3081 * the salvager will later clean it up.
3083 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(salvinfo, vnode.parent))) {
3084 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
3086 code =
3087 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
3088 vnode.uniquifier,
3089 (vnode.parent ? vnode.parent : dir->vnodeNumber),
3090 parentUnique);
3091 if (code == 0)
3092 code = DFlush();
3093 if (code) {
3094 /* didn't really build the new directory properly, let's just give up. */
3095 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
3096 Log("Directory salvage returned code %d, continuing.\n", code);
3097 if (code) {
3098 Log("also failed to decrement link count on new inode");
3100 osi_Assert(1 == 2);
3102 Log("Checking the results of the directory salvage...\n");
3103 if (!DirOK(&newdir)) {
3104 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
3105 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
3106 osi_Assert(code == 0);
3107 osi_Assert(1 == 2);
3109 vnode.cloned = 0;
3110 VNDISK_SET_INO(&vnode, newinode);
3111 length = Length(&newdir);
3112 VNDISK_SET_LEN(&vnode, length);
3113 lcode =
3114 IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
3115 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
3116 sizeof(vnode));
3117 osi_Assert(lcode == sizeof(vnode));
3118 IH_CONDSYNC(salvinfo->vnodeInfo[vLarge].handle);
3120 /* make sure old directory file is really closed */
3121 fdP = IH_OPEN(dir->dirHandle.dirh_handle);
3122 FDH_REALLYCLOSE(fdP);
3124 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
3125 osi_Assert(code == 0);
3126 dir->dirHandle = newdir;
3130 * arguments for JudgeEntry.
3132 struct judgeEntry_params {
3133 struct DirSummary *dir; /**< directory we're examining entries in */
3134 struct SalvInfo *salvinfo; /**< SalvInfo for the current salvage job */
3138 JudgeEntry(void *arock, char *name, afs_int32 vnodeNumber,
3139 afs_int32 unique)
3141 struct judgeEntry_params *params = arock;
3142 struct DirSummary *dir = params->dir;
3143 struct SalvInfo *salvinfo = params->salvinfo;
3144 struct VnodeEssence *vnodeEssence;
3145 afs_int32 dirOrphaned, todelete;
3147 dirOrphaned = IsVnodeOrphaned(salvinfo, dir->vnodeNumber);
3149 vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3150 if (vnodeEssence == NULL) {
3151 if (!Showmode) {
3152 Log("dir vnode %u: invalid entry deleted: %s" OS_DIRSEP "%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
3154 if (!Testing) {
3155 CopyOnWrite(salvinfo, dir);
3156 osi_Assert(Delete(&dir->dirHandle, name) == 0);
3158 return 0;
3160 #ifdef AFS_AIX_ENV
3161 #ifndef AFS_NAMEI_ENV
3162 /* On AIX machines, don't allow entries to point to inode 0. That is a special
3163 * mount inode for the partition. If this inode were deleted, it would crash
3164 * the machine.
3166 if (vnodeEssence->InodeNumber == 0) {
3167 Log("dir vnode %d: invalid entry: %s" OS_DIRSEP "%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
3168 if (!Testing) {
3169 CopyOnWrite(salvinfo, dir);
3170 osi_Assert(Delete(&dir->dirHandle, name) == 0);
3172 return 0;
3174 #endif
3175 #endif
3177 if (!(vnodeNumber & 1) && !Showmode
3178 && !(vnodeEssence->count || vnodeEssence->unique
3179 || vnodeEssence->modeBits)) {
3180 Log("dir vnode %u: invalid entry: %s" OS_DIRSEP "%s (vnode %u, unique %u)%s\n",
3181 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
3182 vnodeNumber, unique,
3183 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
3184 ""));
3185 if (!unique) {
3186 if (!Testing) {
3187 CopyOnWrite(salvinfo, dir);
3188 osi_Assert(Delete(&dir->dirHandle, name) == 0);
3190 return 0;
3194 /* Check if the Uniquifiers match. If not, change the directory entry
3195 * so its unique matches the vnode unique. Delete if the unique is zero
3196 * or if the directory is orphaned.
3198 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
3199 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
3201 if (todelete
3202 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
3203 if (dirOrphaned) {
3204 /* This is an orphaned directory. Don't delete the . or ..
3205 * entry. Otherwise, it will get created in the next
3206 * salvage and deleted again here. So Just skip it.
3207 * */
3208 return 0;
3210 /* (vnodeEssence->unique == 0 && ('.' || '..'));
3211 * Entries arriving here should be deleted, but the directory
3212 * is not orphaned. Therefore, the entry must be pointing at
3213 * the wrong vnode. Skip the 'else' clause and fall through;
3214 * the code below will repair the entry so it correctly points
3215 * at the vnode of the current directory (if '.') or the parent
3216 * directory (if '..'). */
3217 } else {
3218 if (!Showmode) {
3219 Log("dir vnode %u: %s" OS_DIRSEP "%s (vnode %u): unique changed from %u to %u %s\n",
3220 dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique,
3221 vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
3223 if (!Testing) {
3224 AFSFid fid;
3225 fid.Vnode = vnodeNumber;
3226 fid.Unique = vnodeEssence->unique;
3227 CopyOnWrite(salvinfo, dir);
3228 osi_Assert(Delete(&dir->dirHandle, name) == 0);
3229 if (!todelete)
3230 osi_Assert(Create(&dir->dirHandle, name, &fid) == 0);
3232 if (todelete)
3233 return 0; /* no need to continue */
3237 if (strcmp(name, ".") == 0) {
3238 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
3239 AFSFid fid;
3240 if (!Showmode)
3241 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3242 if (!Testing) {
3243 CopyOnWrite(salvinfo, dir);
3244 osi_Assert(Delete(&dir->dirHandle, ".") == 0);
3245 fid.Vnode = dir->vnodeNumber;
3246 fid.Unique = dir->unique;
3247 osi_Assert(Create(&dir->dirHandle, ".", &fid) == 0);
3250 vnodeNumber = fid.Vnode; /* Get the new Essence */
3251 unique = fid.Unique;
3252 vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3254 dir->haveDot = 1;
3255 } else if (strcmp(name, "..") == 0) {
3256 AFSFid pa;
3257 if (dir->parent) {
3258 struct VnodeEssence *dotdot;
3259 pa.Vnode = dir->parent;
3260 dotdot = CheckVnodeNumber(salvinfo, pa.Vnode);
3261 osi_Assert(dotdot != NULL); /* XXX Should not be assert */
3262 pa.Unique = dotdot->unique;
3263 } else {
3264 pa.Vnode = dir->vnodeNumber;
3265 pa.Unique = dir->unique;
3267 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
3268 if (!Showmode)
3269 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
3270 if (!Testing) {
3271 CopyOnWrite(salvinfo, dir);
3272 osi_Assert(Delete(&dir->dirHandle, "..") == 0);
3273 osi_Assert(Create(&dir->dirHandle, "..", &pa) == 0);
3276 vnodeNumber = pa.Vnode; /* Get the new Essence */
3277 unique = pa.Unique;
3278 vnodeEssence = CheckVnodeNumber(salvinfo, vnodeNumber);
3280 dir->haveDotDot = 1;
3281 } else if (strncmp(name, ".__afs", 6) == 0) {
3282 if (!Showmode) {
3283 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);
3285 if (!Testing) {
3286 CopyOnWrite(salvinfo, dir);
3287 osi_Assert(Delete(&dir->dirHandle, name) == 0);
3289 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
3290 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
3291 return 0;
3292 } else {
3293 if (ShowSuid && (vnodeEssence->modeBits & 06000))
3294 Log("FOUND suid/sgid file: %s" OS_DIRSEP "%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);
3295 if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
3296 && !(vnodeEssence->modeBits & 0111)) {
3297 afs_sfsize_t nBytes;
3298 afs_sfsize_t size;
3299 char buf[1025];
3300 IHandle_t *ihP;
3301 FdHandle_t *fdP;
3303 IH_INIT(ihP, salvinfo->fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3304 vnodeEssence->InodeNumber);
3305 fdP = IH_OPEN(ihP);
3306 if (fdP == NULL) {
3307 Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
3308 IH_RELEASE(ihP);
3309 return 0;
3311 size = FDH_SIZE(fdP);
3312 if (size < 0) {
3313 Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, (int)size, vnodeNumber);
3314 FDH_REALLYCLOSE(fdP);
3315 IH_RELEASE(ihP);
3316 return 0;
3319 if (size > 1024)
3320 size = 1024;
3321 nBytes = FDH_PREAD(fdP, buf, size, 0);
3322 if (nBytes == size) {
3323 buf[size] = '\0';
3324 if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
3325 Log("Volume %u (%s) mount point %s" OS_DIRSEP "%s to '%s' invalid, %s to symbolic link\n",
3326 dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
3327 Testing ? "would convert" : "converted");
3328 vnodeEssence->modeBits |= 0111;
3329 vnodeEssence->changed = 1;
3330 } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s" OS_DIRSEP "%s to '%s'\n",
3331 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3332 dir->name ? dir->name : "??", name, buf);
3333 } else {
3334 Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
3335 dir->vname, vnodeNumber, (int)size, (int)nBytes);
3337 FDH_REALLYCLOSE(fdP);
3338 IH_RELEASE(ihP);
3340 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3341 Log("FOUND root file: %s" OS_DIRSEP "%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);
3342 if (vnodeIdToClass(vnodeNumber) == vLarge
3343 && vnodeEssence->name == NULL) {
3344 char *n;
3345 if ((n = (char *)malloc(strlen(name) + 1)))
3346 strcpy(n, name);
3347 vnodeEssence->name = n;
3350 /* The directory entry points to the vnode. Check to see if the
3351 * vnode points back to the directory. If not, then let the
3352 * directory claim it (else it might end up orphaned). Vnodes
3353 * already claimed by another directory are deleted from this
3354 * directory: hardlinks to the same vnode are not allowed
3355 * from different directories.
3357 if (vnodeEssence->parent != dir->vnodeNumber) {
3358 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3359 /* Vnode does not point back to this directory.
3360 * Orphaned dirs cannot claim a file (it may belong to
3361 * another non-orphaned dir).
3363 if (!Showmode) {
3364 Log("dir vnode %u: %s" OS_DIRSEP "%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);
3366 vnodeEssence->parent = dir->vnodeNumber;
3367 vnodeEssence->changed = 1;
3368 } else {
3369 /* Vnode was claimed by another directory */
3370 if (!Showmode) {
3371 if (dirOrphaned) {
3372 Log("dir vnode %u: %s" OS_DIRSEP "%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 " : ""));
3373 } else if (vnodeNumber == 1) {
3374 Log("dir vnode %d: %s" OS_DIRSEP "%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""));
3375 } else {
3376 Log("dir vnode %u: %s" OS_DIRSEP "%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 " : ""));
3379 if (!Testing) {
3380 CopyOnWrite(salvinfo, dir);
3381 osi_Assert(Delete(&dir->dirHandle, name) == 0);
3383 return 0;
3386 /* This directory claims the vnode */
3387 vnodeEssence->claimed = 1;
3389 vnodeEssence->count--;
3390 return 0;
3393 void
3394 DistilVnodeEssence(struct SalvInfo *salvinfo, VolumeId rwVId,
3395 VnodeClass class, Inode ino, Unique * maxu)
3397 struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
3398 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3399 char buf[SIZEOF_LARGEDISKVNODE];
3400 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3401 afs_sfsize_t size;
3402 StreamHandle_t *file;
3403 int vnodeIndex;
3404 int nVnodes;
3405 FdHandle_t *fdP;
3407 IH_INIT(vip->handle, salvinfo->fileSysDevice, rwVId, ino);
3408 fdP = IH_OPEN(vip->handle);
3409 osi_Assert(fdP != NULL);
3410 file = FDH_FDOPEN(fdP, "r+");
3411 osi_Assert(file != NULL);
3412 size = OS_SIZE(fdP->fd_fd);
3413 osi_Assert(size != -1);
3414 vip->nVnodes = (size / vcp->diskSize) - 1;
3415 if (vip->nVnodes > 0) {
3416 osi_Assert((vip->nVnodes + 1) * vcp->diskSize == size);
3417 osi_Assert(STREAM_ASEEK(file, vcp->diskSize) == 0);
3418 osi_Assert((vip->vnodes = (struct VnodeEssence *)
3419 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3420 if (class == vLarge) {
3421 osi_Assert((vip->inodes = (Inode *)
3422 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3423 } else {
3424 vip->inodes = NULL;
3426 } else {
3427 vip->nVnodes = 0;
3428 vip->vnodes = NULL;
3429 vip->inodes = NULL;
3431 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3432 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3433 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3434 nVnodes--, vnodeIndex++) {
3435 if (vnode->type != vNull) {
3436 struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3437 afs_fsize_t vnodeLength;
3438 vip->nAllocatedVnodes++;
3439 vep->count = vnode->linkCount;
3440 VNDISK_GET_LEN(vnodeLength, vnode);
3441 vep->blockCount = nBlocks(vnodeLength);
3442 vip->volumeBlockCount += vep->blockCount;
3443 vep->parent = vnode->parent;
3444 vep->unique = vnode->uniquifier;
3445 if (*maxu < vnode->uniquifier)
3446 *maxu = vnode->uniquifier;
3447 vep->modeBits = vnode->modeBits;
3448 vep->InodeNumber = VNDISK_GET_INO(vnode);
3449 vep->type = vnode->type;
3450 vep->author = vnode->author;
3451 vep->owner = vnode->owner;
3452 vep->group = vnode->group;
3453 if (vnode->type == vDirectory) {
3454 if (class != vLarge) {
3455 VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
3456 vip->nAllocatedVnodes--;
3457 memset(vnode, 0, sizeof(*vnode));
3458 IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3459 vnodeIndexOffset(vcp, vnodeNumber),
3460 (char *)&vnode, sizeof(vnode));
3461 salvinfo->VolumeChanged = 1;
3462 } else
3463 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3467 STREAM_CLOSE(file);
3468 FDH_CLOSE(fdP);
3471 static char *
3472 GetDirName(struct SalvInfo *salvinfo, VnodeId vnode, struct VnodeEssence *vp,
3473 char *path)
3475 struct VnodeEssence *parentvp;
3477 if (vnode == 1) {
3478 strcpy(path, ".");
3479 return path;
3481 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(salvinfo, vp->parent))
3482 && GetDirName(salvinfo, vp->parent, parentvp, path)) {
3483 strcat(path, OS_DIRSEP);
3484 strcat(path, vp->name);
3485 return path;
3487 return 0;
3490 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3491 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3493 static int
3494 IsVnodeOrphaned(struct SalvInfo *salvinfo, VnodeId vnode)
3496 struct VnodeEssence *vep;
3498 if (vnode == 0)
3499 return (1); /* Vnode zero does not exist */
3500 if (vnode == 1)
3501 return (0); /* The root dir vnode is always claimed */
3502 vep = CheckVnodeNumber(salvinfo, vnode); /* Get the vnode essence */
3503 if (!vep || !vep->claimed)
3504 return (1); /* Vnode is not claimed - it is orphaned */
3506 return (IsVnodeOrphaned(salvinfo, vep->parent));
3509 void
3510 SalvageDir(struct SalvInfo *salvinfo, char *name, VolumeId rwVid,
3511 struct VnodeInfo *dirVnodeInfo, IHandle_t * alinkH, int i,
3512 struct DirSummary *rootdir, int *rootdirfound)
3514 static struct DirSummary dir;
3515 static struct DirHandle dirHandle;
3516 struct VnodeEssence *parent;
3517 static char path[MAXPATHLEN];
3518 int dirok, code;
3520 if (dirVnodeInfo->vnodes[i].salvaged)
3521 return; /* already salvaged */
3523 dir.rwVid = rwVid;
3524 dirVnodeInfo->vnodes[i].salvaged = 1;
3526 if (dirVnodeInfo->inodes[i] == 0)
3527 return; /* Not allocated to a directory */
3529 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3530 if (dirVnodeInfo->vnodes[i].parent) {
3531 Log("Bad parent, vnode 1; %s...\n",
3532 (Testing ? "skipping" : "salvaging"));
3533 dirVnodeInfo->vnodes[i].parent = 0;
3534 dirVnodeInfo->vnodes[i].changed = 1;
3536 } else {
3537 parent = CheckVnodeNumber(salvinfo, dirVnodeInfo->vnodes[i].parent);
3538 if (parent && parent->salvaged == 0)
3539 SalvageDir(salvinfo, name, rwVid, dirVnodeInfo, alinkH,
3540 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3541 rootdir, rootdirfound);
3544 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3545 dir.unique = dirVnodeInfo->vnodes[i].unique;
3546 dir.copied = 0;
3547 dir.vname = name;
3548 dir.parent = dirVnodeInfo->vnodes[i].parent;
3549 dir.haveDot = dir.haveDotDot = 0;
3550 dir.ds_linkH = alinkH;
3551 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, salvinfo->fileSysDevice,
3552 dirVnodeInfo->inodes[i], &salvinfo->VolumeChanged);
3554 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3555 if (!dirok) {
3556 if (!RebuildDirs) {
3557 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3558 (Testing ? "skipping" : "salvaging"));
3560 if (!Testing) {
3561 CopyAndSalvage(salvinfo, &dir);
3562 dirok = 1;
3563 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3566 dirHandle = dir.dirHandle;
3568 dir.name =
3569 GetDirName(salvinfo, bitNumberToVnodeNumber(i, vLarge),
3570 &dirVnodeInfo->vnodes[i], path);
3572 if (dirok) {
3573 /* If enumeration failed for random reasons, we will probably delete
3574 * too much stuff, so we guard against this instead.
3576 struct judgeEntry_params judge_params;
3577 judge_params.salvinfo = salvinfo;
3578 judge_params.dir = &dir;
3580 osi_Assert(EnumerateDir(&dirHandle, JudgeEntry, &judge_params) == 0);
3583 /* Delete the old directory if it was copied in order to salvage.
3584 * CopyOnWrite has written the new inode # to the disk, but we still
3585 * have the old one in our local structure here. Thus, we idec the
3586 * local dude.
3588 DFlush();
3589 if (dir.copied && !Testing) {
3590 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3591 osi_Assert(code == 0);
3592 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3595 /* Remember rootdir DirSummary _after_ it has been judged */
3596 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3597 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3598 *rootdirfound = 1;
3601 return;
3605 * Get a new FID that can be used to create a new file.
3607 * @param[in] volHeader vol header for the volume
3608 * @param[in] class what type of vnode we'll be creating (vLarge or vSmall)
3609 * @param[out] afid the FID that we can use (only Vnode and Unique are set)
3610 * @param[inout] maxunique max uniquifier for all vnodes in the volume;
3611 * updated to the new max unique if we create a new
3612 * vnode
3614 static void
3615 GetNewFID(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3616 VnodeClass class, AFSFid *afid, Unique *maxunique)
3618 int i;
3619 for (i = 0; i < salvinfo->vnodeInfo[class].nVnodes; i++) {
3620 if (salvinfo->vnodeInfo[class].vnodes[i].type == vNull) {
3621 break;
3624 if (i == salvinfo->vnodeInfo[class].nVnodes) {
3625 /* no free vnodes; make a new one */
3626 salvinfo->vnodeInfo[class].nVnodes++;
3627 salvinfo->vnodeInfo[class].vnodes =
3628 realloc(salvinfo->vnodeInfo[class].vnodes,
3629 sizeof(struct VnodeEssence) * (i+1));
3631 salvinfo->vnodeInfo[class].vnodes[i].type = vNull;
3634 afid->Vnode = bitNumberToVnodeNumber(i, class);
3636 if (volHeader->uniquifier < (*maxunique + 1)) {
3637 /* header uniq is bad; it will get bumped by 2000 later */
3638 afid->Unique = *maxunique + 1 + 2000;
3639 (*maxunique)++;
3640 } else {
3641 /* header uniq seems okay; just use that */
3642 afid->Unique = *maxunique = volHeader->uniquifier++;
3647 * Create a vnode for a README file explaining not to use a recreated-root vol.
3649 * @param[in] volHeader vol header for the volume
3650 * @param[in] alinkH ihandle for i/o for the volume
3651 * @param[in] vid volume id
3652 * @param[inout] maxunique max uniquifier for all vnodes in the volume;
3653 * updated to the new max unique if we create a new
3654 * vnode
3655 * @param[out] afid FID for the new readme vnode
3656 * @param[out] ainode the inode for the new readme file
3658 * @return operation status
3659 * @retval 0 success
3660 * @retval -1 error
3662 static int
3663 CreateReadme(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3664 IHandle_t *alinkH, VolumeId vid, Unique *maxunique, AFSFid *afid,
3665 Inode *ainode)
3667 Inode readmeinode;
3668 struct VnodeDiskObject *rvnode = NULL;
3669 afs_sfsize_t bytes;
3670 IHandle_t *readmeH = NULL;
3671 struct VnodeEssence *vep;
3672 afs_fsize_t length;
3673 time_t now = time(NULL);
3675 /* Try to make the note brief, but informative. Only administrators should
3676 * be able to read this file at first, so we can hopefully assume they
3677 * know what AFS is, what a volume is, etc. */
3678 char readme[] =
3679 "This volume has been salvaged, but has lost its original root directory.\n"
3680 "The root directory that exists now has been recreated from orphan files\n"
3681 "from the rest of the volume. This recreated root directory may interfere\n"
3682 "with old cached data on clients, and there is no way the salvager can\n"
3683 "reasonably prevent that. So, it is recommended that you do not continue to\n"
3684 "use this volume, but only copy the salvaged data to a new volume.\n"
3685 "Continuing to use this volume as it exists now may cause some clients to\n"
3686 "behave oddly when accessing this volume.\n"
3687 "\n\t -- Your friendly neighborhood OpenAFS salvager\n";
3688 /* ^ the person reading this probably just lost some data, so they could
3689 * use some cheering up. */
3691 /* -1 for the trailing NUL */
3692 length = sizeof(readme) - 1;
3694 GetNewFID(salvinfo, volHeader, vSmall, afid, maxunique);
3696 vep = &salvinfo->vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
3698 /* create the inode and write the contents */
3699 readmeinode = IH_CREATE(alinkH, salvinfo->fileSysDevice,
3700 salvinfo->fileSysPath, 0, vid,
3701 afid->Vnode, afid->Unique, 1);
3702 if (!VALID_INO(readmeinode)) {
3703 Log("CreateReadme: readme IH_CREATE failed\n");
3704 goto error;
3707 IH_INIT(readmeH, salvinfo->fileSysDevice, vid, readmeinode);
3708 bytes = IH_IWRITE(readmeH, 0, readme, length);
3709 IH_RELEASE(readmeH);
3711 if (bytes != length) {
3712 Log("CreateReadme: IWRITE failed (%d/%d)\n", (int)bytes,
3713 (int)sizeof(readme));
3714 goto error;
3717 /* create the vnode and write it out */
3718 rvnode = calloc(1, SIZEOF_SMALLDISKVNODE);
3719 if (!rvnode) {
3720 Log("CreateRootDir: error alloc'ing memory\n");
3721 goto error;
3724 rvnode->type = vFile;
3725 rvnode->cloned = 0;
3726 rvnode->modeBits = 0777;
3727 rvnode->linkCount = 1;
3728 VNDISK_SET_LEN(rvnode, length);
3729 rvnode->uniquifier = afid->Unique;
3730 rvnode->dataVersion = 1;
3731 VNDISK_SET_INO(rvnode, readmeinode);
3732 rvnode->unixModifyTime = rvnode->serverModifyTime = now;
3733 rvnode->author = 0;
3734 rvnode->owner = 0;
3735 rvnode->parent = 1;
3736 rvnode->group = 0;
3737 rvnode->vnodeMagic = VnodeClassInfo[vSmall].magic;
3739 bytes = IH_IWRITE(salvinfo->vnodeInfo[vSmall].handle,
3740 vnodeIndexOffset(&VnodeClassInfo[vSmall], afid->Vnode),
3741 (char*)rvnode, SIZEOF_SMALLDISKVNODE);
3743 if (bytes != SIZEOF_SMALLDISKVNODE) {
3744 Log("CreateReadme: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3745 (int)SIZEOF_SMALLDISKVNODE);
3746 goto error;
3749 /* update VnodeEssence for new readme vnode */
3750 salvinfo->vnodeInfo[vSmall].nAllocatedVnodes++;
3751 vep->count = 0;
3752 vep->blockCount = nBlocks(length);
3753 salvinfo->vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
3754 vep->parent = rvnode->parent;
3755 vep->unique = rvnode->uniquifier;
3756 vep->modeBits = rvnode->modeBits;
3757 vep->InodeNumber = VNDISK_GET_INO(rvnode);
3758 vep->type = rvnode->type;
3759 vep->author = rvnode->author;
3760 vep->owner = rvnode->owner;
3761 vep->group = rvnode->group;
3763 free(rvnode);
3764 rvnode = NULL;
3766 vep->claimed = 1;
3767 vep->changed = 0;
3768 vep->salvaged = 1;
3769 vep->todelete = 0;
3771 *ainode = readmeinode;
3773 return 0;
3775 error:
3776 if (IH_DEC(alinkH, readmeinode, vid)) {
3777 Log("CreateReadme (recovery): IH_DEC failed\n");
3780 if (rvnode) {
3781 free(rvnode);
3782 rvnode = NULL;
3785 return -1;
3789 * create a root dir for a volume that lacks one.
3791 * @param[in] volHeader vol header for the volume
3792 * @param[in] alinkH ihandle for disk access for this volume group
3793 * @param[in] vid volume id we're dealing with
3794 * @param[out] rootdir populated with info about the new root dir
3795 * @param[inout] maxunique max uniquifier for all vnodes in the volume;
3796 * updated to the new max unique if we create a new
3797 * vnode
3799 * @return operation status
3800 * @retval 0 success
3801 * @retval -1 error
3803 static int
3804 CreateRootDir(struct SalvInfo *salvinfo, VolumeDiskData *volHeader,
3805 IHandle_t *alinkH, VolumeId vid, struct DirSummary *rootdir,
3806 Unique *maxunique)
3808 FileVersion dv;
3809 int decroot = 0, decreadme = 0;
3810 AFSFid did, readmeid;
3811 afs_fsize_t length;
3812 Inode rootinode;
3813 struct VnodeDiskObject *rootvnode = NULL;
3814 struct acl_accessList *ACL;
3815 Inode *ip;
3816 afs_sfsize_t bytes;
3817 struct VnodeEssence *vep;
3818 Inode readmeinode = 0;
3819 time_t now = time(NULL);
3821 if (!salvinfo->vnodeInfo[vLarge].vnodes && !salvinfo->vnodeInfo[vSmall].vnodes) {
3822 Log("Not creating new root dir; volume appears to lack any vnodes\n");
3823 goto error;
3826 if (!salvinfo->vnodeInfo[vLarge].vnodes) {
3827 /* We don't have any large vnodes in the volume; allocate room
3828 * for one so we can recreate the root dir */
3829 salvinfo->vnodeInfo[vLarge].nVnodes = 1;
3830 salvinfo->vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
3831 salvinfo->vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
3833 osi_Assert(salvinfo->vnodeInfo[vLarge].vnodes);
3834 osi_Assert(salvinfo->vnodeInfo[vLarge].inodes);
3837 vep = &salvinfo->vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
3838 ip = &salvinfo->vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
3839 if (vep->type != vNull) {
3840 Log("Not creating new root dir; existing vnode 1 is non-null\n");
3841 goto error;
3844 if (CreateReadme(salvinfo, volHeader, alinkH, vid, maxunique, &readmeid,
3845 &readmeinode) != 0) {
3846 goto error;
3848 decreadme = 1;
3850 /* set the DV to a very high number, so it is unlikely that we collide
3851 * with a cached DV */
3852 dv = 1 << 30;
3854 rootinode = IH_CREATE(alinkH, salvinfo->fileSysDevice, salvinfo->fileSysPath,
3855 0, vid, 1, 1, dv);
3856 if (!VALID_INO(rootinode)) {
3857 Log("CreateRootDir: IH_CREATE failed\n");
3858 goto error;
3860 decroot = 1;
3862 SetSalvageDirHandle(&rootdir->dirHandle, vid, salvinfo->fileSysDevice,
3863 rootinode, &salvinfo->VolumeChanged);
3864 did.Volume = vid;
3865 did.Vnode = 1;
3866 did.Unique = 1;
3867 if (MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
3868 Log("CreateRootDir: MakeDir failed\n");
3869 goto error;
3871 if (Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
3872 Log("CreateRootDir: Create failed\n");
3873 goto error;
3875 DFlush();
3876 length = Length(&rootdir->dirHandle);
3877 DZap((void *)&rootdir->dirHandle);
3879 /* create the new root dir vnode */
3880 rootvnode = calloc(1, SIZEOF_LARGEDISKVNODE);
3881 if (!rootvnode) {
3882 Log("CreateRootDir: malloc failed\n");
3883 goto error;
3886 /* only give 'rl' permissions to 'system:administrators'. We do this to
3887 * try to catch the attention of an administrator, that they should not
3888 * be writing to this directory or continue to use it. */
3889 ACL = VVnodeDiskACL(rootvnode);
3890 ACL->size = sizeof(struct acl_accessList);
3891 ACL->version = ACL_ACLVERSION;
3892 ACL->total = 1;
3893 ACL->positive = 1;
3894 ACL->negative = 0;
3895 ACL->entries[0].id = -204; /* system:administrators */
3896 ACL->entries[0].rights = PRSFS_READ | PRSFS_LOOKUP;
3898 rootvnode->type = vDirectory;
3899 rootvnode->cloned = 0;
3900 rootvnode->modeBits = 0777;
3901 rootvnode->linkCount = 2;
3902 VNDISK_SET_LEN(rootvnode, length);
3903 rootvnode->uniquifier = 1;
3904 rootvnode->dataVersion = dv;
3905 VNDISK_SET_INO(rootvnode, rootinode);
3906 rootvnode->unixModifyTime = rootvnode->serverModifyTime = now;
3907 rootvnode->author = 0;
3908 rootvnode->owner = 0;
3909 rootvnode->parent = 0;
3910 rootvnode->group = 0;
3911 rootvnode->vnodeMagic = VnodeClassInfo[vLarge].magic;
3913 /* write it out to disk */
3914 bytes = IH_IWRITE(salvinfo->vnodeInfo[vLarge].handle,
3915 vnodeIndexOffset(&VnodeClassInfo[vLarge], 1),
3916 (char*)rootvnode, SIZEOF_LARGEDISKVNODE);
3918 if (bytes != SIZEOF_LARGEDISKVNODE) {
3919 /* just cast to int and don't worry about printing real 64-bit ints;
3920 * a large disk vnode isn't anywhere near the 32-bit limit */
3921 Log("CreateRootDir: IH_IWRITE failed (%d/%d)\n", (int)bytes,
3922 (int)SIZEOF_LARGEDISKVNODE);
3923 goto error;
3926 /* update VnodeEssence for the new root vnode */
3927 salvinfo->vnodeInfo[vLarge].nAllocatedVnodes++;
3928 vep->count = 0;
3929 vep->blockCount = nBlocks(length);
3930 salvinfo->vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
3931 vep->parent = rootvnode->parent;
3932 vep->unique = rootvnode->uniquifier;
3933 vep->modeBits = rootvnode->modeBits;
3934 vep->InodeNumber = VNDISK_GET_INO(rootvnode);
3935 vep->type = rootvnode->type;
3936 vep->author = rootvnode->author;
3937 vep->owner = rootvnode->owner;
3938 vep->group = rootvnode->group;
3940 free(rootvnode);
3941 rootvnode = NULL;
3943 vep->claimed = 0;
3944 vep->changed = 0;
3945 vep->salvaged = 1;
3946 vep->todelete = 0;
3948 /* update DirSummary for the new root vnode */
3949 rootdir->vnodeNumber = 1;
3950 rootdir->unique = 1;
3951 rootdir->haveDot = 1;
3952 rootdir->haveDotDot = 1;
3953 rootdir->rwVid = vid;
3954 rootdir->copied = 0;
3955 rootdir->parent = 0;
3956 rootdir->name = strdup(".");
3957 rootdir->vname = volHeader->name;
3958 rootdir->ds_linkH = alinkH;
3960 *ip = rootinode;
3962 return 0;
3964 error:
3965 if (decroot && IH_DEC(alinkH, rootinode, vid)) {
3966 Log("CreateRootDir (recovery): IH_DEC (root) failed\n");
3968 if (decreadme && IH_DEC(alinkH, readmeinode, vid)) {
3969 Log("CreateRootDir (recovery): IH_DEC (readme) failed\n");
3971 if (rootvnode) {
3972 free(rootvnode);
3973 rootvnode = NULL;
3975 return -1;
3979 * salvage a volume group.
3981 * @param[in] salvinfo information for the curent salvage job
3982 * @param[in] rwIsp inode summary for rw volume
3983 * @param[in] alinkH link table inode handle
3985 * @return operation status
3986 * @retval 0 success
3989 SalvageVolume(struct SalvInfo *salvinfo, struct InodeSummary *rwIsp, IHandle_t * alinkH)
3991 /* This routine, for now, will only be called for read-write volumes */
3992 int i, j, code;
3993 int BlocksInVolume = 0, FilesInVolume = 0;
3994 VnodeClass class;
3995 struct DirSummary rootdir, oldrootdir;
3996 struct VnodeInfo *dirVnodeInfo;
3997 struct VnodeDiskObject vnode;
3998 VolumeDiskData volHeader;
3999 VolumeId vid;
4000 int orphaned, rootdirfound = 0;
4001 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
4002 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
4003 struct VnodeEssence *vep;
4004 afs_int32 v, pv;
4005 IHandle_t *h;
4006 afs_sfsize_t nBytes;
4007 AFSFid pa;
4008 VnodeId LFVnode, ThisVnode;
4009 Unique LFUnique, ThisUnique;
4010 char npath[128];
4011 int newrootdir = 0;
4013 vid = rwIsp->volSummary->header.id;
4014 IH_INIT(h, salvinfo->fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
4015 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
4016 osi_Assert(nBytes == sizeof(volHeader));
4017 osi_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
4018 osi_Assert(volHeader.destroyMe != DESTROY_ME);
4019 /* (should not have gotten this far with DESTROY_ME flag still set!) */
4021 DistilVnodeEssence(salvinfo, vid, vLarge,
4022 rwIsp->volSummary->header.largeVnodeIndex, &maxunique);
4023 DistilVnodeEssence(salvinfo, vid, vSmall,
4024 rwIsp->volSummary->header.smallVnodeIndex, &maxunique);
4026 dirVnodeInfo = &salvinfo->vnodeInfo[vLarge];
4027 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
4028 SalvageDir(salvinfo, volHeader.name, vid, dirVnodeInfo, alinkH, i,
4029 &rootdir, &rootdirfound);
4031 #ifdef AFS_NT40_ENV
4032 nt_sync(salvinfo->fileSysDevice);
4033 #else
4034 sync(); /* This used to be done lower level, for every dir */
4035 #endif
4036 if (Showmode) {
4037 IH_RELEASE(h);
4038 return 0;
4041 if (!rootdirfound && (orphans == ORPH_ATTACH) && !Testing) {
4043 Log("Cannot find root directory for volume %lu; attempting to create "
4044 "a new one\n", afs_printable_uint32_lu(vid));
4046 code = CreateRootDir(salvinfo, &volHeader, alinkH, vid, &rootdir,
4047 &maxunique);
4048 if (code == 0) {
4049 rootdirfound = 1;
4050 newrootdir = 1;
4051 salvinfo->VolumeChanged = 1;
4055 /* Parse each vnode looking for orphaned vnodes and
4056 * connect them to the tree as orphaned (if requested).
4058 oldrootdir = rootdir;
4059 for (class = 0; class < nVNODECLASSES; class++) {
4060 for (v = 0; v < salvinfo->vnodeInfo[class].nVnodes; v++) {
4061 vep = &(salvinfo->vnodeInfo[class].vnodes[v]);
4062 ThisVnode = bitNumberToVnodeNumber(v, class);
4063 ThisUnique = vep->unique;
4065 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
4066 continue; /* Ignore unused, claimed, and root vnodes */
4068 /* This vnode is orphaned. If it is a directory vnode, then the '..'
4069 * entry in this vnode had incremented the parent link count (In
4070 * JudgeEntry()). We need to go to the parent and decrement that
4071 * link count. But if the parent's unique is zero, then the parent
4072 * link count was not incremented in JudgeEntry().
4074 if (class == vLarge) { /* directory vnode */
4075 pv = vnodeIdToBitNumber(vep->parent);
4076 if (salvinfo->vnodeInfo[vLarge].vnodes[pv].unique != 0) {
4077 if (vep->parent == 1 && newrootdir) {
4078 /* this vnode's parent was the volume root, and
4079 * we just created the volume root. So, the parent
4080 * dir didn't exist during JudgeEntry, so the link
4081 * count was not inc'd there, so don't dec it here.
4084 /* noop */
4086 } else {
4087 salvinfo->vnodeInfo[vLarge].vnodes[pv].count++;
4092 if (!rootdirfound)
4093 continue; /* If no rootdir, can't attach orphaned files */
4095 /* Here we attach orphaned files and directories into the
4096 * root directory, LVVnode, making sure link counts stay correct.
4098 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
4099 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
4100 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
4102 /* Update this orphaned vnode's info. Its parent info and
4103 * link count (do for orphaned directories and files).
4105 vep->parent = LFVnode; /* Parent is the root dir */
4106 vep->unique = LFUnique;
4107 vep->changed = 1;
4108 vep->claimed = 1;
4109 vep->count--; /* Inc link count (root dir will pt to it) */
4111 /* If this orphaned vnode is a directory, change '..'.
4112 * The name of the orphaned dir/file is unknown, so we
4113 * build a unique name. No need to CopyOnWrite the directory
4114 * since it is not connected to tree in BK or RO volume and
4115 * won't be visible there.
4117 if (class == vLarge) {
4118 AFSFid pa;
4119 DirHandle dh;
4121 /* Remove and recreate the ".." entry in this orphaned directory */
4122 SetSalvageDirHandle(&dh, vid, salvinfo->fileSysDevice,
4123 salvinfo->vnodeInfo[class].inodes[v],
4124 &salvinfo->VolumeChanged);
4125 pa.Vnode = LFVnode;
4126 pa.Unique = LFUnique;
4127 osi_Assert(Delete(&dh, "..") == 0);
4128 osi_Assert(Create(&dh, "..", &pa) == 0);
4130 /* The original parent's link count was decremented above.
4131 * Here we increment the new parent's link count.
4133 pv = vnodeIdToBitNumber(LFVnode);
4134 salvinfo->vnodeInfo[vLarge].vnodes[pv].count--;
4138 /* Go to the root dir and add this entry. The link count of the
4139 * root dir was incremented when ".." was created. Try 10 times.
4141 for (j = 0; j < 10; j++) {
4142 pa.Vnode = ThisVnode;
4143 pa.Unique = ThisUnique;
4145 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
4146 ((class ==
4147 vLarge) ? "__ORPHANDIR__" :
4148 "__ORPHANFILE__"), ThisVnode,
4149 ThisUnique);
4151 CopyOnWrite(salvinfo, &rootdir);
4152 code = Create(&rootdir.dirHandle, npath, &pa);
4153 if (!code)
4154 break;
4156 ThisUnique += 50; /* Try creating a different file */
4158 osi_Assert(code == 0);
4159 Log("Attaching orphaned %s to volume's root dir as %s\n",
4160 ((class == vLarge) ? "directory" : "file"), npath);
4162 } /* for each vnode in the class */
4163 } /* for each class of vnode */
4165 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
4166 DFlush();
4167 if (rootdirfound && !oldrootdir.copied && rootdir.copied) {
4168 code =
4169 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
4170 oldrootdir.rwVid);
4171 osi_Assert(code == 0);
4172 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
4175 DFlush(); /* Flush the changes */
4176 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
4177 Log("Cannot attach orphaned files and directories: Root directory not found\n");
4178 orphans = ORPH_IGNORE;
4181 /* Write out all changed vnodes. Orphaned files and directories
4182 * will get removed here also (if requested).
4184 for (class = 0; class < nVNODECLASSES; class++) {
4185 afs_sfsize_t nVnodes = salvinfo->vnodeInfo[class].nVnodes;
4186 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
4187 struct VnodeEssence *vnodes = salvinfo->vnodeInfo[class].vnodes;
4188 FilesInVolume += salvinfo->vnodeInfo[class].nAllocatedVnodes;
4189 BlocksInVolume += salvinfo->vnodeInfo[class].volumeBlockCount;
4190 for (i = 0; i < nVnodes; i++) {
4191 struct VnodeEssence *vnp = &vnodes[i];
4192 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
4194 /* If the vnode is good but is unclaimed (not listed in
4195 * any directory entries), then it is orphaned.
4197 orphaned = -1;
4198 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber))) {
4199 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
4200 vnp->changed = 1;
4203 if (vnp->changed || vnp->count) {
4204 int oldCount;
4205 nBytes =
4206 IH_IREAD(salvinfo->vnodeInfo[class].handle,
4207 vnodeIndexOffset(vcp, vnodeNumber),
4208 (char *)&vnode, sizeof(vnode));
4209 osi_Assert(nBytes == sizeof(vnode));
4211 vnode.parent = vnp->parent;
4212 oldCount = vnode.linkCount;
4213 vnode.linkCount = vnode.linkCount - vnp->count;
4215 if (orphaned == -1)
4216 orphaned = IsVnodeOrphaned(salvinfo, vnodeNumber);
4217 if (orphaned) {
4218 if (!vnp->todelete) {
4219 /* Orphans should have already been attached (if requested) */
4220 osi_Assert(orphans != ORPH_ATTACH);
4221 oblocks += vnp->blockCount;
4222 ofiles++;
4224 if (((orphans == ORPH_REMOVE) || vnp->todelete)
4225 && !Testing) {
4226 BlocksInVolume -= vnp->blockCount;
4227 FilesInVolume--;
4228 if (VNDISK_GET_INO(&vnode)) {
4229 code =
4230 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
4231 osi_Assert(code == 0);
4233 memset(&vnode, 0, sizeof(vnode));
4235 } else if (vnp->count) {
4236 if (!Showmode) {
4237 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
4239 } else {
4240 vnode.modeBits = vnp->modeBits;
4243 vnode.dataVersion++;
4244 if (!Testing) {
4245 nBytes =
4246 IH_IWRITE(salvinfo->vnodeInfo[class].handle,
4247 vnodeIndexOffset(vcp, vnodeNumber),
4248 (char *)&vnode, sizeof(vnode));
4249 osi_Assert(nBytes == sizeof(vnode));
4251 salvinfo->VolumeChanged = 1;
4255 if (!Showmode && ofiles) {
4256 Log("%s %d orphaned files and directories (approx. %u KB)\n",
4257 (!Testing
4258 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
4259 oblocks);
4262 for (class = 0; class < nVNODECLASSES; class++) {
4263 struct VnodeInfo *vip = &salvinfo->vnodeInfo[class];
4264 for (i = 0; i < vip->nVnodes; i++)
4265 if (vip->vnodes[i].name)
4266 free(vip->vnodes[i].name);
4267 if (vip->vnodes)
4268 free(vip->vnodes);
4269 if (vip->inodes)
4270 free(vip->inodes);
4273 /* Set correct resource utilization statistics */
4274 volHeader.filecount = FilesInVolume;
4275 volHeader.diskused = BlocksInVolume;
4277 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
4278 if (volHeader.uniquifier < (maxunique + 1)) {
4279 if (!Showmode)
4280 Log("Volume uniquifier is too low; fixed\n");
4281 /* Plus 2,000 in case there are workstations out there with
4282 * cached vnodes that have since been deleted
4284 volHeader.uniquifier = (maxunique + 1 + 2000);
4287 if (newrootdir) {
4288 Log("*** WARNING: Root directory recreated, but volume is fragile! "
4289 "Only use this salvaged volume to copy data to another volume; "
4290 "do not continue to use this volume (%lu) as-is.\n",
4291 afs_printable_uint32_lu(vid));
4294 if (!Testing && salvinfo->VolumeChanged) {
4295 #ifdef FSSYNC_BUILD_CLIENT
4296 if (salvinfo->useFSYNC) {
4297 afs_int32 fsync_code;
4299 fsync_code = FSYNC_VolOp(vid, NULL, FSYNC_VOL_BREAKCBKS, FSYNC_SALVAGE, NULL);
4300 if (fsync_code) {
4301 Log("Error trying to tell the fileserver to break callbacks for "
4302 "changed volume %lu; error code %ld\n",
4303 afs_printable_uint32_lu(vid),
4304 afs_printable_int32_ld(fsync_code));
4305 } else {
4306 salvinfo->VolumeChanged = 0;
4309 #endif /* FSSYNC_BUILD_CLIENT */
4311 #ifdef AFS_DEMAND_ATTACH_FS
4312 if (!salvinfo->useFSYNC) {
4313 /* A volume's contents have changed, but the fileserver will not
4314 * break callbacks on the volume until it tries to load the vol
4315 * header. So, to reduce the amount of time a client could have
4316 * stale data, remove fsstate.dat, so the fileserver will init
4317 * callback state with all clients. This is a very coarse hammer,
4318 * and in the future we should just record which volumes have
4319 * changed. */
4320 code = unlink(AFSDIR_SERVER_FSSTATE_FILEPATH);
4321 if (code && errno != ENOENT) {
4322 Log("Error %d when trying to unlink FS state file %s\n", errno,
4323 AFSDIR_SERVER_FSSTATE_FILEPATH);
4326 #endif
4329 /* Turn off the inUse bit; the volume's been salvaged! */
4330 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
4331 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
4332 volHeader.inService = 1; /* allow service again */
4333 if (salvinfo->VolumeChanged) {
4334 volHeader.needsCallback = 1;
4335 volHeader.updateDate = time(NULL);
4336 } else {
4337 volHeader.needsCallback = 0;
4339 volHeader.dontSalvage = DONT_SALVAGE;
4340 salvinfo->VolumeChanged = 0;
4341 if (!Testing) {
4342 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4343 osi_Assert(nBytes == sizeof(volHeader));
4345 if (!Showmode) {
4346 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
4347 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
4348 FilesInVolume, BlocksInVolume);
4351 IH_RELEASE(salvinfo->vnodeInfo[vSmall].handle);
4352 IH_RELEASE(salvinfo->vnodeInfo[vLarge].handle);
4353 IH_RELEASE(h);
4354 return 0;
4357 void
4358 ClearROInUseBit(struct VolumeSummary *summary)
4360 IHandle_t *h = summary->volumeInfoHandle;
4361 afs_sfsize_t nBytes;
4363 VolumeDiskData volHeader;
4365 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
4366 osi_Assert(nBytes == sizeof(volHeader));
4367 osi_Assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
4368 volHeader.inUse = 0;
4369 volHeader.needsSalvaged = 0;
4370 volHeader.inService = 1;
4371 volHeader.dontSalvage = DONT_SALVAGE;
4372 if (!Testing) {
4373 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
4374 osi_Assert(nBytes == sizeof(volHeader));
4378 /* MaybeZapVolume
4379 * Possible delete the volume.
4381 * deleteMe - Always do so, only a partial volume.
4383 void
4384 MaybeZapVolume(struct SalvInfo *salvinfo, struct InodeSummary *isp,
4385 char *message, int deleteMe, int check)
4387 if (readOnly(isp) || deleteMe) {
4388 if (isp->volSummary && !isp->volSummary->deleted) {
4389 if (deleteMe) {
4390 if (!Showmode)
4391 Log("Volume %u (is only a partial volume--probably an attempt was made to move/restore it when a machine crash occured.\n", isp->volumeId);
4392 if (!Showmode)
4393 Log("It will be deleted on this server (you may find it elsewhere)\n");
4394 } else {
4395 if (!Showmode)
4396 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
4397 if (!Showmode)
4398 Log("it will be deleted instead. It should be recloned.\n");
4400 if (!Testing) {
4401 afs_int32 code;
4402 char path[64];
4403 char filename[VMAXPATHLEN];
4404 VolumeExternalName_r(isp->volumeId, filename, sizeof(filename));
4405 sprintf(path, "%s" OS_DIRSEP "%s", salvinfo->fileSysPath, filename);
4407 code = VDestroyVolumeDiskHeader(salvinfo->fileSysPartition, isp->volumeId, isp->RWvolumeId);
4408 if (code) {
4409 Log("Error %ld destroying volume disk header for volume %lu\n",
4410 afs_printable_int32_ld(code),
4411 afs_printable_uint32_lu(isp->volumeId));
4414 /* make sure we actually delete the header file; ENOENT
4415 * is fine, since VDestroyVolumeDiskHeader probably already
4416 * unlinked it */
4417 if (unlink(path) && errno != ENOENT) {
4418 Log("Unable to unlink %s (errno = %d)\n", path, errno);
4420 if (salvinfo->useFSYNC) {
4421 AskDelete(salvinfo, isp->volumeId);
4423 isp->volSummary->deleted = 1;
4426 } else if (!check) {
4427 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
4428 isp->volumeId);
4429 Abort("Salvage of volume %u aborted\n", isp->volumeId);
4433 #ifdef AFS_DEMAND_ATTACH_FS
4435 * Locks a volume on disk for salvaging.
4437 * @param[in] volumeId volume ID to lock
4439 * @return operation status
4440 * @retval 0 success
4441 * @retval -1 volume lock raced with a fileserver restart; all volumes must
4442 * checked out and locked again
4444 * @note DAFS only
4446 static int
4447 LockVolume(struct SalvInfo *salvinfo, VolumeId volumeId)
4449 afs_int32 code;
4450 int locktype;
4452 /* should always be WRITE_LOCK, but keep the lock-type logic all
4453 * in one place, in VVolLockType. Params will be ignored, but
4454 * try to provide what we're logically doing. */
4455 locktype = VVolLockType(V_VOLUPD, 1);
4457 code = VLockVolumeByIdNB(volumeId, salvinfo->fileSysPartition, locktype);
4458 if (code) {
4459 if (code == EBUSY) {
4460 Abort("Someone else appears to be using volume %lu; Aborted\n",
4461 afs_printable_uint32_lu(volumeId));
4463 Abort("Error %ld trying to lock volume %lu; Aborted\n",
4464 afs_printable_int32_ld(code),
4465 afs_printable_uint32_lu(volumeId));
4468 code = FSYNC_VerifyCheckout(volumeId, salvinfo->fileSysPartition->name, FSYNC_VOL_OFF, FSYNC_SALVAGE);
4469 if (code == SYNC_DENIED) {
4470 /* need to retry checking out volumes */
4471 return -1;
4473 if (code != SYNC_OK) {
4474 Abort("FSYNC_VerifyCheckout failed for volume %lu with code %ld\n",
4475 afs_printable_uint32_lu(volumeId), afs_printable_int32_ld(code));
4478 /* set inUse = programType in the volume header to ensure that nobody
4479 * tries to use this volume again without salvaging, if we somehow crash
4480 * or otherwise exit before finishing the salvage.
4482 if (!Testing) {
4483 IHandle_t *h;
4484 struct VolumeHeader header;
4485 struct VolumeDiskHeader diskHeader;
4486 struct VolumeDiskData volHeader;
4488 code = VReadVolumeDiskHeader(volumeId, salvinfo->fileSysPartition, &diskHeader);
4489 if (code) {
4490 return 0;
4493 DiskToVolumeHeader(&header, &diskHeader);
4495 IH_INIT(h, salvinfo->fileSysDevice, header.parent, header.volumeInfo);
4496 if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
4497 volHeader.stamp.magic != VOLUMEINFOMAGIC) {
4499 IH_RELEASE(h);
4500 return 0;
4503 volHeader.inUse = programType;
4505 /* If we can't re-write the header, bail out and error. We don't
4506 * assert when reading the header, since it's possible the
4507 * header isn't really there (when there's no data associated
4508 * with the volume; we just delete the vol header file in that
4509 * case). But if it's there enough that we can read it, but
4510 * somehow we cannot write to it to signify we're salvaging it,
4511 * we've got a big problem and we cannot continue. */
4512 osi_Assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
4514 IH_RELEASE(h);
4517 return 0;
4519 #endif /* AFS_DEMAND_ATTACH_FS */
4521 static void
4522 AskError(struct SalvInfo *salvinfo, VolumeId volumeId)
4524 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
4525 afs_int32 code;
4526 code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4527 FSYNC_VOL_FORCE_ERROR, FSYNC_WHATEVER, NULL);
4528 if (code != SYNC_OK) {
4529 Log("AskError: failed to force volume %lu into error state; "
4530 "SYNC error code %ld (%s)\n", (long unsigned)volumeId,
4531 (long)code, SYNC_res2string(code));
4533 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
4536 void
4537 AskOffline(struct SalvInfo *salvinfo, VolumeId volumeId)
4539 afs_int32 code, i;
4540 SYNC_response res;
4542 memset(&res, 0, sizeof(res));
4544 for (i = 0; i < 3; i++) {
4545 code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4546 FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
4548 if (code == SYNC_OK) {
4549 break;
4550 } else if (code == SYNC_DENIED) {
4551 if (AskDAFS())
4552 Log("AskOffline: file server denied offline request; a general salvage may be required.\n");
4553 else
4554 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
4555 Abort("Salvage aborted\n");
4556 } else if (code == SYNC_BAD_COMMAND) {
4557 Log("AskOffline: fssync protocol mismatch (bad command word '%d'); salvage aborting.\n",
4558 FSYNC_VOL_OFF);
4559 if (AskDAFS()) {
4560 #ifdef AFS_DEMAND_ATTACH_FS
4561 Log("AskOffline: please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
4562 #else
4563 Log("AskOffline: fileserver is DAFS but we are not.\n");
4564 #endif
4565 } else {
4566 #ifdef AFS_DEMAND_ATTACH_FS
4567 Log("AskOffline: fileserver is not DAFS but we are.\n");
4568 #else
4569 Log("AskOffline: please make sure fileserver, volserver and salvager binaries are same version.\n");
4570 #endif
4572 Abort("Salvage aborted\n");
4573 } else if (i < 2) {
4574 /* try it again */
4575 Log("AskOffline: request for fileserver to take volume offline failed; trying again...\n");
4576 FSYNC_clientFinis();
4577 FSYNC_clientInit();
4580 if (code != SYNC_OK) {
4581 Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
4582 Abort("Salvage aborted\n");
4586 /* don't want to pass around state; remember it here */
4587 static int isDAFS = -1;
4589 AskDAFS(void)
4591 SYNC_response res;
4592 afs_int32 code = 1, i;
4594 /* we don't care if we race. the answer shouldn't change */
4595 if (isDAFS != -1)
4596 return isDAFS;
4598 memset(&res, 0, sizeof(res));
4600 for (i = 0; code && i < 3; i++) {
4601 code = FSYNC_VolOp(0, NULL, FSYNC_VOL_LISTVOLUMES, FSYNC_SALVAGE, &res);
4602 if (code) {
4603 Log("AskDAFS: FSYNC_VOL_LISTVOLUMES failed with code %ld reason "
4604 "%ld (%s); trying again...\n", (long)code, (long)res.hdr.reason,
4605 FSYNC_reason2string(res.hdr.reason));
4606 FSYNC_clientFinis();
4607 FSYNC_clientInit();
4611 if (code) {
4612 Log("AskDAFS: could not determine DAFS-ness, assuming not DAFS\n");
4613 res.hdr.flags = 0;
4616 if ((res.hdr.flags & SYNC_FLAG_DAFS_EXTENSIONS)) {
4617 isDAFS = 1;
4618 } else {
4619 isDAFS = 0;
4622 return isDAFS;
4625 static void
4626 MaybeAskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
4628 struct VolumeDiskHeader diskHdr;
4629 int code;
4630 code = VReadVolumeDiskHeader(volumeId, salvinfo->fileSysPartition, &diskHdr);
4631 if (code) {
4632 /* volume probably does not exist; no need to bring back online */
4633 return;
4635 AskOnline(salvinfo, volumeId);
4638 void
4639 AskOnline(struct SalvInfo *salvinfo, VolumeId volumeId)
4641 afs_int32 code, i;
4643 for (i = 0; i < 3; i++) {
4644 code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4645 FSYNC_VOL_ON, FSYNC_WHATEVER, NULL);
4647 if (code == SYNC_OK) {
4648 break;
4649 } else if (code == SYNC_DENIED) {
4650 Log("AskOnline: file server denied online request to volume %u partition %s; trying again...\n", volumeId, salvinfo->fileSysPartition->name);
4651 } else if (code == SYNC_BAD_COMMAND) {
4652 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
4653 FSYNC_VOL_ON);
4654 Log("AskOnline: please make sure file server binaries are same version.\n");
4655 break;
4656 } else if (i < 2) {
4657 /* try it again */
4658 Log("AskOnline: request for fileserver to put volume online failed; trying again...\n");
4659 FSYNC_clientFinis();
4660 FSYNC_clientInit();
4665 void
4666 AskDelete(struct SalvInfo *salvinfo, VolumeId volumeId)
4668 afs_int32 code, i;
4669 SYNC_response res;
4671 for (i = 0; i < 3; i++) {
4672 memset(&res, 0, sizeof(res));
4673 code = FSYNC_VolOp(volumeId, salvinfo->fileSysPartition->name,
4674 FSYNC_VOL_DONE, FSYNC_SALVAGE, &res);
4676 if (code == SYNC_OK) {
4677 break;
4678 } else if (code == SYNC_DENIED) {
4679 Log("AskOnline: file server denied DONE request to volume %u partition %s; trying again...\n", volumeId, salvinfo->fileSysPartition->name);
4680 } else if (code == SYNC_BAD_COMMAND) {
4681 Log("AskOnline: fssync protocol mismatch (bad command word '%d')\n",
4682 FSYNC_VOL_DONE);
4683 if (AskDAFS()) {
4684 #ifdef AFS_DEMAND_ATTACH_FS
4685 Log("AskOnline: please make sure dafileserver, davolserver, salvageserver and dasalvager binaries are same version.\n");
4686 #else
4687 Log("AskOnline: fileserver is DAFS but we are not.\n");
4688 #endif
4689 } else {
4690 #ifdef AFS_DEMAND_ATTACH_FS
4691 Log("AskOnline: fileserver is not DAFS but we are.\n");
4692 #else
4693 Log("AskOnline: please make sure fileserver, volserver and salvager binaries are same version.\n");
4694 #endif
4696 break;
4697 } else if (code == SYNC_FAILED &&
4698 (res.hdr.reason == FSYNC_UNKNOWN_VOLID ||
4699 res.hdr.reason == FSYNC_WRONG_PART)) {
4700 /* volume is already effectively 'deleted' */
4701 break;
4702 } else if (i < 2) {
4703 /* try it again */
4704 Log("AskOnline: request for fileserver to delete volume failed; trying again...\n");
4705 FSYNC_clientFinis();
4706 FSYNC_clientInit();
4712 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
4714 /* Volume parameter is passed in case iopen is upgraded in future to
4715 * require a volume Id to be passed
4717 char buf[4096];
4718 IHandle_t *srcH, *destH;
4719 FdHandle_t *srcFdP, *destFdP;
4720 ssize_t nBytes = 0;
4721 afs_foff_t size = 0;
4723 IH_INIT(srcH, device, rwvolume, inode1);
4724 srcFdP = IH_OPEN(srcH);
4725 osi_Assert(srcFdP != NULL);
4726 IH_INIT(destH, device, rwvolume, inode2);
4727 destFdP = IH_OPEN(destH);
4728 while ((nBytes = FDH_PREAD(srcFdP, buf, sizeof(buf), size)) > 0) {
4729 osi_Assert(FDH_PWRITE(destFdP, buf, nBytes, size) == nBytes);
4730 size += nBytes;
4732 osi_Assert(nBytes == 0);
4733 FDH_REALLYCLOSE(srcFdP);
4734 FDH_REALLYCLOSE(destFdP);
4735 IH_RELEASE(srcH);
4736 IH_RELEASE(destH);
4737 return 0;
4740 void
4741 PrintInodeList(struct SalvInfo *salvinfo)
4743 struct ViceInodeInfo *ip;
4744 struct ViceInodeInfo *buf;
4745 struct afs_stat status;
4746 int nInodes;
4747 afs_ino_str_t stmp;
4749 osi_Assert(afs_fstat(salvinfo->inodeFd, &status) == 0);
4750 buf = (struct ViceInodeInfo *)malloc(status.st_size);
4751 osi_Assert(buf != NULL);
4752 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
4753 osi_Assert(read(salvinfo->inodeFd, buf, status.st_size) == status.st_size);
4754 for (ip = buf; nInodes--; ip++) {
4755 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
4756 PrintInode(stmp, ip->inodeNumber), ip->linkCount,
4757 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
4758 ip->u.param[2], ip->u.param[3]);
4760 free(buf);
4763 void
4764 PrintInodeSummary(struct SalvInfo *salvinfo)
4766 int i;
4767 struct InodeSummary *isp;
4769 for (i = 0; i < salvinfo->nVolumesInInodeFile; i++) {
4770 isp = &salvinfo->inodeSummary[i];
4771 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n", isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes, isp->nSpecialInodes, isp->maxUniquifier);
4776 Fork(void)
4778 int f;
4779 #ifdef AFS_NT40_ENV
4780 f = 0;
4781 osi_Assert(0); /* Fork is never executed in the NT code path */
4782 #else
4783 f = fork();
4784 osi_Assert(f >= 0);
4785 #ifdef AFS_DEMAND_ATTACH_FS
4786 if ((f == 0) && (programType == salvageServer)) {
4787 /* we are a salvageserver child */
4788 #ifdef FSSYNC_BUILD_CLIENT
4789 VChildProcReconnectFS_r();
4790 #endif
4791 #ifdef SALVSYNC_BUILD_CLIENT
4792 VReconnectSALV_r();
4793 #endif
4795 #endif /* AFS_DEMAND_ATTACH_FS */
4796 #endif /* !AFS_NT40_ENV */
4797 return f;
4800 void
4801 Exit(int code)
4803 if (ShowLog)
4804 showlog();
4806 #ifdef AFS_DEMAND_ATTACH_FS
4807 if (programType == salvageServer) {
4808 /* release all volume locks before closing down our SYNC channels.
4809 * the fileserver may try to online volumes we have checked out when
4810 * we close down FSSYNC, so we should make sure we don't have those
4811 * volumes locked when it does */
4812 struct DiskPartition64 *dp;
4813 int i;
4814 for (i = 0; i <= VOLMAXPARTS; i++) {
4815 dp = VGetPartitionById(i, 0);
4816 if (dp) {
4817 VLockFileReinit(&dp->volLockFile);
4820 # ifdef SALVSYNC_BUILD_CLIENT
4821 VDisconnectSALV();
4822 # endif
4823 # ifdef FSSYNC_BUILD_CLIENT
4824 VDisconnectFS();
4825 # endif
4827 #endif /* AFS_DEMAND_ATTACH_FS */
4829 #ifdef AFS_NT40_ENV
4830 if (main_thread != pthread_self())
4831 pthread_exit((void *)code);
4832 else
4833 exit(code);
4834 #else
4835 exit(code);
4836 #endif
4840 Wait(char *prog)
4842 int status;
4843 int pid;
4844 pid = wait(&status);
4845 osi_Assert(pid != -1);
4846 if (WCOREDUMP(status))
4847 Log("\"%s\" core dumped!\n", prog);
4848 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
4849 return -1;
4850 return pid;
4853 static char *
4854 TimeStamp(time_t clock, int precision)
4856 struct tm *lt;
4857 static char timestamp[20];
4858 lt = localtime(&clock);
4859 if (precision)
4860 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
4861 else
4862 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
4863 return timestamp;
4866 void
4867 CheckLogFile(char * log_path)
4869 char oldSlvgLog[AFSDIR_PATH_MAX];
4871 #ifndef AFS_NT40_ENV
4872 if (useSyslog) {
4873 ShowLog = 0;
4874 return;
4876 #endif
4878 strcpy(oldSlvgLog, log_path);
4879 strcat(oldSlvgLog, ".old");
4880 if (!logFile) {
4881 renamefile(log_path, oldSlvgLog);
4882 logFile = afs_fopen(log_path, "a");
4884 if (!logFile) { /* still nothing, use stdout */
4885 logFile = stdout;
4886 ShowLog = 0;
4888 #ifndef AFS_NAMEI_ENV
4889 AFS_DEBUG_IOPS_LOG(logFile);
4890 #endif
4894 #ifndef AFS_NT40_ENV
4895 void
4896 TimeStampLogFile(char * log_path)
4898 char stampSlvgLog[AFSDIR_PATH_MAX];
4899 struct tm *lt;
4900 time_t now;
4902 now = time(0);
4903 lt = localtime(&now);
4904 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
4905 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
4906 log_path, lt->tm_year + 1900,
4907 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
4908 lt->tm_sec);
4910 /* try to link the logfile to a timestamped filename */
4911 /* if it fails, oh well, nothing we can do */
4912 if (link(log_path, stampSlvgLog))
4913 ; /* oh well */
4915 #endif
4917 void
4918 showlog(void)
4920 char line[256];
4922 #ifndef AFS_NT40_ENV
4923 if (useSyslog) {
4924 printf("Can't show log since using syslog.\n");
4925 fflush(stdout);
4926 return;
4928 #endif
4930 if (logFile) {
4931 rewind(logFile);
4932 fclose(logFile);
4935 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
4937 if (!logFile)
4938 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
4939 else {
4940 rewind(logFile);
4941 while (fgets(line, sizeof(line), logFile))
4942 printf("%s", line);
4943 fflush(stdout);
4947 void
4948 Log(const char *format, ...)
4950 struct timeval now;
4951 char tmp[1024];
4952 va_list args;
4954 va_start(args, format);
4955 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4956 va_end(args);
4957 #ifndef AFS_NT40_ENV
4958 if (useSyslog) {
4959 syslog(LOG_INFO, "%s", tmp);
4960 } else
4961 #endif
4962 if (logFile) {
4963 gettimeofday(&now, 0);
4964 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
4965 fflush(logFile);
4969 void
4970 Abort(const char *format, ...)
4972 va_list args;
4973 char tmp[1024];
4975 va_start(args, format);
4976 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
4977 va_end(args);
4978 #ifndef AFS_NT40_ENV
4979 if (useSyslog) {
4980 syslog(LOG_INFO, "%s", tmp);
4981 } else
4982 #endif
4983 if (logFile) {
4984 fprintf(logFile, "%s", tmp);
4985 fflush(logFile);
4986 if (ShowLog)
4987 showlog();
4990 if (debug)
4991 abort();
4992 Exit(1);
4995 char *
4996 ToString(const char *s)
4998 char *p;
4999 p = (char *)malloc(strlen(s) + 1);
5000 osi_Assert(p != NULL);
5001 strcpy(p, s);
5002 return p;
5005 /* Remove the FORCESALVAGE file */
5006 void
5007 RemoveTheForce(char *path)
5009 char target[1024];
5010 struct afs_stat force; /* so we can use afs_stat to find it */
5011 strcpy(target,path);
5012 strcat(target,"/FORCESALVAGE");
5013 if (!Testing && ForceSalvage) {
5014 if (afs_stat(target,&force) == 0) unlink(target);
5018 #ifndef AFS_AIX32_ENV
5020 * UseTheForceLuke - see if we can use the force
5023 UseTheForceLuke(char *path)
5025 struct afs_stat force;
5026 char target[1024];
5027 strcpy(target,path);
5028 strcat(target,"/FORCESALVAGE");
5030 return (afs_stat(target, &force) == 0);
5032 #else
5034 * UseTheForceLuke - see if we can use the force
5036 * NOTE:
5037 * The VRMIX fsck will not muck with the filesystem it is supposedly
5038 * fixing and create a "FORCESALVAGE" file (by design). Instead, we
5039 * muck directly with the root inode, which is within the normal
5040 * domain of fsck.
5041 * ListViceInodes() has a side effect of setting ForceSalvage if
5042 * it detects a need, based on root inode examination.
5045 UseTheForceLuke(char *path)
5048 return 0; /* sorry OB1 */
5050 #endif
5052 #ifdef AFS_NT40_ENV
5053 /* NT support routines */
5055 static char execpathname[MAX_PATH];
5057 nt_SalvagePartition(char *partName, int jobn)
5059 int pid;
5060 int n;
5061 childJob_t job;
5062 if (!*execpathname) {
5063 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
5064 if (!n || n == 1023)
5065 return -1;
5067 job.cj_magic = SALVAGER_MAGIC;
5068 job.cj_number = jobn;
5069 (void)strcpy(job.cj_part, partName);
5070 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
5071 return pid;
5075 nt_SetupPartitionSalvage(void *datap, int len)
5077 childJob_t *jobp = (childJob_t *) datap;
5078 char logname[AFSDIR_PATH_MAX];
5080 if (len != sizeof(childJob_t))
5081 return -1;
5082 if (jobp->cj_magic != SALVAGER_MAGIC)
5083 return -1;
5084 myjob = *jobp;
5086 /* Open logFile */
5087 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
5088 myjob.cj_number);
5089 logFile = afs_fopen(logname, "w");
5090 if (!logFile)
5091 logFile = stdout;
5093 return 0;
5097 #endif /* AFS_NT40_ENV */