host/config/protecteddevicedetails.cpp (401 lines of code) (raw):
///
/// \file protecteddevicedetails.cpp
///
/// \brief manages the device name mapping to the SCSI-ID and device name for Azure to Azure
/// replication scenarios
/// class VxProtectedDeviceDetail: mapping of Disk ID to SCSI-ID and Disk ID to Device Name
/// class VxProtectedDeviceDetailCache: manages the in-memory cache of the device details
/// class VxProtectedDeviceDetailPeristentStore: Adds, removes and deletes th device details to persistant file
///
///
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <ace/Guard_T.h>
#include <ace/Mutex.h>
#include <ace/File_Lock.h>
#include <boost/lexical_cast.hpp>
#include "protecteddevicedetails.h"
#include "localconfigurator.h"
#include "portablehelpers.h"
#include "portablehelpersmajor.h"
#include "inmdefines.h"
#include "logger.h"
#include "inmsafeint.h"
#include "inmageex.h"
#include "securityutils.h"
#include "scopeguard.h"
#include "errorexception.h"
#include <boost/algorithm/string.hpp>
#include "setpermissions.h"
const char VxProtectedDeviceDetailPeristentStore::CHECKSUM_SEPARATOR = '\n';
VxProtectedDeviceDetailPeristentStore::VxProtectedDeviceDetailPeristentStore()
{
}
bool VxProtectedDeviceDetailPeristentStore::Init(void)
{
std::string cachePath;
bool brval = LocalConfigurator::getVxProtectedDeviceDetailCachePathname(cachePath);
if (brval)
{
SetCachePath(cachePath);
}
return brval;
}
void VxProtectedDeviceDetailPeristentStore::SetCachePath(const std::string &cachePath)
{
m_cachePath = cachePath;
#if defined (SV_WINDOWS) && defined (SV_USES_LONGPATHS)
m_cacheLockPath = "\\\\?\\" + m_cachePath + INMLOCKEXT;
#else
m_cacheLockPath = m_cachePath + INMLOCKEXT;
#endif
}
/// RefreshCache should be called only with the m_detailCacheLock held.
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE VxProtectedDeviceDetailPeristentStore::RefreshCache()
{
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE cs = E_MAP_CACHE_DOES_NOT_EXIST;
time_t t = GetMapCacheMtime();
if (0 == t)
{
m_errMsg = "Failed to find device name map file modified time.";
return cs;
}
if (t != m_detailCache.m_mTime)
{
cs = ReadFromPersistence(m_detailCache);
}
return cs;
}
bool VxProtectedDeviceDetailPeristentStore::AddMap(const VxProtectedDeviceDetail &devNameMap)
{
boost::unique_lock<boost::mutex> lock(m_detailCacheLock);
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE cs = E_MAP_CACHE_DOES_NOT_EXIST;
cs = RefreshCache();
if (cs != E_MAP_CACHE_VALID && cs != E_MAP_CACHE_DOES_NOT_EXIST)
return false;
m_detailCache.m_details.push_back(devNameMap);
if (Persist(m_detailCache.m_details))
{
cs = ReadFromPersistence(m_detailCache);
}
else
{
DebugPrintf(SV_LOG_ERROR,
"%s adding to device name map failed.\n",
FUNCTION_NAME);
cs = E_MAP_CACHE_WRITE_FAILURE;
}
return (cs == E_MAP_CACHE_VALID);
}
bool VxProtectedDeviceDetailPeristentStore::GetMap(const std::string &id, VxProtectedDeviceDetail &devNameMap)
{
boost::unique_lock<boost::mutex> lock(m_detailCacheLock);
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE cs = E_MAP_CACHE_DOES_NOT_EXIST;
cs = RefreshCache();
if (cs != E_MAP_CACHE_VALID)
return false;
VxProtectedDeviceDetailIter_t iter = m_detailCache.m_details.begin();
while (iter != m_detailCache.m_details.end())
{
if (boost::iequals(iter->m_id,id))
{
devNameMap = *iter;
return true;
}
iter++;
}
return false;
}
bool VxProtectedDeviceDetailPeristentStore::RemoveMap(const VxProtectedDeviceDetail &devNameMap)
{
boost::unique_lock<boost::mutex> lock(m_detailCacheLock);
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE cs = E_MAP_CACHE_DOES_NOT_EXIST;
cs = RefreshCache();
if (cs != E_MAP_CACHE_VALID)
return false;
VxProtectedDeviceDetailIter_t iter = m_detailCache.m_details.begin();
while (iter != m_detailCache.m_details.end())
{
if (iter->m_id == devNameMap.m_id)
iter = m_detailCache.m_details.erase(iter);
else
iter++;
}
if (Persist(m_detailCache.m_details))
{
cs = ReadFromPersistence(m_detailCache);
}
else
cs = E_MAP_CACHE_WRITE_FAILURE;
return (cs == E_MAP_CACHE_VALID);
}
bool VxProtectedDeviceDetailPeristentStore::GetCurrentDetails(VxProtectedDeviceDetails_t &details)
{
boost::unique_lock<boost::mutex> lock(m_detailCacheLock);
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE cs = E_MAP_CACHE_DOES_NOT_EXIST;
cs = RefreshCache();
if (cs == E_MAP_CACHE_DOES_NOT_EXIST)
return true;
if (cs != E_MAP_CACHE_VALID)
return false;
details = m_detailCache.m_details;
return true;
}
bool VxProtectedDeviceDetailPeristentStore::Persist(const VxProtectedDeviceDetails_t &details)
{
// Check if earlier ID has changed
bool haveput = CacheMapsAndChecksum(details);
// Do not leave stale cache file
if (!haveput) {
std::string delErrMsg;
if (!DeleteCacheFile(delErrMsg))
m_errMsg += delErrMsg;
}
std::string errormsg;
if (!securitylib::setPermissions(m_cachePath, errormsg)) {
DebugPrintf(SV_LOG_ERROR, "%s\n", errormsg.c_str());
}
return haveput;
}
/*
* Cache file format:
* _______________
* |<checksum><\n>|
* |<data> |
* |______________|
*/
bool VxProtectedDeviceDetailPeristentStore::CacheMapsAndChecksum(const VxProtectedDeviceDetails_t &details)
{
bool havecached = false;
VxProtectedDeviceDetailCache deviceDetailCache;
deviceDetailCache.m_details = details;
std::string serializedDetailss;
try {
serializedDetailss = JSON::producer<VxProtectedDeviceDetailCache>::convert(deviceDetailCache);
}
catch (JSON::json_exception& jsone)
{
m_errMsg = "serializing map caught json exception. ";
m_errMsg += jsone.what();
return havecached;
}
catch (...) {
m_errMsg = "serializing map caught unknown exception.";
return havecached;
}
std::string checksum = securitylib::genSha256Mac(serializedDetailss.c_str(), serializedDetailss.length());
DebugPrintf(SV_LOG_DEBUG, "Updating cache file %s with checksum: %s\n", m_cachePath.c_str(), checksum.c_str());
//Acquire write lock
DebugPrintf(SV_LOG_DEBUG, "Acquiring write lock %s\n", m_cacheLockPath.c_str());
ACE_File_Lock lock(ACE_TEXT_CHAR_TO_TCHAR(m_cacheLockPath.c_str()), O_CREAT | O_RDWR, SV_LOCK_PERMISSIONS, 0);
if (-1 == lock.acquire_write()) {
int saveErrNo = ACE_OS::last_error();
m_errMsg = std::string("Failed to take dev details cache write lock ") + m_cacheLockPath + " with error " + boost::lexical_cast<std::string>(saveErrNo) + ".";
return false;
}
DebugPrintf(SV_LOG_DEBUG, "Acquired.\n");
//Write to cache file
FILE *fp;
std::string mode, errMsg;
mode = "w";
mode += FOPEN_MODE_NOINHERIT;
if (InmFopen(&fp, m_cachePath, mode, errMsg)) {
DebugPrintf(SV_LOG_DEBUG, "opened cache file.\n");
havecached = InmFprintf(fp, m_cachePath, errMsg, false, 0, "%s%c%s", checksum.c_str(), CHECKSUM_SEPARATOR, serializedDetailss.c_str());
if (havecached)
DebugPrintf(SV_LOG_DEBUG, "updated cache file.\n");
else
m_errMsg = std::string("Failed to update dev details cache file ") + m_cachePath + " with error " + errMsg + ".";
//For output file io functions, fclose return value has to be checked to know all output was written correctly
if (InmFclose(&fp, m_cachePath, errMsg))
DebugPrintf(SV_LOG_DEBUG, "closed cache file.\n");
else {
havecached = false;
m_errMsg += std::string("Failed to close handle of dev details cache file ") + m_cachePath + " with error " + errMsg + ".";
}
} else
m_errMsg = std::string("Failed to open dev details cache file ") + m_cachePath + " with error " + errMsg + ".";
return havecached;
}
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE VxProtectedDeviceDetailPeristentStore::ReadFromPersistence(VxProtectedDeviceDetailCache &dc)
{
MAP_CACHE_STATE cs = VerifyAndGetCache(dc);
// Clear cache if it is not valid
if ((E_MAP_CACHE_CORRUPT == cs) || (E_MAP_CACHE_READ_FAILURE == cs)) {
std::string delErrMsg;
if (!DeleteCacheFile(delErrMsg))
m_errMsg += delErrMsg;
}
return cs;
}
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE VxProtectedDeviceDetailPeristentStore::VerifyAndGetCache(VxProtectedDeviceDetailCache &dc)
{
//Acquire read lock
DebugPrintf(SV_LOG_DEBUG, "Getting dev details from cache %s. Acquiring read lock %s\n", m_cachePath.c_str(), m_cacheLockPath.c_str());
ACE_File_Lock lock(ACE_TEXT_CHAR_TO_TCHAR(m_cacheLockPath.c_str()), O_CREAT | O_RDWR, SV_LOCK_PERMISSIONS, 0);
if (-1 == lock.acquire_read()) {
int saveErrNo = ACE_OS::last_error();
m_errMsg = std::string("Failed to take dev details cache read lock ") + m_cacheLockPath + " with error " + boost::lexical_cast<std::string>(saveErrNo) + ".";
return E_MAP_CACHE_LOCK_FAILURE;
}
DebugPrintf(SV_LOG_DEBUG, "Acquired.\n");
//Open cache
MAP_CACHE_STATE cs;
std::string mode("r");
mode += FOPEN_MODE_NOINHERIT;
FILE *fp = fopen(m_cachePath.c_str(), mode.c_str());
if (fp) {
DebugPrintf(SV_LOG_DEBUG, "opened cache file.\n");
ON_BLOCK_EXIT(&fclose, fp);
dc.m_mTime = GetMapCacheMtime();
cs = VerifyFileAndGetCache(fp, dc);
} else {
int saveErrNo = errno;
cs = (ENOENT == saveErrNo) ? E_MAP_CACHE_DOES_NOT_EXIST : E_MAP_CACHE_READ_FAILURE;
m_errMsg = std::string("Failed to open cache file ") + m_cachePath + " with error " + boost::lexical_cast<std::string>(saveErrNo)+".";
}
return cs;
}
VxProtectedDeviceDetailPeristentStore::MAP_CACHE_STATE VxProtectedDeviceDetailPeristentStore::VerifyFileAndGetCache(FILE *fp, VxProtectedDeviceDetailCache &dc)
{
MAP_CACHE_STATE cs = E_MAP_CACHE_CORRUPT;
//Get checksum
int i;
char c;
std::string checksum;
while ((i = getc(fp)) != EOF) {
c = static_cast<char>(i);
if (CHECKSUM_SEPARATOR == c)
break;
checksum += c;
}
DebugPrintf(SV_LOG_DEBUG, "Checksum found is %s\n", checksum.c_str());
//Get data
std::string data;
while ((i = getc(fp)) != EOF) {
c = static_cast<char>(i);
data += c;
}
bool shouldproceed = true;
//Verify checksum
if (checksum.empty()) {
m_errMsg = std::string("Failed to find checksum in dev details cache file ") + m_cachePath + ".";
shouldproceed = false;
}
//Verify data
if (data.empty()) {
m_errMsg += std::string("Failed to find data in dev details cache file ") + m_cachePath + ".";
shouldproceed = false;
}
//non empty checksum and data: calculate and compare checksum
if (shouldproceed)
cs = FillCacheIfDataValid(checksum, data, dc) ? E_MAP_CACHE_VALID : E_MAP_CACHE_CORRUPT;
return cs;
}
bool VxProtectedDeviceDetailPeristentStore::FillCacheIfDataValid(const std::string &checksum, const std::string &data, VxProtectedDeviceDetailCache &dc)
{
bool isvalid = false;
//Get new checksum
std::string newChecksum = securitylib::genSha256Mac(data.c_str(), data.length());
DebugPrintf(SV_LOG_DEBUG, "Newly generated checksum is %s\n", newChecksum.c_str());
//Compare and parse data to get volumes
if (newChecksum == checksum) {
try {
DebugPrintf(SV_LOG_DEBUG, "Checksums match.\n");
dc = JSON::consumer<VxProtectedDeviceDetailCache>::convert(data);
isvalid = true;
dc.m_checksum = checksum;
DebugPrintf(SV_LOG_DEBUG, "Successfully parsed dev details.\n");
} catch (std::string const& e) {
m_errMsg = std::string("Failed to parse data to get dev details with error ") + e;
} catch (char const* e) {
m_errMsg = std::string("Failed to parse data to get dev details with error ") + e;
} catch (ContextualException const& ce) {
m_errMsg = std::string("Failed to parse data to get dev details with error ") + ce.what();
} catch (std::exception const& e) {
m_errMsg = std::string("Failed to parse data to get dev details with error ") + e.what();
} catch (...) {
m_errMsg = "Failed to parse data to get dev details.";
}
} else
m_errMsg = "Checksum in file: " + checksum + ", does not match calculated checksum: " + newChecksum;
if (!isvalid)
m_errMsg += std::string(". dev details cache file ") + m_cachePath + " is corrupt.";
return isvalid;
}
bool VxProtectedDeviceDetailPeristentStore::ClearMap(void)
{
std::string delErrMsg;
bool havecleared = DeleteCacheFile(delErrMsg);
if (!havecleared)
m_errMsg = delErrMsg;
return havecleared;
}
//This is also an implicit corrective action if write/read to cache fail. Hence m_errMsg should not be overwritten.
bool VxProtectedDeviceDetailPeristentStore::DeleteCacheFile(std::string &errMsg)
{
bool havedeleted = true;
DebugPrintf(SV_LOG_DEBUG, "Clearing cache file %s.\n", m_cachePath.c_str());
//Dual check to avoid unnecessary lock in case lot of readers are trying to delete corrupt cache
if (MapExists()) {
//Acquire write lock
DebugPrintf(SV_LOG_DEBUG, "Acquiring write lock %s\n", m_cacheLockPath.c_str());
ACE_File_Lock lock(ACE_TEXT_CHAR_TO_TCHAR(m_cacheLockPath.c_str()), O_CREAT | O_RDWR, SV_LOCK_PERMISSIONS, 0);
if (-1 == lock.acquire_write()) {
int saveErrNo = ACE_OS::last_error();
errMsg = std::string("Failed to take dev details cache write lock ") + m_cacheLockPath + " with error " + boost::lexical_cast<std::string>(saveErrNo)+".";
return false;
}
DebugPrintf(SV_LOG_DEBUG, "Acquired.\n");
//check again and clear
if (MapExists()) {
havedeleted = (0 == remove(m_cachePath.c_str()));
if (havedeleted)
DebugPrintf(SV_LOG_DEBUG, "Cleared dev details cache file %s.\n", m_cachePath.c_str());
else {
int saveErrNo = errno;
errMsg = std::string("Failed to remove dev details cache file ") + m_cachePath + " with error " + boost::lexical_cast<std::string>(saveErrNo) + ".";
}
}
}
return havedeleted;
}
bool VxProtectedDeviceDetailPeristentStore::MapExists(void)
{
ACE_stat st;
bool isexisting = false;
if (0 == sv_stat(m_cachePath.c_str(), &st)) {
isexisting = true;
DebugPrintf(SV_LOG_DEBUG, "dev details cache file %s exists.\n", m_cachePath.c_str());
} else
DebugPrintf(SV_LOG_DEBUG, "dev details cache file %s does not exist.\n", m_cachePath.c_str());
return isexisting;
}
std::string VxProtectedDeviceDetailPeristentStore::GetErrorMessage(void)
{
return m_errMsg;
}
time_t VxProtectedDeviceDetailPeristentStore::GetMapCacheMtime(void)
{
ACE_stat st;
time_t t = 0;
if (0 == sv_stat(m_cachePath.c_str(), &st)) {
t = st.st_mtime;
std::stringstream ss;
ss << "dev details cache file " << m_cachePath << " has mtime " << t;
DebugPrintf(SV_LOG_DEBUG, "%s.\n", ss.str().c_str());
} else
DebugPrintf(SV_LOG_DEBUG, "dev details cache file %s does not exist. Hence cannot find mtime.\n", m_cachePath.c_str());
return t;
}
bool VxProtectedDeviceDetailPeristentStore::BackupCurrentDetails(void)
{
bool havecopied = true;
DebugPrintf(SV_LOG_DEBUG, "backing up cache file %s.\n", m_cachePath.c_str());
//Dual check to avoid unnecessary lock in case lot of readers are trying to delete corrupt cache
if (MapExists()) {
//Acquire write lock
DebugPrintf(SV_LOG_DEBUG, "Acquiring write lock %s\n", m_cacheLockPath.c_str());
ACE_File_Lock lock(ACE_TEXT_CHAR_TO_TCHAR(m_cacheLockPath.c_str()), O_CREAT | O_RDWR, SV_LOCK_PERMISSIONS, 0);
if (-1 == lock.acquire_write()) {
int saveErrNo = ACE_OS::last_error();
m_errMsg = std::string("Failed to take dev details cache write lock ") + m_cacheLockPath + " with error " + boost::lexical_cast<std::string>(saveErrNo)+".";
return false;
}
DebugPrintf(SV_LOG_DEBUG, "Acquired.\n");
//check again and clear
if (MapExists()) {
try {
boost::filesystem::path backupFilePath(m_cachePath);
backupFilePath.replace_extension(boost::posix_time::to_iso_string(boost::posix_time::microsec_clock::local_time()) + ".dat");
boost::filesystem::copy_file(m_cachePath, backupFilePath.string());
}
catch (const boost::filesystem::filesystem_error& exp)
{
havecopied = false;
m_errMsg = std::string("Failed to take backup of dev details cache file ") + m_cachePath + " with error " + exp.what();
}
if (havecopied)
DebugPrintf(SV_LOG_DEBUG, "backed up dev details cache file %s.\n", m_cachePath.c_str());
}
}
return havecopied;
}
void VxProtectedDeviceDetailPeristentStore::Dump(void)
{
boost::unique_lock<boost::mutex> lock(m_detailCacheLock);
VxProtectedDeviceDetailIter_t iter = m_detailCache.m_details.begin();
while (iter != m_detailCache.m_details.end())
{
DebugPrintf(SV_LOG_ALWAYS,
"%s : %s %s %s\n",
FUNCTION_NAME,
iter->m_id.c_str(),
iter->m_scsiId.c_str(),
iter->m_deviceName.c_str());
iter++;
}
}