#include <afsconfig.h>
#include <afs/param.h>
-RCSID
- ("$Header$");
#ifndef AFS_NT40_ENV
#include <sys/param.h>
#include <io.h>
#include <WINNT/afsevent.h>
#endif
-#if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
-#define WCOREDUMP(x) (x & 0200)
+#ifndef WCOREDUMP
+#define WCOREDUMP(x) ((x) & 0200)
#endif
#include <rx/xdr.h>
#include <afs/afsint.h>
#include <afs/osi_inode.h>
#endif
#include <afs/cmd.h>
+#include <afs/dir.h>
#include <afs/afsutil.h>
#include <afs/fileutil.h>
#include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
#include "partition.h"
#include "daemon_com.h"
#include "fssync.h"
+#include "volume_inline.h"
#include "salvsync.h"
#include "viceinode.h"
#include "salvage.h"
#include "volinodes.h" /* header magic number, etc. stuff */
#include "vol-salvage.h"
+#include "vol_internal.h"
+#include <afs/acl.h>
+#include <afs/prs_fs.h>
+
+#ifdef FSSYNC_BUILD_CLIENT
+#include "vg_cache.h"
+#endif
+
#ifdef AFS_NT40_ENV
#include <pthread.h>
#endif
int orphans = ORPH_IGNORE; /* -orphans option */
int Showmode = 0;
+
#ifndef AFS_NT40_ENV
int useSyslog = 0; /* -syslog flag */
int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
#endif
+#ifdef AFS_NT40_ENV
+int canfork = 0;
+#else
+int canfork = 1;
+#endif
+
#define MAXPARALLEL 32
int OKToZap; /* -o flag */
char *fileSysPathName; /* NT needs this to make name pretty in log. */
IHandle_t *VGLinkH; /* Link handle for current volume group. */
int VGLinkH_cnt; /* # of references to lnk handle. */
-struct DiskPartition *fileSysPartition; /* Partition being salvaged */
+struct DiskPartition64 *fileSysPartition; /* Partition being salvaged */
#ifndef AFS_NT40_ENV
char *fileSysDeviceName; /* The block device where the file system
* being salvaged was mounted */
struct VnodeInfo vnodeInfo[nVNODECLASSES];
-struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
+struct VolumeSummary *volumeSummaryp = NULL; /* Holds all the volumes in a part */
int nVolumes; /* Number of volumes (read-write and read-only)
* in volume summary */
/*@printflike@*/ void Log(const char *format, ...);
/*@printflike@*/ void Abort(const char *format, ...);
static int IsVnodeOrphaned(VnodeId vnode);
+static int AskVolumeSummary(VolumeId singleVolumeNumber);
+
+#ifdef AFS_DEMAND_ATTACH_FS
+static int LockVolume(VolumeId volumeId);
+#endif /* AFS_DEMAND_ATTACH_FS */
/* Uniquifier stored in the Inode */
static Unique
return 0; /* otherwise may be transient, e.g. EMFILE */
}
-
#define MAX_ARGS 128
#ifdef AFS_NT40_ENV
char *save_args[MAX_ARGS];
int n_save_args = 0;
-pthread_t main_thread;
+extern pthread_t main_thread;
+childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
#endif
-
-/* Get the salvage lock if not already held. Hold until process exits. */
-void
-ObtainSalvageLock(void)
+/**
+ * Get the salvage lock if not already held. Hold until process exits.
+ *
+ * @param[in] locktype READ_LOCK or WRITE_LOCK
+ */
+static void
+_ObtainSalvageLock(int locktype)
{
- int salvageLock;
+ struct VLockFile salvageLock;
+ int offset = 0;
+ int nonblock = 1;
+ int code;
-#ifdef AFS_NT40_ENV
- salvageLock =
- (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
- OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (salvageLock == (int)INVALID_HANDLE_VALUE) {
- fprintf(stderr,
- "salvager: There appears to be another salvager running! Aborted.\n");
- Exit(1);
- }
-#else
- salvageLock =
- afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
- if (salvageLock < 0) {
+ VLockFileInit(&salvageLock, AFSDIR_SERVER_SLVGLOCK_FILEPATH);
+
+ code = VLockFileLock(&salvageLock, offset, locktype, nonblock);
+ if (code == EBUSY) {
fprintf(stderr,
- "salvager: can't open salvage lock file %s, aborting\n",
- AFSDIR_SERVER_SLVGLOCK_FILEPATH);
+ "salvager: There appears to be another salvager running! "
+ "Aborted.\n");
Exit(1);
- }
-#ifdef AFS_DARWIN_ENV
- if (flock(salvageLock, LOCK_EX) == -1) {
-#else
- if (lockf(salvageLock, F_LOCK, 0) == -1) {
-#endif
+ } else if (code) {
fprintf(stderr,
- "salvager: There appears to be another salvager running! Aborted.\n");
+ "salvager: Error %d trying to acquire salvage lock! "
+ "Aborted.\n", code);
Exit(1);
}
-#endif
+}
+void
+ObtainSalvageLock(void)
+{
+ _ObtainSalvageLock(WRITE_LOCK);
+}
+void
+ObtainSharedSalvageLock(void)
+{
+ _ObtainSalvageLock(READ_LOCK);
}
#define HDSTR "\\Device\\Harddisk"
#define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
int
-SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
+SameDisk(struct DiskPartition64 *p1, struct DiskPartition64 *p2)
{
#define RES_LEN 256
char res[RES_LEN];
* PartsPerDisk are on the same disk.
*/
void
-SalvageFileSysParallel(struct DiskPartition *partP)
+SalvageFileSysParallel(struct DiskPartition64 *partP)
{
struct job {
- struct DiskPartition *partP;
+ struct DiskPartition64 *partP;
int pid; /* Pid for this job */
int jobnumb; /* Log file job number */
struct job *nextjob; /* Next partition on disk to salvage */
void
-SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
+SalvageFileSys(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
{
if (!canfork || debug || Fork() == 0) {
SalvageFileSys1(partP, singleVolumeNumber);
}
void
-SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
+SalvageFileSys1(struct DiskPartition64 *partP, VolumeId singleVolumeNumber)
{
char *name, *tdir;
char inodeListPath[256];
+ FILE *inodeFile = NULL;
static char tmpDevName[100];
static char wpath[100];
struct VolumeSummary *vsp, *esp;
int i, j;
+ int code;
+ int tries = 0;
+
+ retry:
+ tries++;
+ if (inodeFile) {
+ fclose(inodeFile);
+ inodeFile = NULL;
+ }
+ if (tries > VOL_MAX_CHECKOUT_RETRIES) {
+ Abort("Raced too many times with fileserver restarts while trying to "
+ "checkout/lock volumes; Aborted\n");
+ }
+#ifdef AFS_DEMAND_ATTACH_FS
+ if (tries > 1) {
+ /* unlock all previous volume locks, since we're about to lock them
+ * again */
+ VLockFileReinit(&partP->volLockFile);
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
fileSysPartition = partP;
fileSysDevice = fileSysPartition->device;
filesysfulldev = wpath;
#endif
- VLockPartition(partP->name);
- if (singleVolumeNumber || ForceSalvage)
+ if (singleVolumeNumber) {
+#ifndef AFS_DEMAND_ATTACH_FS
+ /* only non-DAFS locks the partition when salvaging a single volume;
+ * DAFS will lock the individual volumes in the VG */
+ VLockPartition(partP->name);
+#endif /* !AFS_DEMAND_ATTACH_FS */
+
ForceSalvage = 1;
- else
- ForceSalvage = UseTheForceLuke(fileSysPath);
- if (singleVolumeNumber) {
/* salvageserver already setup fssync conn for us */
if ((programType != salvageServer) && !VConnectFS()) {
Abort("Couldn't connect to file server\n");
}
- AskOffline(singleVolumeNumber);
+
+ AskOffline(singleVolumeNumber, partP->name);
+#ifdef AFS_DEMAND_ATTACH_FS
+ if (LockVolume(singleVolumeNumber)) {
+ goto retry;
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
} else {
+ VLockPartition(partP->name);
+ if (ForceSalvage) {
+ ForceSalvage = 1;
+ } else {
+ ForceSalvage = UseTheForceLuke(fileSysPath);
+ }
if (!Showmode)
Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
partP->name, name, (Testing ? "(READONLY mode)" : ""));
snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
getpid());
#endif
- if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
- unlink(inodeListPath);
- return;
+
+ inodeFile = fopen(inodeListPath, "w+b");
+ if (!inodeFile) {
+ Abort("Error %d when creating inode description file %s; not salvaged\n", errno, inodeListPath);
}
#ifdef AFS_NT40_ENV
/* Using nt_unlink here since we're really using the delete on close
* mean to unlink the file at that point. Those places have been
* modified to actually do that so that the NT crt can be used there.
*/
- inodeFd =
- _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
- nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
+ code = nt_unlink(inodeListPath);
#else
- inodeFd = afs_open(inodeListPath, O_RDONLY);
- unlink(inodeListPath);
+ code = unlink(inodeListPath);
#endif
+ if (code < 0) {
+ Log("Error %d when trying to unlink %s\n", errno, inodeListPath);
+ }
+
+ if (GetInodeSummary(inodeFile, singleVolumeNumber) < 0) {
+ fclose(inodeFile);
+ return;
+ }
+ inodeFd = fileno(inodeFile);
if (inodeFd == -1)
Abort("Temporary file %s is missing...\n", inodeListPath);
+ afs_lseek(inodeFd, 0L, SEEK_SET);
if (ListInodeOption) {
PrintInodeList();
return;
* Fix up inodes on last volume in set (whether it is read-write
* or read-only).
*/
- GetVolumeSummary(singleVolumeNumber);
+ if (GetVolumeSummary(singleVolumeNumber)) {
+ goto retry;
+ }
for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
i < nVolumesInInodeFile; i = j) {
RemoveTheForce(fileSysPath);
if (!Testing && singleVolumeNumber) {
+#ifdef AFS_DEMAND_ATTACH_FS
+ /* unlock vol headers so the fs can attach them when we AskOnline */
+ VLockFileReinit(&fileSysPartition->volLockFile);
+#endif /* AFS_DEMAND_ATTACH_FS */
+
AskOnline(singleVolumeNumber, fileSysPartition->name);
/* Step through the volumeSummary list and set all volumes on-line.
fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
}
- close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
+ fclose(inodeFile); /* SalvageVolumeGroup was the last which needed it. */
}
void
DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
{
+ char path[64];
+ sprintf(path, "%s/%s", fileSysPath, vsp->fileName);
+
if (!Showmode)
- Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
- if (!Testing)
- unlink(vsp->fileName);
+ Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", path, (Testing ? "would have been " : ""));
+ if (!Testing) {
+ afs_int32 code;
+ code = VDestroyVolumeDiskHeader(fileSysPartition, vsp->header.id, vsp->header.parent);
+ if (code) {
+ Log("Error %ld destroying volume disk header for volume %lu\n",
+ afs_printable_int32_ld(code),
+ afs_printable_uint32_lu(vsp->header.id));
+ }
+
+ /* make sure we actually delete the fileName file; ENOENT
+ * is fine, since VDestroyVolumeDiskHeader probably already
+ * unlinked it */
+ if (unlink(path) && errno != ENOENT) {
+ Log("Unable to unlink %s (errno = %d)\n", path, errno);
+ }
+ }
vsp->fileName = 0;
}
+int
CompareInodes(const void *_p1, const void *_p2)
{
register const struct ViceInodeInfo *p1 = _p1;
CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
register struct InodeSummary *summary)
{
- int volume = ip->u.vnode.volumeId;
- int rwvolume = volume;
- register n, nSpecial;
+ VolumeId volume = ip->u.vnode.volumeId;
+ VolumeId rwvolume = volume;
+ register int n, nSpecial;
register Unique maxunique;
n = nSpecial = 0;
maxunique = 0;
}
int
-OnlyOneVolume(struct ViceInodeInfo *inodeinfo, int singleVolumeNumber, void *rock)
+OnlyOneVolume(struct ViceInodeInfo *inodeinfo, afs_uint32 singleVolumeNumber, void *rock)
{
if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
return (inodeinfo->u.special.parentId == singleVolumeNumber);
* be unlinked by the caller.
*/
int
-GetInodeSummary(char *path, VolumeId singleVolumeNumber)
+GetInodeSummary(FILE *inodeFile, VolumeId singleVolumeNumber)
{
struct afs_stat status;
int forceSal, err;
+ int code;
struct ViceInodeInfo *ip;
struct InodeSummary summary;
char summaryFileName[50];
/* This file used to come from vfsck; cobble it up ourselves now... */
if ((err =
- ListViceInodes(dev, fileSysPath, path,
+ ListViceInodes(dev, fileSysPath, inodeFile,
singleVolumeNumber ? OnlyOneVolume : 0,
singleVolumeNumber, &forceSal, forceR, wpath, NULL)) < 0) {
if (err == -2) {
- Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, path, dev);
+ Log("*** I/O error %d when writing a tmp inode file; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, dev);
return -1;
}
- unlink(path);
Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
}
if (forceSal && !ForceSalvage) {
Log("***Forced salvage of all volumes on this partition***\n");
ForceSalvage = 1;
}
- inodeFd = afs_open(path, O_RDWR);
+ fseek(inodeFile, 0L, SEEK_SET);
+ inodeFd = fileno(inodeFile);
if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
- unlink(path);
Abort("No inode description file for \"%s\"; not salvaged\n", dev);
}
tdir = (tmpdir ? tmpdir : part);
#endif
summaryFile = afs_fopen(summaryFileName, "a+");
if (summaryFile == NULL) {
- close(inodeFd);
- unlink(path);
Abort("Unable to create inode summary file\n");
}
+
+#ifdef AFS_NT40_ENV
+ /* Using nt_unlink here since we're really using the delete on close
+ * semantics of unlink. In most places in the salvager, we really do
+ * mean to unlink the file at that point. Those places have been
+ * modified to actually do that so that the NT crt can be used there.
+ */
+ code = nt_unlink(summaryFileName);
+#else
+ code = unlink(summaryFileName);
+#endif
+ if (code < 0) {
+ Log("Error %d when trying to unlink %s\n", errno, summaryFileName);
+ }
+
if (!canfork || debug || Fork() == 0) {
int nInodes;
unsigned long st_size=(unsigned long) status.st_size;
nInodes = st_size / sizeof(struct ViceInodeInfo);
if (nInodes == 0) {
fclose(summaryFile);
- close(inodeFd);
- unlink(summaryFileName);
if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
RemoveTheForce(fileSysPath);
else {
ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
if (ip == NULL) {
fclose(summaryFile);
- close(inodeFd);
- unlink(path);
- unlink(summaryFileName);
Abort
("Unable to allocate enough space to read inode table; %s not salvaged\n",
dev);
}
if (read(inodeFd, ip, st_size) != st_size) {
fclose(summaryFile);
- close(inodeFd);
- unlink(path);
- unlink(summaryFileName);
Abort("Unable to read inode table; %s not salvaged\n", dev);
}
qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
|| write(inodeFd, ip, st_size) != st_size) {
fclose(summaryFile);
- close(inodeFd);
- unlink(path);
- unlink(summaryFileName);
Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
}
summary.index = 0;
if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
fclose(summaryFile);
- close(inodeFd);
return -1;
}
summary.index += (summary.nInodes);
if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
fclose(summaryFile);
- close(inodeFd);
return -1;
}
if (canfork && !debug) {
} else {
if (Wait("Inode summary") == -1) {
fclose(summaryFile);
- close(inodeFd);
- unlink(path);
- unlink(summaryFileName);
Exit(1); /* salvage of this partition aborted */
}
}
nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
fclose(summaryFile);
- close(inodeFd);
- unlink(summaryFileName);
return 0;
}
return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
}
-void
-GetVolumeSummary(VolumeId singleVolumeNumber)
+/**
+ * Gleans volumeSummary information by asking the fileserver
+ *
+ * @param[in] singleVolumeNumber the volume we're salvaging. 0 if we're
+ * salvaging a whole partition
+ *
+ * @return whether we obtained the volume summary information or not
+ * @retval 0 success; we obtained the volume summary information
+ * @retval -1 we raced with a fileserver restart; volume locks and checkout
+ * must be retried
+ * @retval 1 we did not get the volume summary information; either the
+ * fileserver responded with an error, or we are not supposed to
+ * ask the fileserver for the information (e.g. we are salvaging
+ * the entire partition or we are not the salvageserver)
+ *
+ * @note for non-DAFS, always returns 1
+ */
+static int
+AskVolumeSummary(VolumeId singleVolumeNumber)
{
- DIR *dirp;
- afs_int32 nvols = 0;
- struct VolumeSummary *vsp, vs;
- struct VolumeDiskHeader diskHeader;
- struct dirent *dp;
-
- /* Get headers from volume directory */
- if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
- Abort("Can't read directory %s; not salvaged\n", fileSysPath);
- if (!singleVolumeNumber) {
- while ((dp = readdir(dirp))) {
- char *p = dp->d_name;
- p = strrchr(dp->d_name, '.');
- if (p != NULL && strcmp(p, VHDREXT) == 0) {
- int fd;
- if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
- && read(fd, (char *)&diskHeader, sizeof(diskHeader))
- == sizeof(diskHeader)
- && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
- DiskToVolumeHeader(&vs.header, &diskHeader);
- nvols++;
+ afs_int32 code = 1;
+#if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
+ if (programType == salvageServer) {
+ if (singleVolumeNumber) {
+ FSSYNC_VGQry_response_t q_res;
+ SYNC_response res;
+ struct VolumeSummary *vsp;
+ int i;
+ struct VolumeDiskHeader diskHdr;
+
+ memset(&res, 0, sizeof(res));
+
+ code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
+
+ /*
+ * We must wait for the partition to finish scanning before
+ * can continue, since we will not know if we got the entire
+ * VG membership unless the partition is fully scanned.
+ * We could, in theory, just scan the partition ourselves if
+ * the VG cache is not ready, but we would be doing the exact
+ * same scan the fileserver is doing; it will almost always
+ * be faster to wait for the fileserver. The only exceptions
+ * are if the partition does not take very long to scan, and
+ * in that case it's fast either way, so who cares?
+ */
+ if (code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING) {
+ Log("waiting for fileserver to finish scanning partition %s...\n",
+ fileSysPartition->name);
+
+ for (i = 1; code == SYNC_FAILED && res.hdr.reason == FSYNC_PART_SCANNING; i++) {
+ /* linearly ramp up from 1 to 10 seconds; nothing fancy,
+ * just so small partitions don't need to wait over 10
+ * seconds every time, and large partitions are generally
+ * polled only once every ten seconds. */
+ sleep((i > 10) ? (i = 10) : i);
+
+ code = FSYNC_VGCQuery(fileSysPartition->name, singleVolumeNumber, &q_res, &res);
}
- close(fd);
}
- }
-#ifdef AFS_NT40_ENV
- closedir(dirp);
- dirp = opendir("."); /* No rewinddir for NT */
-#else
- rewinddir(dirp);
-#endif
- if (!nvols)
- nvols = 1;
- volumeSummaryp =
- (struct VolumeSummary *)malloc(nvols *
- sizeof(struct VolumeSummary));
- } else
- volumeSummaryp =
- (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
- assert(volumeSummaryp != NULL);
- nVolumes = 0;
- vsp = volumeSummaryp;
- while ((dp = readdir(dirp))) {
- char *p = dp->d_name;
- p = strrchr(dp->d_name, '.');
- if (p != NULL && strcmp(p, VHDREXT) == 0) {
- int error = 0;
- int fd;
- if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
- || read(fd, &diskHeader, sizeof(diskHeader))
- != sizeof(diskHeader)
- || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
- error = 1;
+ if (code == SYNC_FAILED && res.hdr.reason == FSYNC_UNKNOWN_VOLID) {
+ /* This can happen if there's no header for the volume
+ * we're salvaging, or no headers exist for the VG (if
+ * we're salvaging an RW). Act as if we got a response
+ * with no VG members. The headers may be created during
+ * salvaging, if there are inodes in this VG. */
+ code = 0;
+ memset(&q_res, 0, sizeof(q_res));
+ q_res.rw = singleVolumeNumber;
}
- close(fd);
- if (error) {
- if (!singleVolumeNumber) {
- if (!Showmode)
- Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
- if (!Testing)
- unlink(dp->d_name);
+
+ if (code) {
+ Log("fileserver refused VGCQuery request for volume %lu on "
+ "partition %s, code %ld reason %ld\n",
+ afs_printable_uint32_lu(singleVolumeNumber),
+ fileSysPartition->name,
+ afs_printable_int32_ld(code),
+ afs_printable_int32_ld(res.hdr.reason));
+ goto done;
+ }
+
+ if (q_res.rw != singleVolumeNumber) {
+ Log("fileserver requested salvage of clone %lu; scheduling salvage of volume group %lu...\n",
+ afs_printable_uint32_lu(singleVolumeNumber),
+ afs_printable_uint32_lu(q_res.rw));
+#ifdef SALVSYNC_BUILD_CLIENT
+ if (SALVSYNC_LinkVolume(q_res.rw,
+ singleVolumeNumber,
+ fileSysPartition->name,
+ NULL) != SYNC_OK) {
+ Log("schedule request failed\n");
}
- } else {
- char nameShouldBe[64];
- DiskToVolumeHeader(&vsp->header, &diskHeader);
- if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
- && vsp->header.parent != singleVolumeNumber) {
- Log("%u is a read-only volume; not salvaged\n",
- singleVolumeNumber);
- Exit(1);
+#endif /* SALVSYNC_BUILD_CLIENT */
+ Exit(SALSRV_EXIT_VOLGROUP_LINK);
+ }
+
+ volumeSummaryp = malloc(VOL_VG_MAX_VOLS * sizeof(struct VolumeSummary));
+ assert(volumeSummaryp != NULL);
+
+ nVolumes = 0;
+ vsp = volumeSummaryp;
+
+ for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
+ char name[VMAXPATHLEN];
+
+ if (!q_res.children[i]) {
+ continue;
}
- if (!singleVolumeNumber
- || (vsp->header.id == singleVolumeNumber
- || vsp->header.parent == singleVolumeNumber)) {
- (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
- VFORMAT, vsp->header.id);
- if (singleVolumeNumber)
- AskOffline(vsp->header.id);
- if (strcmp(nameShouldBe, dp->d_name)) {
- if (!Showmode)
- Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", dp->d_name, (Testing ? "it would have been " : ""));
- if (!Testing)
- unlink(dp->d_name);
- } else {
- vsp->fileName = ToString(dp->d_name);
- nVolumes++;
- vsp++;
+
+ /* AskOffline for singleVolumeNumber was called much earlier */
+ if (q_res.children[i] != singleVolumeNumber) {
+ AskOffline(q_res.children[i], fileSysPartition->name);
+ if (LockVolume(q_res.children[i])) {
+ /* need to retry */
+ return -1;
+ }
+ }
+
+ code = VReadVolumeDiskHeader(q_res.children[i], fileSysPartition, &diskHdr);
+ if (code) {
+ Log("Cannot read header for %lu; trying to salvage group anyway\n",
+ afs_printable_uint32_lu(q_res.children[i]));
+ code = 0;
+ continue;
+ }
+
+ DiskToVolumeHeader(&vsp->header, &diskHdr);
+ VolumeExternalName_r(q_res.children[i], name, sizeof(name));
+ vsp->fileName = ToString(name);
+ nVolumes++;
+ vsp++;
+ }
+
+ qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
+ CompareVolumes);
+ }
+ done:
+ if (code) {
+ Log("Cannot get volume summary from fileserver; falling back to scanning "
+ "entire partition\n");
+ }
+ }
+#endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
+ return code;
+}
+
+/**
+ * count how many volume headers are found by VWalkVolumeHeaders.
+ *
+ * @param[in] dp the disk partition (unused)
+ * @param[in] name full path to the .vol header (unused)
+ * @param[in] hdr the header data (unused)
+ * @param[in] last whether this is the last try or not (unused)
+ * @param[in] rock actually an afs_int32*; the running count of how many
+ * volumes we have found
+ *
+ * @retval 0 always
+ */
+static int
+CountHeader(struct DiskPartition64 *dp, const char *name,
+ struct VolumeDiskHeader *hdr, int last, void *rock)
+{
+ afs_int32 *nvols = (afs_int32 *)rock;
+ (*nvols)++;
+ return 0;
+}
+
+/**
+ * parameters to pass to the VWalkVolumeHeaders callbacks when recording volume
+ * data.
+ */
+struct SalvageScanParams {
+ VolumeId singleVolumeNumber; /**< 0 for a partition-salvage, otherwise the
+ * vol id of the VG we're salvaging */
+ struct VolumeSummary *vsp; /**< ptr to the current volume summary object
+ * we're filling in */
+ afs_int32 nVolumes; /**< # of vols we've encountered */
+ afs_int32 totalVolumes; /**< max # of vols we should encounter (the
+ * # of vols we've alloc'd memory for) */
+ int retry; /**< do we need to retry vol lock/checkout? */
+};
+
+/**
+ * records volume summary info found from VWalkVolumeHeaders.
+ *
+ * Found volumes are also taken offline if they are in the specific volume
+ * group we are looking for.
+ *
+ * @param[in] dp the disk partition
+ * @param[in] name full path to the .vol header
+ * @param[in] hdr the header data
+ * @param[in] last 1 if this is the last try to read the header, 0 otherwise
+ * @param[in] rock actually a struct SalvageScanParams*, containing the
+ * information needed to record the volume summary data
+ *
+ * @return operation status
+ * @retval 0 success
+ * @retval -1 volume locking raced with fileserver restart; checking out
+ * and locking volumes needs to be retried
+ * @retval 1 volume header is mis-named and should be deleted
+ */
+static int
+RecordHeader(struct DiskPartition64 *dp, const char *name,
+ struct VolumeDiskHeader *hdr, int last, void *rock)
+{
+ char nameShouldBe[64];
+ struct SalvageScanParams *params;
+ struct VolumeSummary summary;
+ VolumeId singleVolumeNumber;
+
+ params = (struct SalvageScanParams *)rock;
+
+ singleVolumeNumber = params->singleVolumeNumber;
+
+ DiskToVolumeHeader(&summary.header, hdr);
+
+ if (singleVolumeNumber && summary.header.id == singleVolumeNumber
+ && summary.header.parent != singleVolumeNumber) {
+
+ if (programType == salvageServer) {
+#ifdef SALVSYNC_BUILD_CLIENT
+ Log("fileserver requested salvage of clone %u; scheduling salvage of volume group %u...\n",
+ summary.header.id, summary.header.parent);
+ if (SALVSYNC_LinkVolume(summary.header.parent,
+ summary.header.id,
+ dp->name,
+ NULL) != SYNC_OK) {
+ Log("schedule request failed\n");
+ }
+#endif
+ Exit(SALSRV_EXIT_VOLGROUP_LINK);
+
+ } else {
+ Log("%u is a read-only volume; not salvaged\n",
+ singleVolumeNumber);
+ Exit(1);
+ }
+ }
+
+ if (!singleVolumeNumber || summary.header.id == singleVolumeNumber
+ || summary.header.parent == singleVolumeNumber) {
+
+ /* check if the header file is incorrectly named */
+ int badname = 0;
+ const char *base = strrchr(name, '/');
+ if (base) {
+ base++;
+ } else {
+ base = name;
+ }
+
+ (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
+ VFORMAT, afs_printable_uint32_lu(summary.header.id));
+
+
+ if (strcmp(nameShouldBe, base)) {
+ /* .vol file has wrong name; retry/delete */
+ badname = 1;
+ }
+
+ if (!badname || last) {
+ /* only offline the volume if the header is good, or if this is
+ * the last try looking at it; avoid AskOffline'ing the same vol
+ * multiple times */
+
+ if (singleVolumeNumber
+ && summary.header.id != singleVolumeNumber) {
+ /* don't offline singleVolumeNumber; we already did that
+ * earlier */
+
+ AskOffline(summary.header.id, fileSysPartition->name);
+
+#ifdef AFS_DEMAND_ATTACH_FS
+ if (!badname) {
+ /* don't lock the volume if the header is bad, since we're
+ * about to delete it anyway. */
+ if (LockVolume(summary.header.id)) {
+ params->retry = 1;
+ return -1;
}
}
+#endif /* AFS_DEMAND_ATTACH_FS */
+ }
+ }
+ if (badname) {
+ if (last && !Showmode) {
+ Log("Volume header file %s is incorrectly named (should be %s "
+ "not %s); %sdeleted (it will be recreated later, if "
+ "necessary)\n", name, nameShouldBe, base,
+ (Testing ? "it would have been " : ""));
}
- close(fd);
+ return 1;
+ }
+
+ summary.fileName = ToString(base);
+ params->nVolumes++;
+
+ if (params->nVolumes > params->totalVolumes) {
+ /* We found more volumes than we found on the first partition walk;
+ * apparently something created a volume while we were
+ * partition-salvaging, or we found more than 20 vols when salvaging a
+ * particular volume. Abort if we detect this, since other programs
+ * supposed to not touch the partition while it is partition-salvaging,
+ * and we shouldn't find more than 20 vols in a VG.
+ */
+ Abort("Found %ld vol headers, but should have found at most %ld! "
+ "Make sure the volserver/fileserver are not running at the "
+ "same time as a partition salvage\n",
+ afs_printable_int32_ld(params->nVolumes),
+ afs_printable_int32_ld(params->totalVolumes));
+ }
+
+ memcpy(params->vsp, &summary, sizeof(summary));
+ params->vsp++;
+ }
+
+ return 0;
+}
+
+/**
+ * possibly unlinks bad volume headers found from VWalkVolumeHeaders.
+ *
+ * If the header could not be read in at all, the header is always unlinked.
+ * If instead RecordHeader said the header was bad (that is, the header file
+ * is mis-named), we only unlink if we are doing a partition salvage, as
+ * opposed to salvaging a specific volume group.
+ *
+ * @param[in] dp the disk partition
+ * @param[in] name full path to the .vol header
+ * @param[in] hdr header data, or NULL if the header could not be read
+ * @param[in] rock actually a struct SalvageScanParams*, with some information
+ * about the scan
+ */
+static void
+UnlinkHeader(struct DiskPartition64 *dp, const char *name,
+ struct VolumeDiskHeader *hdr, void *rock)
+{
+ struct SalvageScanParams *params;
+ int dounlink = 0;
+
+ params = (struct SalvageScanParams *)rock;
+
+ if (!hdr) {
+ /* no header; header is too bogus to read in at all */
+ if (!Showmode) {
+ Log("%s is not a legitimate volume header file; %sdeleted\n", name, (Testing ? "it would have been " : ""));
+ }
+ if (!Testing) {
+ dounlink = 1;
+ }
+
+ } else if (!params->singleVolumeNumber) {
+ /* We were able to read in a header, but RecordHeader said something
+ * was wrong with it. We only unlink those if we are doing a partition
+ * salvage. */
+ if (!Testing) {
+ dounlink = 1;
+ }
+ }
+
+ if (dounlink && unlink(name)) {
+ Log("Error %d while trying to unlink %s\n", errno, name);
+ }
+}
+
+/**
+ * Populates volumeSummaryp with volume summary information, either by asking
+ * the fileserver for VG information, or by scanning the /vicepX partition.
+ *
+ * @param[in] singleVolumeNumber the volume ID of the single volume group we
+ * are salvaging, or 0 if this is a partition
+ * salvage
+ *
+ * @return operation status
+ * @retval 0 success
+ * @retval -1 we raced with a fileserver restart; checking out and locking
+ * volumes must be retried
+ */
+int
+GetVolumeSummary(VolumeId singleVolumeNumber)
+{
+ afs_int32 nvols = 0;
+ struct SalvageScanParams params;
+ int code;
+
+ code = AskVolumeSummary(singleVolumeNumber);
+ if (code == 0) {
+ /* we successfully got the vol information from the fileserver; no
+ * need to scan the partition */
+ return 0;
+ }
+ if (code < 0) {
+ /* we need to retry volume checkout */
+ return code;
+ }
+
+ if (!singleVolumeNumber) {
+ /* Count how many volumes we have in /vicepX */
+ code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, CountHeader,
+ NULL, &nvols);
+ if (code < 0) {
+ Abort("Can't read directory %s; not salvaged\n", fileSysPath);
}
+ if (!nvols)
+ nvols = 1;
+ } else {
+ nvols = VOL_VG_MAX_VOLS;
}
- closedir(dirp);
+
+ volumeSummaryp = malloc(nvols * sizeof(struct VolumeSummary));
+ assert(volumeSummaryp != NULL);
+
+ params.singleVolumeNumber = singleVolumeNumber;
+ params.vsp = volumeSummaryp;
+ params.nVolumes = 0;
+ params.totalVolumes = nvols;
+ params.retry = 0;
+
+ /* walk the partition directory of volume headers and record the info
+ * about them; unlinking invalid headers */
+ code = VWalkVolumeHeaders(fileSysPartition, fileSysPath, RecordHeader,
+ UnlinkHeader, ¶ms);
+ if (params.retry) {
+ /* we apparently need to retry checking-out/locking volumes */
+ return -1;
+ }
+ if (code < 0) {
+ Abort("Failed to get volume header summary\n");
+ }
+ nVolumes = params.nVolumes;
+
qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
CompareVolumes);
+
+ return 0;
}
/* Find the link table. This should be associated with the RW volume or, if
int check;
Inode ino;
int dec_VGLinkH = 0;
- int VGLinkH_p1;
+ int VGLinkH_p1 =0;
FdHandle_t *fdP = NULL;
VGLinkH_cnt = 0;
for (i = 0; i < nVols; i++) {
ip = allInodes + isp[i].index;
for (j = isp[i].nSpecialInodes; j < isp[i].nInodes; j++) {
+#ifdef AFS_NT40_ENV
+ nt_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
+#else
namei_SetLinkCount(fdP, ip[j].inodeNumber, 1, 1);
+#endif
}
}
}
&& volHeader.stamp.magic == VOLUMEINFOMAGIC
&& volHeader.dontSalvage == DONT_SALVAGE
&& volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
- if (volHeader.inUse == 1) {
+ if (volHeader.inUse != 0) {
volHeader.inUse = 0;
volHeader.inService = 1;
if (!Testing) {
register struct ViceInodeInfo *inodes, int RW,
int check, int *deleteMe)
{
- int headerFd = 0;
int i;
register struct ViceInodeInfo *ip;
int allinodesobsolete = 1;
struct VolumeDiskHeader diskHeader;
+ afs_int32 (*writefunc)(VolumeDiskHeader_t *, struct DiskPartition64 *) = NULL;
+ int *skip;
+
+ /* keeps track of special inodes that are probably 'good'; they are
+ * referenced in the vol header, and are included in the given inodes
+ * array */
+ struct {
+ int valid;
+ Inode inode;
+ } goodspecial[MAXINODETYPE];
if (deleteMe)
*deleteMe = 0;
+
+ memset(goodspecial, 0, sizeof(goodspecial));
+
+ skip = malloc(isp->nSpecialInodes * sizeof(*skip));
+ if (skip) {
+ memset(skip, 0, isp->nSpecialInodes * sizeof(*skip));
+ } else {
+ Log("cannot allocate memory for inode skip array when salvaging "
+ "volume %lu; not performing duplicate special inode recovery\n",
+ afs_printable_uint32_lu(isp->volumeId));
+ /* still try to perform the salvage; the skip array only does anything
+ * if we detect duplicate special inodes */
+ }
+
+ /*
+ * First, look at the special inodes and see if any are referenced by
+ * the existing volume header. If we find duplicate special inodes, we
+ * can use this information to use the referenced inode (it's more
+ * likely to be the 'good' one), and throw away the duplicates.
+ */
+ if (isp->volSummary && skip) {
+ /* use tempHeader, so we can use the stuff[] array to easily index
+ * into the isp->volSummary special inodes */
+ memcpy(&tempHeader, &isp->volSummary->header, sizeof(struct VolumeHeader));
+
+ for (i = 0; i < isp->nSpecialInodes; i++) {
+ ip = &inodes[isp->index + i];
+ if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
+ /* will get taken care of in a later loop */
+ continue;
+ }
+ if (ip->inodeNumber == *(stuff[ip->u.special.type - 1].inode)) {
+ goodspecial[ip->u.special.type-1].valid = 1;
+ goodspecial[ip->u.special.type-1].inode = ip->inodeNumber;
+ }
+ }
+ }
+
memset(&tempHeader, 0, sizeof(tempHeader));
tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
tempHeader.stamp.version = VOLUMEHEADERVERSION;
tempHeader.id = isp->volumeId;
tempHeader.parent = isp->RWvolumeId;
+
/* Check for duplicates (inodes are sorted by type field) */
for (i = 0; i < isp->nSpecialInodes - 1; i++) {
ip = &inodes[isp->index + i];
if (ip->u.special.type == (ip + 1)->u.special.type) {
- if (!Showmode)
- Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
- return -1;
+ afs_ino_str_t stmp1, stmp2;
+
+ if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
+ /* Will be caught in the loop below */
+ continue;
+ }
+ if (!Showmode) {
+ Log("Duplicate special %d inodes for volume %u found (%s, %s);\n",
+ ip->u.special.type, isp->volumeId,
+ PrintInode(stmp1, ip->inodeNumber),
+ PrintInode(stmp2, (ip+1)->inodeNumber));
+ }
+ if (skip && goodspecial[ip->u.special.type-1].valid) {
+ Inode gi = goodspecial[ip->u.special.type-1].inode;
+
+ if (!Showmode) {
+ Log("using special inode referenced by vol header (%s)\n",
+ PrintInode(stmp1, gi));
+ }
+
+ /* the volume header references some special inode of
+ * this type in the inodes array; are we it? */
+ if (ip->inodeNumber != gi) {
+ skip[i] = 1;
+ } else if ((ip+1)->inodeNumber != gi) {
+ /* in case this is the last iteration; we need to
+ * make sure we check ip+1, too */
+ skip[i+1] = 1;
+ }
+ } else {
+ if (!Showmode)
+ Log("cannot determine which is correct; salvage of volume %u aborted\n", isp->volumeId);
+ if (skip) {
+ free(skip);
+ }
+ return -1;
+ }
}
}
for (i = 0; i < isp->nSpecialInodes; i++) {
ip = &inodes[isp->index + i];
if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
if (check) {
- Log("Rubbish header inode\n");
+ Log("Rubbish header inode %s of type %d\n",
+ PrintInode(NULL, ip->inodeNumber),
+ ip->u.special.type);
+ if (skip) {
+ free(skip);
+ }
return -1;
}
- Log("Rubbish header inode; deleted\n");
+ Log("Rubbish header inode %s of type %d; deleted\n",
+ PrintInode(NULL, ip->inodeNumber),
+ ip->u.special.type);
} else if (!stuff[ip->u.special.type - 1].obsolete) {
- *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
+ if (skip && skip[i]) {
+ if (orphans == ORPH_REMOVE) {
+ Log("Removing orphan special inode %s of type %d\n",
+ PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
+ continue;
+ } else {
+ Log("Ignoring orphan special inode %s of type %d\n",
+ PrintInode(NULL, ip->inodeNumber), ip->u.special.type);
+ /* fall through to the ip->linkCount--; line below */
+ }
+ } else {
+ *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
+ allinodesobsolete = 0;
+ }
if (!check && ip->u.special.type != VI_LINKTABLE)
ip->linkCount--; /* Keep the inode around */
- allinodesobsolete = 0;
}
}
+ if (skip) {
+ free(skip);
+ }
+ skip = NULL;
if (allinodesobsolete) {
if (deleteMe)
}
if (isp->volSummary == NULL) {
- char name[64];
- (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
+ char path[64];
+ char headerName[64];
+ (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
+ (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
if (check) {
Log("No header file for volume %u\n", isp->volumeId);
return -1;
}
if (!Showmode)
- Log("No header file for volume %u; %screating %s/%s\n",
+ Log("No header file for volume %u; %screating %s\n",
isp->volumeId, (Testing ? "it would have been " : ""),
- fileSysPathName, name);
- headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
- assert(headerFd != -1);
+ path);
isp->volSummary = (struct VolumeSummary *)
malloc(sizeof(struct VolumeSummary));
- isp->volSummary->fileName = ToString(name);
+ isp->volSummary->fileName = ToString(headerName);
+
+ writefunc = VCreateVolumeDiskHeader;
} else {
- char name[64];
+ char path[64];
+ char headerName[64];
/* hack: these two fields are obsolete... */
isp->volSummary->header.volumeAcl = 0;
isp->volSummary->header.volumeMountTable = 0;
sizeof(struct VolumeHeader))) {
/* We often remove the name before calling us, so we make a fake one up */
if (isp->volSummary->fileName) {
- strcpy(name, isp->volSummary->fileName);
+ strcpy(headerName, isp->volSummary->fileName);
} else {
- (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
- isp->volSummary->fileName = ToString(name);
+ (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(isp->volumeId));
+ isp->volSummary->fileName = ToString(headerName);
}
+ (void)afs_snprintf(path, sizeof path, "%s/%s", fileSysPath, headerName);
- Log("Header file %s is damaged or no longer valid%s\n", name,
+ Log("Header file %s is damaged or no longer valid%s\n", path,
(check ? "" : "; repairing"));
if (check)
return -1;
- headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
- assert(headerFd != -1);
+ writefunc = VWriteVolumeDiskHeader;
}
}
- if (headerFd) {
+ if (writefunc) {
memcpy(&isp->volSummary->header, &tempHeader,
sizeof(struct VolumeHeader));
if (Testing) {
Log("It would have written a new header file for volume %u\n",
isp->volumeId);
} else {
+ afs_int32 code;
VolumeHeaderToDisk(&diskHeader, &tempHeader);
- if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
- != sizeof(struct VolumeDiskHeader)) {
- Log("Couldn't rewrite volume header file!\n");
- close(headerFd);
+ code = (*writefunc)(&diskHeader, fileSysPartition);
+ if (code) {
+ Log("Error %ld writing volume header file for volume %lu\n",
+ afs_printable_int32_ld(code),
+ afs_printable_uint32_lu(diskHeader.id));
return -1;
}
}
- close(headerFd);
}
IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
isp->volSummary->header.volumeInfo);
struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
Inode oldinode, newinode;
DirHandle newdir;
+ FdHandle_t *fdP;
afs_int32 code;
afs_sfsize_t lcode;
afs_int32 parentUnique = 1;
struct VnodeEssence *vnodeEssence;
+ afs_fsize_t length;
if (Testing)
return;
}
vnode.cloned = 0;
VNDISK_SET_INO(&vnode, newinode);
- VNDISK_SET_LEN(&vnode, Length(&newdir));
+ length = Length(&newdir);
+ VNDISK_SET_LEN(&vnode, length);
lcode =
IH_IWRITE(vnodeInfo[vLarge].handle,
vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
sizeof(vnode));
assert(lcode == sizeof(vnode));
+#if 0
#ifdef AFS_NT40_ENV
nt_sync(fileSysDevice);
#else
* an open FD on the file itself to fsync.
*/
#endif
+#else
+ vnodeInfo[vLarge].handle->ih_synced = 1;
+#endif
+ /* make sure old directory file is really closed */
+ fdP = IH_OPEN(dir->dirHandle.dirh_handle);
+ FDH_REALLYCLOSE(fdP);
+
code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
assert(code == 0);
dir->dirHandle = newdir;
}
-void
-JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
- Unique unique)
+int
+JudgeEntry(void *dirVal, char *name, afs_int32 vnodeNumber,
+ afs_int32 unique)
{
+ struct DirSummary *dir = (struct DirSummary *)dirVal;
struct VnodeEssence *vnodeEssence;
afs_int32 dirOrphaned, todelete;
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
#ifdef AFS_AIX_ENV
#ifndef AFS_NAMEI_ENV
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
#endif
#endif
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
}
* entry. Otherwise, it will get created in the next
* salvage and deleted again here. So Just skip it.
*/
- return;
+ return 0;
}
todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
}
if (!Testing) {
- ViceFid fid;
+ AFSFid fid;
fid.Vnode = vnodeNumber;
fid.Unique = vnodeEssence->unique;
CopyOnWrite(dir);
assert(Create(&dir->dirHandle, name, &fid) == 0);
}
if (todelete)
- return; /* no need to continue */
+ return 0; /* no need to continue */
}
if (strcmp(name, ".") == 0) {
if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
- ViceFid fid;
+ AFSFid fid;
if (!Showmode)
Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
if (!Testing) {
}
dir->haveDot = 1;
} else if (strcmp(name, "..") == 0) {
- ViceFid pa;
+ AFSFid pa;
if (dir->parent) {
struct VnodeEssence *dotdot;
pa.Vnode = dir->parent;
}
vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
- return;
+ return 0;
} else {
if (ShowSuid && (vnodeEssence->modeBits & 06000))
Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
- if (ShowMounts && (vnodeEssence->type == vSymlink)
+ if (/* ShowMounts && */ (vnodeEssence->type == vSymlink)
&& !(vnodeEssence->modeBits & 0111)) {
int code, size;
- char buf[1024];
+ char buf[1025];
IHandle_t *ihP;
FdHandle_t *fdP;
IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
vnodeEssence->InodeNumber);
fdP = IH_OPEN(ihP);
- assert(fdP != NULL);
+ if (fdP == NULL) {
+ Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber);
+ IH_RELEASE(ihP);
+ return 0;
+ }
size = FDH_SIZE(fdP);
- assert(size != -1);
- memset(buf, 0, 1024);
+ if (size < 0) {
+ Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber);
+ FDH_REALLYCLOSE(fdP);
+ IH_RELEASE(ihP);
+ return 0;
+ }
+
if (size > 1024)
size = 1024;
code = FDH_READ(fdP, buf, size);
- assert(code == size);
- Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
- dir->dirHandle.dirh_handle->ih_vid, dir->vname,
- dir->name ? dir->name : "??", name, buf);
+ if (code == size) {
+ buf[size] = '\0';
+ if ( (*buf != '#' && *buf != '%') || buf[strlen(buf)-1] != '.' ) {
+ Log("Volume %u (%s) mount point %s/%s to '%s' invalid, %s to symbolic link\n",
+ dir->dirHandle.dirh_handle->ih_vid, dir->vname, dir->name ? dir->name : "??", name, buf,
+ Testing ? "would convert" : "converted");
+ vnodeEssence->modeBits |= 0111;
+ vnodeEssence->changed = 1;
+ } else if (ShowMounts) Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
+ dir->dirHandle.dirh_handle->ih_vid, dir->vname,
+ dir->name ? dir->name : "??", name, buf);
+ } else {
+ Log("Volume %s cound not read mount point vnode %u size %d code %d\n",
+ dir->vname, vnodeNumber, size, code);
+ }
FDH_REALLYCLOSE(fdP);
IH_RELEASE(ihP);
}
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
}
/* This directory claims the vnode */
vnodeEssence->claimed = 1;
}
vnodeEssence->count--;
+ return 0;
}
void
vep->owner = vnode->owner;
vep->group = vnode->group;
if (vnode->type == vDirectory) {
- assert(class == vLarge);
- vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
+ if (class != vLarge) {
+ VnodeId vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
+ vip->nAllocatedVnodes--;
+ memset(vnode, 0, sizeof(vnode));
+ IH_IWRITE(vnodeInfo[vSmall].handle,
+ vnodeIndexOffset(vcp, vnodeNumber),
+ (char *)&vnode, sizeof(vnode));
+ VolumeChanged = 1;
+ } else
+ vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
}
}
}
return;
}
+/**
+ * Get a new FID that can be used to create a new file.
+ *
+ * @param[in] volHeader vol header for the volume
+ * @param[in] class what type of vnode we'll be creating (vLarge or vSmall)
+ * @param[out] afid the FID that we can use (only Vnode and Unique are set)
+ * @param[inout] maxunique max uniquifier for all vnodes in the volume;
+ * updated to the new max unique if we create a new
+ * vnode
+ */
+static void
+GetNewFID(VolumeDiskData *volHeader, VnodeClass class, AFSFid *afid,
+ Unique *maxunique)
+{
+ int i;
+ for (i = 0; i < vnodeInfo[class].nVnodes; i++) {
+ if (vnodeInfo[class].vnodes[i].type == vNull) {
+ break;
+ }
+ }
+ if (i == vnodeInfo[class].nVnodes) {
+ /* no free vnodes; make a new one */
+ vnodeInfo[class].nVnodes++;
+ vnodeInfo[class].vnodes = realloc(vnodeInfo[class].vnodes,
+ sizeof(struct VnodeEssence) * (i+1));
+ vnodeInfo[class].vnodes[i].type = vNull;
+ }
+
+ afid->Vnode = bitNumberToVnodeNumber(i, class);
+
+ if (volHeader->uniquifier < (*maxunique + 1)) {
+ /* header uniq is bad; it will get bumped by 2000 later */
+ afid->Unique = *maxunique + 1 + 2000;
+ (*maxunique)++;
+ } else {
+ /* header uniq seems okay; just use that */
+ afid->Unique = *maxunique = volHeader->uniquifier++;
+ }
+}
+
+/**
+ * Create a vnode for a README file explaining not to use a recreated-root vol.
+ *
+ * @param[in] volHeader vol header for the volume
+ * @param[in] alinkH ihandle for i/o for the volume
+ * @param[in] vid volume id
+ * @param[inout] maxunique max uniquifier for all vnodes in the volume;
+ * updated to the new max unique if we create a new
+ * vnode
+ * @param[out] afid FID for the new readme vnode
+ * @param[out] ainode the inode for the new readme file
+ *
+ * @return operation status
+ * @retval 0 success
+ * @retval -1 error
+ */
+static int
+CreateReadme(VolumeDiskData *volHeader, IHandle_t *alinkH,
+ VolumeId vid, Unique *maxunique, AFSFid *afid, Inode *ainode)
+{
+ Inode readmeinode;
+ struct VnodeDiskObject *rvnode = NULL;
+ afs_sfsize_t bytes;
+ IHandle_t *readmeH = NULL;
+ struct VnodeEssence *vep;
+ afs_fsize_t length;
+ time_t now = time(NULL);
+
+ /* Try to make the note brief, but informative. Only administrators should
+ * be able to read this file at first, so we can hopefully assume they
+ * know what AFS is, what a volume is, etc. */
+ char readme[] =
+"This volume has been salvaged, but has lost its original root directory.\n"
+"The root directory that exists now has been recreated from orphan files\n"
+"from the rest of the volume. This recreated root directory may interfere\n"
+"with old cached data on clients, and there is no way the salvager can\n"
+"reasonably prevent that. So, it is recommended that you do not continue to\n"
+"use this volume, but only copy the salvaged data to a new volume.\n"
+"Continuing to use this volume as it exists now may cause some clients to\n"
+"behave oddly when accessing this volume.\n"
+"\n\t -- Your friendly neighborhood OpenAFS salvager\n";
+ /* ^ the person reading this probably just lost some data, so they could
+ * use some cheering up. */
+
+ /* -1 for the trailing NUL */
+ length = sizeof(readme) - 1;
+
+ GetNewFID(volHeader, vSmall, afid, maxunique);
+
+ vep = &vnodeInfo[vSmall].vnodes[vnodeIdToBitNumber(afid->Vnode)];
+
+ /* create the inode and write the contents */
+ readmeinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid,
+ afid->Vnode, afid->Unique, 1);
+ if (!VALID_INO(readmeinode)) {
+ Log("CreateReadme: readme IH_CREATE failed\n");
+ goto error;
+ }
+
+ IH_INIT(readmeH, fileSysDevice, vid, readmeinode);
+ bytes = IH_IWRITE(readmeH, 0, readme, length);
+ IH_RELEASE(readmeH);
+
+ if (bytes != length) {
+ Log("CreateReadme: IWRITE failed (%d/%d)\n", (int)bytes,
+ (int)sizeof(readme));
+ goto error;
+ }
+
+ /* create the vnode and write it out */
+ rvnode = malloc(SIZEOF_SMALLDISKVNODE);
+ if (!rvnode) {
+ Log("CreateRootDir: error alloc'ing memory\n");
+ goto error;
+ }
+
+ rvnode->type = vFile;
+ rvnode->cloned = 0;
+ rvnode->modeBits = 0777;
+ rvnode->linkCount = 1;
+ VNDISK_SET_LEN(rvnode, length);
+ rvnode->uniquifier = afid->Unique;
+ rvnode->dataVersion = 1;
+ VNDISK_SET_INO(rvnode, readmeinode);
+ rvnode->unixModifyTime = rvnode->serverModifyTime = now;
+ rvnode->author = 0;
+ rvnode->owner = 0;
+ rvnode->parent = 1;
+ rvnode->group = 0;
+ rvnode->vnodeMagic = VnodeClassInfo[vSmall].magic;
+
+ bytes = IH_IWRITE(vnodeInfo[vSmall].handle,
+ vnodeIndexOffset(&VnodeClassInfo[vSmall], afid->Vnode),
+ (char*)rvnode, SIZEOF_SMALLDISKVNODE);
+
+ if (bytes != SIZEOF_SMALLDISKVNODE) {
+ Log("CreateReadme: IH_IWRITE failed (%d/%d)\n", (int)bytes,
+ (int)SIZEOF_SMALLDISKVNODE);
+ goto error;
+ }
+
+ /* update VnodeEssence for new readme vnode */
+ vnodeInfo[vSmall].nAllocatedVnodes++;
+ vep->count = 0;
+ vep->blockCount = nBlocks(length);
+ vnodeInfo[vSmall].volumeBlockCount += vep->blockCount;
+ vep->parent = rvnode->parent;
+ vep->unique = rvnode->uniquifier;
+ vep->modeBits = rvnode->modeBits;
+ vep->InodeNumber = VNDISK_GET_INO(rvnode);
+ vep->type = rvnode->type;
+ vep->author = rvnode->author;
+ vep->owner = rvnode->owner;
+ vep->group = rvnode->group;
+
+ free(rvnode);
+ rvnode = NULL;
+
+ vep->claimed = 1;
+ vep->changed = 0;
+ vep->salvaged = 1;
+ vep->todelete = 0;
+
+ *ainode = readmeinode;
+
+ return 0;
+
+ error:
+ if (IH_DEC(alinkH, readmeinode, vid)) {
+ Log("CreateReadme (recovery): IH_DEC failed\n");
+ }
+
+ if (rvnode) {
+ free(rvnode);
+ rvnode = NULL;
+ }
+
+ return -1;
+}
+
+/**
+ * create a root dir for a volume that lacks one.
+ *
+ * @param[in] volHeader vol header for the volume
+ * @param[in] alinkH ihandle for disk access for this volume group
+ * @param[in] vid volume id we're dealing with
+ * @param[out] rootdir populated with info about the new root dir
+ * @param[inout] maxunique max uniquifier for all vnodes in the volume;
+ * updated to the new max unique if we create a new
+ * vnode
+ *
+ * @return operation status
+ * @retval 0 success
+ * @retval -1 error
+ */
+static int
+CreateRootDir(VolumeDiskData *volHeader, IHandle_t *alinkH, VolumeId vid,
+ struct DirSummary *rootdir, Unique *maxunique)
+{
+ FileVersion dv;
+ int decroot = 0, decreadme = 0;
+ AFSFid did, readmeid;
+ afs_fsize_t length;
+ Inode rootinode;
+ struct VnodeDiskObject *rootvnode;
+ struct acl_accessList *ACL;
+ Inode *ip;
+ afs_sfsize_t bytes;
+ struct VnodeEssence *vep;
+ Inode readmeinode;
+ time_t now = time(NULL);
+
+ if (!vnodeInfo[vLarge].vnodes && !vnodeInfo[vSmall].vnodes) {
+ Log("Not creating new root dir; volume appears to lack any vnodes\n");
+ goto error;
+ }
+
+ if (!vnodeInfo[vLarge].vnodes) {
+ /* We don't have any large vnodes in the volume; allocate room
+ * for one so we can recreate the root dir */
+ vnodeInfo[vLarge].nVnodes = 1;
+ vnodeInfo[vLarge].vnodes = calloc(1, sizeof(struct VnodeEssence));
+ vnodeInfo[vLarge].inodes = calloc(1, sizeof(Inode));
+
+ assert(vnodeInfo[vLarge].vnodes);
+ assert(vnodeInfo[vLarge].inodes);
+ }
+
+ vep = &vnodeInfo[vLarge].vnodes[vnodeIdToBitNumber(1)];
+ ip = &vnodeInfo[vLarge].inodes[vnodeIdToBitNumber(1)];
+ if (vep->type != vNull) {
+ Log("Not creating new root dir; existing vnode 1 is non-null\n");
+ goto error;
+ }
+
+ if (CreateReadme(volHeader, alinkH, vid, maxunique, &readmeid, &readmeinode)) {
+ goto error;
+ }
+ decreadme = 1;
+
+ /* set the DV to a very high number, so it is unlikely that we collide
+ * with a cached DV */
+ dv = 1 << 30;
+
+ rootinode = IH_CREATE(alinkH, fileSysDevice, fileSysPath, 0, vid, 1, 1, dv);
+ if (!VALID_INO(rootinode)) {
+ Log("CreateRootDir: IH_CREATE failed\n");
+ goto error;
+ }
+ decroot = 1;
+
+ SetSalvageDirHandle(&rootdir->dirHandle, vid, fileSysDevice, rootinode);
+ did.Volume = vid;
+ did.Vnode = 1;
+ did.Unique = 1;
+ if (MakeDir(&rootdir->dirHandle, (afs_int32*)&did, (afs_int32*)&did)) {
+ Log("CreateRootDir: MakeDir failed\n");
+ goto error;
+ }
+ if (Create(&rootdir->dirHandle, "README.ROOTDIR", &readmeid)) {
+ Log("CreateRootDir: Create failed\n");
+ goto error;
+ }
+ DFlush();
+ length = Length(&rootdir->dirHandle);
+ DZap((void *)&rootdir->dirHandle);
+
+ /* create the new root dir vnode */
+ rootvnode = malloc(SIZEOF_LARGEDISKVNODE);
+ if (!rootvnode) {
+ Log("CreateRootDir: malloc failed\n");
+ goto error;
+ }
+
+ /* only give 'rl' permissions to 'system:administrators'. We do this to
+ * try to catch the attention of an administrator, that they should not
+ * be writing to this directory or continue to use it. */
+ ACL = VVnodeDiskACL(rootvnode);
+ ACL->size = sizeof(struct acl_accessList);
+ ACL->version = ACL_ACLVERSION;
+ ACL->total = 1;
+ ACL->positive = 1;
+ ACL->negative = 0;
+ ACL->entries[0].id = -204; /* system:administrators */
+ ACL->entries[0].rights = PRSFS_READ | PRSFS_LOOKUP;
+
+ rootvnode->type = vDirectory;
+ rootvnode->cloned = 0;
+ rootvnode->modeBits = 0777;
+ rootvnode->linkCount = 2;
+ VNDISK_SET_LEN(rootvnode, length);
+ rootvnode->uniquifier = 1;
+ rootvnode->dataVersion = dv;
+ VNDISK_SET_INO(rootvnode, rootinode);
+ rootvnode->unixModifyTime = rootvnode->serverModifyTime = now;
+ rootvnode->author = 0;
+ rootvnode->owner = 0;
+ rootvnode->parent = 0;
+ rootvnode->group = 0;
+ rootvnode->vnodeMagic = VnodeClassInfo[vLarge].magic;
+
+ /* write it out to disk */
+ bytes = IH_IWRITE(vnodeInfo[vLarge].handle,
+ vnodeIndexOffset(&VnodeClassInfo[vLarge], 1),
+ (char*)rootvnode, SIZEOF_LARGEDISKVNODE);
+
+ if (bytes != SIZEOF_LARGEDISKVNODE) {
+ /* just cast to int and don't worry about printing real 64-bit ints;
+ * a large disk vnode isn't anywhere near the 32-bit limit */
+ Log("CreateRootDir: IH_IWRITE failed (%d/%d)\n", (int)bytes,
+ (int)SIZEOF_LARGEDISKVNODE);
+ goto error;
+ }
+
+ /* update VnodeEssence for the new root vnode */
+ vnodeInfo[vLarge].nAllocatedVnodes++;
+ vep->count = 0;
+ vep->blockCount = nBlocks(length);
+ vnodeInfo[vLarge].volumeBlockCount += vep->blockCount;
+ vep->parent = rootvnode->parent;
+ vep->unique = rootvnode->uniquifier;
+ vep->modeBits = rootvnode->modeBits;
+ vep->InodeNumber = VNDISK_GET_INO(rootvnode);
+ vep->type = rootvnode->type;
+ vep->author = rootvnode->author;
+ vep->owner = rootvnode->owner;
+ vep->group = rootvnode->group;
+
+ free(rootvnode);
+ rootvnode = NULL;
+
+ vep->claimed = 0;
+ vep->changed = 0;
+ vep->salvaged = 1;
+ vep->todelete = 0;
+
+ /* update DirSummary for the new root vnode */
+ rootdir->vnodeNumber = 1;
+ rootdir->unique = 1;
+ rootdir->haveDot = 1;
+ rootdir->haveDotDot = 1;
+ rootdir->rwVid = vid;
+ rootdir->copied = 0;
+ rootdir->parent = 0;
+ rootdir->name = strdup(".");
+ rootdir->vname = volHeader->name;
+ rootdir->ds_linkH = alinkH;
+
+ *ip = rootinode;
+
+ return 0;
+
+ error:
+ if (decroot && IH_DEC(alinkH, rootinode, vid)) {
+ Log("CreateRootDir (recovery): IH_DEC (root) failed\n");
+ }
+ if (decreadme && IH_DEC(alinkH, readmeinode, vid)) {
+ Log("CreateRootDir (recovery): IH_DEC (readme) failed\n");
+ }
+ if (rootvnode) {
+ free(rootvnode);
+ rootvnode = NULL;
+ }
+ return -1;
+}
+
int
SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
{
afs_int32 v, pv;
IHandle_t *h;
afs_sfsize_t nBytes;
- ViceFid pa;
+ AFSFid pa;
VnodeId LFVnode, ThisVnode;
Unique LFUnique, ThisUnique;
char npath[128];
+ int newrootdir = 0;
vid = rwIsp->volSummary->header.id;
IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
&rootdirfound);
}
+#ifdef AFS_NT40_ENV
+ nt_sync(fileSysDevice);
+#else
+ sync(); /* This used to be done lower level, for every dir */
+#endif
if (Showmode) {
IH_RELEASE(h);
return 0;
}
+ if (!rootdirfound && (orphans == ORPH_ATTACH) && !Testing) {
+
+ Log("Cannot find root directory for volume %lu; attempting to create "
+ "a new one\n", afs_printable_uint32_lu(vid));
+
+ code = CreateRootDir(&volHeader, alinkH, vid, &rootdir, &maxunique);
+ if (code == 0) {
+ rootdirfound = 1;
+ newrootdir = 1;
+ }
+ }
+
/* Parse each vnode looking for orphaned vnodes and
* connect them to the tree as orphaned (if requested).
*/
*/
if (class == vLarge) { /* directory vnode */
pv = vnodeIdToBitNumber(vep->parent);
- if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
- vnodeInfo[vLarge].vnodes[pv].count++;
+ if (vnodeInfo[vLarge].vnodes[pv].unique != 0) {
+ if (vep->parent == 1 && newrootdir) {
+ /* this vnode's parent was the volume root, and
+ * we just created the volume root. So, the parent
+ * dir didn't exist during JudgeEntry, so the link
+ * count was not inc'd there, so don't dec it here.
+ */
+
+ /* noop */
+
+ } else {
+ vnodeInfo[vLarge].vnodes[pv].count++;
+ }
+ }
}
if (!rootdirfound)
* won't be visible there.
*/
if (class == vLarge) {
- ViceFid pa;
+ AFSFid pa;
DirHandle dh;
/* Remove and recreate the ".." entry in this orphaned directory */
if (!Showmode) {
Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
}
+ } else {
+ vnode.modeBits = vnp->modeBits;
}
vnode.dataVersion++;
volHeader.uniquifier = (maxunique + 1 + 2000);
}
+ if (newrootdir) {
+ Log("*** WARNING: Root directory recreated, but volume is fragile! "
+ "Only use this salvaged volume to copy data to another volume; "
+ "do not continue to use this volume (%lu) as-is.\n",
+ afs_printable_uint32_lu(vid));
+ }
+
/* Turn off the inUse bit; the volume's been salvaged! */
volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
if (!Showmode)
Log("it will be deleted instead. It should be recloned.\n");
}
- if (!Testing)
- unlink(isp->volSummary->fileName);
+ if (!Testing) {
+ afs_int32 code;
+ char path[64];
+ sprintf(path, "%s/%s", fileSysPath, isp->volSummary->fileName);
+
+ code = VDestroyVolumeDiskHeader(fileSysPartition, isp->volumeId, isp->RWvolumeId);
+ if (code) {
+ Log("Error %ld destroying volume disk header for volume %lu\n",
+ afs_printable_int32_ld(code),
+ afs_printable_uint32_lu(isp->volumeId));
+ }
+
+ /* make sure we actually delete the fileName file; ENOENT
+ * is fine, since VDestroyVolumeDiskHeader probably already
+ * unlinked it */
+ if (unlink(path) && errno != ENOENT) {
+ Log("Unable to unlink %s (errno = %d)\n", path, errno);
+ }
+ }
}
} else if (!check) {
Log("%s salvage was unsuccessful: read-write volume %u\n", message,
}
}
+#ifdef AFS_DEMAND_ATTACH_FS
+/**
+ * Locks a volume on disk for salvaging.
+ *
+ * @param[in] volumeId volume ID to lock
+ *
+ * @return operation status
+ * @retval 0 success
+ * @retval -1 volume lock raced with a fileserver restart; all volumes must
+ * checked out and locked again
+ *
+ * @note DAFS only
+ */
+static int
+LockVolume(VolumeId volumeId)
+{
+ afs_int32 code;
+ int locktype;
+
+ /* should always be WRITE_LOCK, but keep the lock-type logic all
+ * in one place, in VVolLockType. Params will be ignored, but
+ * try to provide what we're logically doing. */
+ locktype = VVolLockType(V_VOLUPD, 1);
+
+ code = VLockVolumeByIdNB(volumeId, fileSysPartition, locktype);
+ if (code) {
+ if (code == EBUSY) {
+ Abort("Someone else appears to be using volume %lu; Aborted\n",
+ afs_printable_uint32_lu(volumeId));
+ }
+ Abort("Error %ld trying to lock volume %lu; Aborted\n",
+ afs_printable_int32_ld(code),
+ afs_printable_uint32_lu(volumeId));
+ }
+
+ code = FSYNC_VerifyCheckout(volumeId, fileSysPathName, FSYNC_VOL_OFF, FSYNC_SALVAGE);
+ if (code == SYNC_DENIED) {
+ /* need to retry checking out volumes */
+ return -1;
+ }
+ if (code != SYNC_OK) {
+ Abort("FSYNC_VerifyCheckout failed for volume %lu with code %ld\n",
+ afs_printable_uint32_lu(volumeId), afs_printable_int32_ld(code));
+ }
+
+ /* set inUse = programType in the volume header to ensure that nobody
+ * tries to use this volume again without salvaging, if we somehow crash
+ * or otherwise exit before finishing the salvage.
+ */
+ if (!Testing) {
+ IHandle_t *h;
+ struct VolumeHeader header;
+ struct VolumeDiskHeader diskHeader;
+ struct VolumeDiskData volHeader;
+
+ code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
+ if (code) {
+ return 0;
+ }
+
+ DiskToVolumeHeader(&header, &diskHeader);
+
+ IH_INIT(h, fileSysDevice, header.parent, header.volumeInfo);
+ if (IH_IREAD(h, 0, (char*)&volHeader, sizeof(volHeader)) != sizeof(volHeader) ||
+ volHeader.stamp.magic != VOLUMEINFOMAGIC) {
+
+ IH_RELEASE(h);
+ return 0;
+ }
+
+ volHeader.inUse = programType;
+
+ /* If we can't re-write the header, bail out and error. We don't
+ * assert when reading the header, since it's possible the
+ * header isn't really there (when there's no data associated
+ * with the volume; we just delete the vol header file in that
+ * case). But if it's there enough that we can read it, but
+ * somehow we cannot write to it to signify we're salvaging it,
+ * we've got a big problem and we cannot continue. */
+ assert(IH_IWRITE(h, 0, (char*)&volHeader, sizeof(volHeader)) == sizeof(volHeader));
+
+ IH_RELEASE(h);
+ }
+
+ return 0;
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
void
-AskOffline(VolumeId volumeId)
+AskOffline(VolumeId volumeId, char * partition)
{
afs_int32 code, i;
+ SYNC_response res;
+
+ memset(&res, 0, sizeof(res));
for (i = 0; i < 3; i++) {
- code = FSYNC_VolOp(volumeId, NULL, FSYNC_VOL_OFF, FSYNC_SALVAGE, NULL);
+ code = FSYNC_VolOp(volumeId, partition, FSYNC_VOL_OFF, FSYNC_SALVAGE, &res);
if (code == SYNC_OK) {
break;
register struct ViceInodeInfo *ip;
struct ViceInodeInfo *buf;
struct afs_stat status;
- register nInodes;
+ register int nInodes;
assert(afs_fstat(inodeFd, &status) == 0);
buf = (struct ViceInodeInfo *)malloc(status.st_size);
#else
f = fork();
assert(f >= 0);
+#ifdef AFS_DEMAND_ATTACH_FS
+ if ((f == 0) && (programType == salvageServer)) {
+ /* we are a salvageserver child */
+#ifdef FSSYNC_BUILD_CLIENT
+ VChildProcReconnectFS_r();
#endif
+#ifdef SALVSYNC_BUILD_CLIENT
+ VReconnectSALV_r();
+#endif
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* !AFS_NT40_ENV */
return f;
}
void
-Exit(code)
- int code;
+Exit(int code)
{
if (ShowLog)
showlog();
+
+#ifdef AFS_DEMAND_ATTACH_FS
+ if (programType == salvageServer) {
+#ifdef SALVSYNC_BUILD_CLIENT
+ VDisconnectSALV();
+#endif
+#ifdef FSSYNC_BUILD_CLIENT
+ VDisconnectFS();
+#endif
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
#ifdef AFS_NT40_ENV
if (main_thread != pthread_self())
pthread_exit((void *)code);
static char timestamp[20];
lt = localtime(&clock);
if (precision)
- (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
+ (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
else
(void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
return timestamp;
}
#endif
- rewind(logFile);
- fclose(logFile);
+ if (logFile) {
+ rewind(logFile);
+ fclose(logFile);
+ }
logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
syslog(LOG_INFO, "%s", tmp);
} else
#endif
- {
- gettimeofday(&now, 0);
- fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
- fflush(logFile);
- }
+ if (logFile) {
+ gettimeofday(&now, 0);
+ fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
+ fflush(logFile);
+ }
}
void
syslog(LOG_INFO, "%s", tmp);
} else
#endif
- {
- fprintf(logFile, "%s", tmp);
- fflush(logFile);
- if (ShowLog)
- showlog();
- }
+ if (logFile) {
+ fprintf(logFile, "%s", tmp);
+ fflush(logFile);
+ if (ShowLog)
+ showlog();
+ }
if (debug)
abort();
}
char *
-ToString(char *s)
+ToString(const char *s)
{
register char *p;
p = (char *)malloc(strlen(s) + 1);
assert(p != NULL);
strcpy(p, s);
return p;
-
}
/* Remove the FORCESALVAGE file */
void
RemoveTheForce(char *path)
{
+ char target[1024];
+ struct afs_stat force; /* so we can use afs_stat to find it */
+ strcpy(target,path);
+ strcat(target,"/FORCESALVAGE");
if (!Testing && ForceSalvage) {
- if (chdir(path) == 0)
- unlink("FORCESALVAGE");
+ if (afs_stat(target,&force) == 0) unlink(target);
}
}
UseTheForceLuke(char *path)
{
struct afs_stat force;
+ char target[1024];
+ strcpy(target,path);
+ strcat(target,"/FORCESALVAGE");
- assert(chdir(path) != -1);
-
- return (afs_stat("FORCESALVAGE", &force) == 0);
+ return (afs_stat(target, &force) == 0);
}
#else
/*