mirror of
https://git.openafs.org/openafs.git
synced 2025-01-19 07:20:11 +00:00
Create missing root directory when ORPH_ATTACH
When we are salvaging with ORPH_ATTACH, orphans are normally attached to a volume's root directory. If the volume is missing a root directory, however, nothing is attached, and the volume can appear empty or unusable. So, to make it possible to get a useful volume out of a volume that lost (only) its root directory, create a new root dir, and attach orphans to that root. FIXES 94658 Change-Id: I946a60485b5793952707f44ac369ee243e8dc076 Reviewed-on: http://gerrit.openafs.org/1235 Reviewed-by: Derrick Brashear <shadow@dementia.org> Tested-by: Derrick Brashear <shadow@dementia.org>
This commit is contained in:
parent
88a40ffd3b
commit
32ebc44f42
@ -190,6 +190,8 @@ Vnodes with 0 inode pointers in RW volumes are now deleted.
|
||||
#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"
|
||||
@ -3356,6 +3358,372 @@ SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
|
||||
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)
|
||||
{
|
||||
@ -3379,6 +3747,7 @@ 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);
|
||||
@ -3408,6 +3777,18 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
|
||||
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).
|
||||
*/
|
||||
@ -3429,8 +3810,20 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
|
||||
*/
|
||||
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)
|
||||
@ -3628,6 +4021,13 @@ SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user