host/config/datacacher.cpp (435 lines of code) (raw):

#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 "datacacher.h" #include "localconfigurator.h" #include "portablehelpers.h" #include "portablehelpersmajor.h" #include "inmdefines.h" #include "serializevolumegroupsettings.h" #include "logger.h" #include "inmsafeint.h" #include "inmageex.h" #include "securityutils.h" #include "scopeguard.h" #include "setpermissions.h" const char DataCacher::CHECKSUM_SEPARATOR = '\n'; bool DataCacher::UpdateVolumesCache(DataCacher::VolumesCache &volumesCache, std::string &err, SV_LOG_LEVEL logLevel) { bool iscacheavailable = false; DataCacher dataCacher; std::stringstream ssErr; if (dataCacher.Init()) { time_t t = dataCacher.GetVolumesCacheMtime(logLevel); if (0 == t) { ssErr << "Failed to find volumes cache modified time."; DebugPrintf(logLevel, "%s: %s\n", FUNCTION_NAME, ssErr.str().c_str()); } else if (t != volumesCache.m_MTime) { DebugPrintf(SV_LOG_DEBUG, "volumes cache updated. Getting latest one.\n"); DataCacher::CACHE_STATE cs = dataCacher.GetVolumesFromCache(volumesCache); if (DataCacher::E_CACHE_VALID == cs) { DebugPrintf(SV_LOG_DEBUG, "Got latest cache.\n"); iscacheavailable = true; } else if (DataCacher::E_CACHE_DOES_NOT_EXIST == cs) { ssErr << "volumes cache not existing."; DebugPrintf(SV_LOG_ERROR, "%s: %s\n", FUNCTION_NAME, ssErr.str().c_str()); } else { ssErr << "Failed to get volumes cache with error " << dataCacher.GetErrMsg(); DebugPrintf(SV_LOG_ERROR, "%s: %s\n", FUNCTION_NAME, ssErr.str().c_str()); } } else { DebugPrintf(SV_LOG_DEBUG, "volumes cache is already latest.\n"); iscacheavailable = true; } } else { ssErr << "DataCacher initialization failed."; DebugPrintf(SV_LOG_ERROR, "%s: %s\n", FUNCTION_NAME, ssErr.str().c_str()); } err = ssErr.str(); return iscacheavailable; } DataCacher::DataCacher() { } bool DataCacher::Init(void) { std::string volumesCachePath; bool brval = LocalConfigurator::getVolumesCachePathname(volumesCachePath); if (brval) { SetVolumesCachePath(volumesCachePath); } return brval; } void DataCacher::SetVolumesCachePath(const std::string &volumesCachePath) { m_VolumesCachePath = volumesCachePath; #if defined (SV_WINDOWS) && defined (SV_USES_LONGPATHS) m_VolumesCacheLockPath = "\\\\?\\" + m_VolumesCachePath + INMLOCKEXT; #else m_VolumesCacheLockPath = m_VolumesCachePath + INMLOCKEXT; #endif } bool DataCacher::GetSCSIAddress(const VolumeSummary &vs, std::string& bus, std::string& port, std::string& target, std::string& lun) { ConstAttributesIter_t cit; cit = vs.attributes.find(NsVolumeAttributes::SCSI_BUS); if (vs.attributes.end() == cit) { return false; } bus = cit->second; cit = vs.attributes.find(NsVolumeAttributes::SCSI_PORT); if (vs.attributes.end() == cit){ return false; } port = cit->second; cit = vs.attributes.find(NsVolumeAttributes::SCSI_TARGET_ID); if (vs.attributes.end() == cit){ return false; } target = cit->second; cit = vs.attributes.find(NsVolumeAttributes::SCSI_LOGICAL_UNIT); if (vs.attributes.end() == cit){ return false; } lun = cit->second; return true; } void DataCacher::ValidateOldVolumeState(const VolumeSummary &vs, const VolumeSummaries_t& allVolumes) { std::string oldScsiBus, oldScsiLun, oldScsiPort, oldScsiTarget; std::string scsiBus, scsiLun, scsiPort, scsiTarget; if (!GetSCSIAddress(vs, oldScsiBus, oldScsiPort, oldScsiTarget, oldScsiLun)) { return; } VolumeSummaries_t::const_iterator volIt = allVolumes.begin(); for (; volIt != allVolumes.end(); volIt++) { if (VolumeSummary::DISK != volIt->type) { continue; } ConstAttributesIter_t cit; cit = volIt->attributes.find(NsVolumeAttributes::NAME_BASED_ON); if (volIt->attributes.end() == cit) { continue; } if (NsVolumeAttributes::SIGNATURE != cit->second) { continue; } if (!GetSCSIAddress(*volIt, scsiBus, scsiPort, scsiTarget, scsiLun)) { continue; } if ((0 != scsiBus.compare(oldScsiBus)) || (0 != scsiTarget.compare(oldScsiTarget)) || (0 != scsiPort.compare(oldScsiPort)) || (0 != scsiLun.compare(oldScsiLun))) { continue; } if (0 != vs.name.compare(volIt->name)) { if (!vs.id.empty() && !volIt->id.empty()) { DebugPrintf(SV_LOG_ERROR, "DiskId Changed old Id: %s Signature: %s New Id: %s Signature: %s ScsiBus: %s ScsiPort: %s ScsiTarget: %s ScsiLun: %s", vs.id.c_str(), vs.name.c_str(), volIt->id.c_str(), volIt->name.c_str(), scsiBus.c_str(), scsiPort.c_str(), scsiTarget.c_str(), scsiLun.c_str()); } else { DebugPrintf(SV_LOG_ERROR, "DiskId Changed old Signature: %s New Signature: %s ScsiBus: %s ScsiPort: %s ScsiTarget: %s ScsiLun: %s", vs.name.c_str(), volIt->name.c_str(), scsiBus.c_str(), scsiPort.c_str(), scsiTarget.c_str(), scsiLun.c_str()); } } break; } } void DataCacher::VerifyVolumeStateChanges(const VolumeSummaries_t &allVolumes) { VolumesCache oldVolumesCache; DataCacher::CACHE_STATE cs = GetVolumesFromCache(oldVolumesCache); if (cs != DataCacher::E_CACHE_VALID){ return; } VolumeSummaries_t::iterator oldVolumeIt = oldVolumesCache.m_Vs.begin(); for (; oldVolumeIt != oldVolumesCache.m_Vs.end(); oldVolumeIt++) { if (VolumeSummary::DISK != oldVolumeIt->type) { continue; } ConstAttributesIter_t cit; cit = oldVolumeIt->attributes.find(NsVolumeAttributes::NAME_BASED_ON); if (oldVolumeIt->attributes.end() == cit) { continue; } if (NsVolumeAttributes::SIGNATURE != cit->second) { continue; } ValidateOldVolumeState(*oldVolumeIt, allVolumes); } } bool DataCacher::PutVolumesToCache(const VolumeSummaries_t &vs) { DebugPrintf(SV_LOG_DEBUG, "ENTERED %s\n", FUNCTION_NAME); // Check if earlier ID has changed #ifdef SV_WINDOWS VerifyVolumeStateChanges(vs); #endif bool haveput = CacheVolumesAndChecksum(vs); // Do not leave stale cache file if (!haveput) { std::string delErrMsg; if (!DeleteVolumesCacheFile(delErrMsg)) m_ErrMsg += delErrMsg; } DebugPrintf(SV_LOG_DEBUG, "EXITED %s\n", FUNCTION_NAME); return haveput; } /* * volumes Cache file format: * _______________ * |<checksum><\n>| * |<data> | * |______________| */ bool DataCacher::CacheVolumesAndChecksum(const VolumeSummaries_t &vs) { bool havecached = false; //Generate serialized volume summaries and its checksum std::stringstream serializedVs; serializedVs << CxArgObj<VolumeSummaries_t>(vs); std::string checksum = securitylib::genSha256Mac(serializedVs.str().c_str(), serializedVs.str().length()); VolumesCache vc; DataCacher::CACHE_STATE cs = GetVolumesFromCache(vc); if (DataCacher::E_CACHE_VALID == cs) { if (checksum == vc.m_Checksum) { DebugPrintf(SV_LOG_DEBUG, "The volume cache is not updated as the checksum is matching.\n"); return true; } } DebugPrintf(SV_LOG_DEBUG, "Updating volumes cache file %s with checksum: %s\n", m_VolumesCachePath.c_str(), checksum.c_str()); //Acquire write lock DebugPrintf(SV_LOG_DEBUG, "Acquiring write lock %s\n", m_VolumesCacheLockPath.c_str()); ACE_File_Lock lock(ACE_TEXT_CHAR_TO_TCHAR(m_VolumesCacheLockPath.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 volumes cache write lock ") + m_VolumesCacheLockPath + " 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_VolumesCachePath, mode, errMsg)) { DebugPrintf(SV_LOG_DEBUG, "opened cache file.\n"); havecached = InmFprintf(fp, m_VolumesCachePath, errMsg, false, 0, "%s%c%s", checksum.c_str(), CHECKSUM_SEPARATOR, serializedVs.str().c_str()); if (havecached) DebugPrintf(SV_LOG_DEBUG, "updated cache file.\n"); else m_ErrMsg = std::string("Failed to update volumes cache file ") + m_VolumesCachePath + " 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_VolumesCachePath, errMsg)) { std::string errormsg; if (!securitylib::setPermissions(m_VolumesCachePath, errormsg)) { DebugPrintf(SV_LOG_ERROR, "%s\n", errormsg.c_str()); } DebugPrintf(SV_LOG_DEBUG, "closed cache file.\n"); } else { havecached = false; m_ErrMsg += std::string("Failed to close handle of volumes cache file ") + m_VolumesCachePath + " with error " + errMsg + "."; } } else m_ErrMsg = std::string("Failed to open volumes cache file ") + m_VolumesCachePath + " with error " + errMsg + "."; return havecached; } DataCacher::CACHE_STATE DataCacher::GetVolumesFromCache(VolumesCache &vc) { CACHE_STATE cs = VerifyCacheAndGetVolumes(vc); // Clear cache if it is not valid if ((E_CACHE_CORRUPT == cs) || (E_CACHE_READ_FAILURE == cs)) { std::string delErrMsg; if (!DeleteVolumesCacheFile(delErrMsg)) m_ErrMsg += delErrMsg; } return cs; } DataCacher::CACHE_STATE DataCacher::VerifyCacheAndGetVolumes(VolumesCache &vc) { //Acquire read lock DebugPrintf(SV_LOG_DEBUG, "Getting volumes from volumes cache %s. Acquiring read lock %s\n", m_VolumesCachePath.c_str(), m_VolumesCacheLockPath.c_str()); ACE_File_Lock lock(ACE_TEXT_CHAR_TO_TCHAR(m_VolumesCacheLockPath.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 volumes cache read lock ") + m_VolumesCacheLockPath + " with error " + boost::lexical_cast<std::string>(saveErrNo) + "."; return E_CACHE_LOCK_FAILURE; } DebugPrintf(SV_LOG_DEBUG, "Acquired.\n"); //Open cache CACHE_STATE cs; std::string mode("r"); mode += FOPEN_MODE_NOINHERIT; FILE *fp = fopen(m_VolumesCachePath.c_str(), mode.c_str()); if (fp) { DebugPrintf(SV_LOG_DEBUG, "opened cache file.\n"); ON_BLOCK_EXIT(&fclose, fp); vc.m_MTime = GetVolumesCacheMtime(); cs = VerifyFileAndGetVolumes(fp, vc); } else { int saveErrNo = errno; cs = (ENOENT == saveErrNo) ? E_CACHE_DOES_NOT_EXIST : E_CACHE_READ_FAILURE; m_ErrMsg = std::string("Failed to open volumes cache file ") + m_VolumesCachePath + " with error " + boost::lexical_cast<std::string>(saveErrNo)+"."; } return cs; } DataCacher::CACHE_STATE DataCacher::VerifyFileAndGetVolumes(FILE *fp, VolumesCache &vc) { CACHE_STATE cs = E_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 volumes cache file ") + m_VolumesCachePath + "."; shouldproceed = false; } //Verify data if (data.empty()) { m_ErrMsg += std::string("Failed to find data in volumes cache file ") + m_VolumesCachePath + "."; shouldproceed = false; } //non empty checksum and data: calculate and compare checksum if (shouldproceed) cs = FillVolumesCacheIfDataValid(checksum, data, vc) ? E_CACHE_VALID : E_CACHE_CORRUPT; return cs; } bool DataCacher::FillVolumesCacheIfDataValid(const std::string &checksum, const std::string &data, VolumesCache &vc) { 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"); vc.m_Vs = unmarshal<VolumeSummaries_t>(data); isvalid = true; vc.m_Checksum = checksum; DebugPrintf(SV_LOG_DEBUG, "Successfully parsed volumes.\n"); } catch (std::string const& e) { m_ErrMsg = std::string("Failed to parse data to get volumes with error ") + e; } catch (char const* e) { m_ErrMsg = std::string("Failed to parse data to get volumes with error ") + e; } catch (ContextualException const& ce) { m_ErrMsg = std::string("Failed to parse data to get volumes with error ") + ce.what(); } catch (std::exception const& e) { m_ErrMsg = std::string("Failed to parse data to get volumes with error ") + e.what(); } catch (...) { m_ErrMsg = "Failed to parse data to get volumes failed"; } } else m_ErrMsg = "Checksum in file: " + checksum + ", does not match calculated checksum: " + newChecksum; if (!isvalid) m_ErrMsg += std::string(". volumes cache file ") + m_VolumesCachePath + " is corrupt."; return isvalid; } bool DataCacher::ClearVolumesCache(void) { std::string delErrMsg; bool havecleared = DeleteVolumesCacheFile(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 DataCacher::DeleteVolumesCacheFile(std::string &errMsg) { bool havedeleted = true; DebugPrintf(SV_LOG_DEBUG, "Clearing cache file %s.\n", m_VolumesCachePath.c_str()); //Dual check to avoid unnecessary lock in case lot of readers are trying to delete corrupt cache if (DoesVolumesCacheExist()) { //Acquire write lock DebugPrintf(SV_LOG_DEBUG, "Acquiring write lock %s\n", m_VolumesCacheLockPath.c_str()); ACE_File_Lock lock(ACE_TEXT_CHAR_TO_TCHAR(m_VolumesCacheLockPath.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 volumes cache write lock ") + m_VolumesCacheLockPath + " with error " + boost::lexical_cast<std::string>(saveErrNo)+"."; return false; } DebugPrintf(SV_LOG_DEBUG, "Acquired.\n"); //check again and clear if (DoesVolumesCacheExist()) { havedeleted = (0 == remove(m_VolumesCachePath.c_str())); if (havedeleted) DebugPrintf(SV_LOG_DEBUG, "Cleared volumes cache file %s.\n", m_VolumesCachePath.c_str()); else { int saveErrNo = errno; errMsg = std::string("Failed to remove volumes cache file ") + m_VolumesCachePath + " with error " + boost::lexical_cast<std::string>(saveErrNo) + "."; } } } return havedeleted; } bool DataCacher::DoesVolumesCacheExist(void) { ACE_stat st; bool isexisting = false; if (0 == sv_stat(m_VolumesCachePath.c_str(), &st)) { isexisting = true; DebugPrintf(SV_LOG_DEBUG, "volumes cache file %s exists.\n", m_VolumesCachePath.c_str()); } else DebugPrintf(SV_LOG_DEBUG, "volumes cache file %s does not exist.\n", m_VolumesCachePath.c_str()); return isexisting; } std::string DataCacher::GetErrMsg(void) { return m_ErrMsg; } time_t DataCacher::GetVolumesCacheMtime(SV_LOG_LEVEL logLevel) { ACE_stat st; time_t t = 0; if (0 == sv_stat(m_VolumesCachePath.c_str(), &st)) { t = st.st_mtime; std::stringstream ss; ss << "volumes cache file " << m_VolumesCachePath << " has mtime " << t; DebugPrintf(SV_LOG_DEBUG, "%s.\n", ss.str().c_str()); } else DebugPrintf(logLevel, "volumes cache file %s does not exist. Hence cannot find mtime.\n", m_VolumesCachePath.c_str()); return t; } std::string DataCacher::GetDeviceNameForDeviceSignature(std::string m_deviceSig) { std::string err; DataCacher::VolumesCache volumesCache; if (!DataCacher::UpdateVolumesCache(volumesCache, err)) { throw INMAGE_EX(err.c_str()); } ConstVolumeSummariesIter itrVS = volumesCache.m_Vs.begin(); for (/*empty*/; itrVS != volumesCache.m_Vs.end(); ++itrVS) { ConstAttributesIter_t cit = itrVS->attributes.find(NsVolumeAttributes::NAME_BASED_ON); if (cit != itrVS->attributes.end() && ((NsVolumeAttributes::SIGNATURE == cit->second) || (NsVolumeAttributes::SCSIHCTL == cit->second))) { if (itrVS->type == VolumeSummary::DISK && itrVS->name.compare(m_deviceSig) == 0) { if (itrVS->attributes.find("display_name") == itrVS->attributes.end()) { DebugPrintf(SV_LOG_ERROR, "Display name is not populated"); } else { return itrVS->attributes.at("display_name"); } } } } DebugPrintf(SV_LOG_DEBUG, "Could not find display name for device %s\n", m_deviceSig.c_str()); return m_deviceSig; }