#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"
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
IUnique(Unique u)
{
char *name, *tdir;
char inodeListPath[256];
- FILE *inodeFile;
+ 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, 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)" : ""));
* 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.
* salvaging a whole partition
*
* @return whether we obtained the volume summary information or not
- * @retval 0 success; we obtained the volume summary information
- * @retval nonzero 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)
+ * @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
*/
AskVolumeSummary(VolumeId singleVolumeNumber)
{
afs_int32 code = 1;
-#ifdef FSSYNC_BUILD_CLIENT
+#if defined(FSSYNC_BUILD_CLIENT) && defined(AFS_DEMAND_ATTACH_FS)
if (programType == salvageServer) {
if (singleVolumeNumber) {
FSSYNC_VGQry_response_t q_res;
continue;
}
+ /* 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",
"entire partition\n");
}
}
-#endif /* FSSYNC_BUILD_CLIENT */
+#endif /* FSSYNC_BUILD_CLIENT && AFS_DEMAND_ATTACH_FS */
return code;
}
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? */
};
/**
* information needed to record the volume summary data
*
* @return operation status
- * @retval 0 success
- * @retval 1 volume header is mis-named and should be deleted
+ * @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,
* 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) {
}
}
-void
+/**
+ * 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;
- if (AskVolumeSummary(singleVolumeNumber) == 0) {
+ code = AskVolumeSummary(singleVolumeNumber);
+ if (code == 0) {
/* we successfully got the vol information from the fileserver; no
* need to scan the partition */
- return;
+ return 0;
+ }
+ if (code < 0) {
+ /* we need to retry volume checkout */
+ return code;
}
if (!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");
}
qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
CompareVolumes);
+
+ return 0;
}
/* Find the link table. This should be associated with the RW volume or, if
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)
{
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);
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)
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 */
}
}
+#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, char * partition)
Log("AskOffline: request for fileserver to take volume offline failed; salvage aborting.\n");
Abort("Salvage aborted\n");
}
-
-#ifdef AFS_DEMAND_ATTACH_FS
- /* set inUse = programType in the volume header. We do this in case
- * the fileserver restarts/crashes while we are salvaging.
- * Otherwise, the fileserver could attach the volume again on
- * startup while we are salvaging, which would be very bad, or
- * schedule another salvage while we are salvaging, which would be
- * annoying. */
- if (!Testing) {
- IHandle_t *h;
- struct VolumeHeader header;
- struct VolumeDiskHeader diskHeader;
- struct VolumeDiskData volHeader;
-
- code = VReadVolumeDiskHeader(volumeId, fileSysPartition, &diskHeader);
- if (code) {
- return;
- }
-
- 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;
- }
-
- 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);
- }
-#endif /* AFS_DEMAND_ATTACH_FS */
}
void