/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2017 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "common/Fmd.hh" #include "common/StringConversion.hh" #include "common/LayoutId.hh" #include "common/StringTokenizer.hh" EOSCOMMONNAMESPACE_BEGIN //------------------------------------------------------------------------------ // Get set of known fsck error strings //------------------------------------------------------------------------------ std::set GetKnownFsckErrs() { return {FSCK_M_CX_DIFF, FSCK_M_MEM_SZ_DIFF, FSCK_D_CX_DIFF, FSCK_D_MEM_SZ_DIFF, FSCK_UNREG_N, FSCK_REP_DIFF_N, FSCK_REP_MISSING_N, FSCK_BLOCKXS_ERR, FSCK_ORPHANS_N, FSCK_STRIPE_ERR}; } //------------------------------------------------------------------------------ // Convert string to FsckErr type //------------------------------------------------------------------------------ FsckErr ConvertToFsckErr(const std::string& serr) { if (serr == FSCK_M_CX_DIFF) { return FsckErr::MgmXsDiff; } else if (serr == FSCK_M_MEM_SZ_DIFF) { return FsckErr::MgmSzDiff; } else if (serr == FSCK_D_CX_DIFF) { return FsckErr::FstXsDiff; } else if (serr == FSCK_D_MEM_SZ_DIFF) { return FsckErr::FstSzDiff; } else if (serr == FSCK_UNREG_N) { return FsckErr::UnregRepl; } else if (serr == FSCK_REP_DIFF_N) { return FsckErr::DiffRepl; } else if (serr == FSCK_REP_MISSING_N) { return FsckErr::MissRepl; } else if (serr == FSCK_BLOCKXS_ERR) { return FsckErr::BlockxsErr; } else if (serr == FSCK_ORPHANS_N) { return FsckErr::Orphans; } else if (serr == FSCK_STRIPE_ERR) { return FsckErr::StripeErr; } else { return FsckErr::None; } } //------------------------------------------------------------------------------ // Convert to FsckErr type to string //------------------------------------------------------------------------------ std::string FsckErrToString(const FsckErr& err) { if (err == FsckErr::MgmXsDiff) { return FSCK_M_CX_DIFF; } else if (err == FsckErr::MgmSzDiff) { return FSCK_M_MEM_SZ_DIFF; } else if (err == FsckErr::FstXsDiff) { return FSCK_D_CX_DIFF; } else if (err == FsckErr::FstSzDiff) { return FSCK_D_MEM_SZ_DIFF; } else if (err == FsckErr::UnregRepl) { return FSCK_UNREG_N; } else if (err == FsckErr::DiffRepl) { return FSCK_REP_DIFF_N; } else if (err == FsckErr::MissRepl) { return FSCK_REP_MISSING_N; } else if (err == FsckErr::BlockxsErr) { return FSCK_BLOCKXS_ERR; } else if (err == FsckErr::Orphans) { return FSCK_ORPHANS_N; } else if (err == FsckErr::StripeErr) { return FSCK_STRIPE_ERR; } else { return "none"; } } //------------------------------------------------------------------------------ // Convert an FST env representation to an Fmd struct //------------------------------------------------------------------------------ bool EnvToFstFmd(XrdOucEnv& env, FmdHelper& fmd) { // Check that all tags are present std::set tags {"id", "cid", "ctime", "ctime_ns", "mtime", "mtime_ns", "size", "lid", "uid", "gid"}; // "fsid", "disksize", "filecxerror", // "blockcxerror", "layouterror", "locations"}; for (const auto& tag : tags) { if (env.Get(tag.c_str()) == nullptr) { int envlen = 0; eos_static_crit("msg=\"missing fields in fmd encoding\" field=%s " "encoding=\"%s\"", tag.c_str(), env.Env(envlen)); return false; } } fmd.mProtoFmd.set_fid(strtoull(env.Get("id"), 0, 10)); fmd.mProtoFmd.set_cid(strtoull(env.Get("cid"), 0, 10)); fmd.mProtoFmd.set_ctime(strtoul(env.Get("ctime"), 0, 10)); fmd.mProtoFmd.set_ctime_ns(strtoul(env.Get("ctime_ns"), 0, 10)); fmd.mProtoFmd.set_mtime(strtoul(env.Get("mtime"), 0, 10)); fmd.mProtoFmd.set_mtime_ns(strtoul(env.Get("mtime_ns"), 0, 10)); fmd.mProtoFmd.set_size(strtoull(env.Get("size"), 0, 10)); fmd.mProtoFmd.set_lid(strtoul(env.Get("lid"), 0, 16)); fmd.mProtoFmd.set_uid((uid_t) strtoul(env.Get("uid"), 0, 10)); fmd.mProtoFmd.set_gid((gid_t) strtoul(env.Get("gid"), 0, 10)); if (env.Get("fsid")) { fmd.mProtoFmd.set_fsid(strtoull(env.Get("fsid"), 0, 10)); } if (env.Get("disksize")) { fmd.mProtoFmd.set_disksize(strtoull(env.Get("disksize"), 0, 10)); } if (env.Get("checksum")) { fmd.mProtoFmd.set_checksum(env.Get("checksum")); } if (env.Get("filecxerror")) { fmd.mProtoFmd.set_filecxerror(strtoul(env.Get("filecxerror"), 0, 16)); } if (env.Get("blockcxerror")) { fmd.mProtoFmd.set_blockcxerror(strtoul(env.Get("blockcxerror"), 0, 16)); } if (env.Get("layouterror")) { fmd.mProtoFmd.set_layouterror(strtoul(env.Get("layouterror"), 0, 16)); } if (fmd.mProtoFmd.checksum() == "none") { fmd.mProtoFmd.set_checksum(""); } if (env.Get("diskchecksum")) { fmd.mProtoFmd.set_diskchecksum(env.Get("diskchecksum")); } if (fmd.mProtoFmd.diskchecksum() == "none") { fmd.mProtoFmd.set_diskchecksum(""); } if (env.Get("mgmchecksum")) { fmd.mProtoFmd.set_mgmchecksum(env.Get("mgmchecksum")); } if (fmd.mProtoFmd.mgmchecksum() == "none") { fmd.mProtoFmd.set_mgmchecksum(""); } if (env.Get("locations")) { fmd.mProtoFmd.set_locations(env.Get("locations")); } if (fmd.mProtoFmd.locations() == "none") { fmd.mProtoFmd.set_locations(""); } if (env.Get("stripeerror")) { fmd.mProtoFmd.clear_stripeerror(); if (std::string(env.Get("stripeerror")) != "none") { std::vector stripeErrToken; eos::common::StringConversion::Tokenize(env.Get("stripeerror"), stripeErrToken, ","); for (const auto& id : stripeErrToken) { try { fmd.mProtoFmd.add_stripeerror(std::stol(id)); } catch (...) { eos_static_err("msg=\"skip non-numeric stripe error\" value=\"%s\"", id.c_str()); } } } } return true; } //------------------------------------------------------------------------------ // Populate data structures with any inconsistencies deteced while inspecting // the FmdHelper object //------------------------------------------------------------------------------ void CollectInconsistencies(const FmdHelper& fmd, const eos::common::FileSystem::fsid_t fsid, FsckErrsPerFsMap& errs_map) { auto& proto_fmd = fmd.mProtoFmd; if (proto_fmd.blockcxerror()) { errs_map["blockxs_err"][fsid].insert(proto_fmd.fid()); } if (proto_fmd.layouterror()) { if (proto_fmd.layouterror() & LayoutId::kOrphan) { errs_map["orphans_n"][fsid].insert(proto_fmd.fid()); } if (proto_fmd.layouterror() & LayoutId::kUnregistered) { errs_map["unreg_n"][fsid].insert(proto_fmd.fid()); } if (proto_fmd.layouterror() & LayoutId::kReplicaWrong) { errs_map["rep_diff_n"][fsid].insert(proto_fmd.fid()); } if (proto_fmd.layouterror() & LayoutId::kMissing) { errs_map["rep_missing_n"][fsid].insert(proto_fmd.fid()); } } if (proto_fmd.mgmsize() != eos::common::FmdHelper::UNDEF) { if (proto_fmd.size() != eos::common::FmdHelper::UNDEF) { // Report missmatch only for non-rain layout files if (!LayoutId::IsRain(proto_fmd.lid()) && proto_fmd.size() != proto_fmd.mgmsize()) { errs_map["m_mem_sz_diff"][fsid].insert(proto_fmd.fid()); } } else { // RAIN stripes with mgmsize != 0 and disksize == 0 are broken if (LayoutId::IsRain(proto_fmd.lid())) { if (proto_fmd.mgmsize() && (proto_fmd.disksize() == 0)) { errs_map["d_mem_sz_diff"][fsid].insert(proto_fmd.fid()); } } } } if (proto_fmd.disksize() != eos::common::FmdHelper::UNDEF) { if (proto_fmd.size() != eos::common::FmdHelper::UNDEF) { if (LayoutId::IsRain(proto_fmd.lid())) { if (proto_fmd.disksize() != LayoutId::ExpectedStripeSize(proto_fmd.lid(), proto_fmd.size())) { errs_map["d_mem_sz_diff"][fsid].insert(proto_fmd.fid()); } } else { if (proto_fmd.size() != proto_fmd.disksize()) { errs_map["d_mem_sz_diff"][fsid].insert(proto_fmd.fid()); } } } } if (!proto_fmd.layouterror()) { if (!LayoutId::IsRain(proto_fmd.lid())) { if (proto_fmd.size() && (proto_fmd.size() != eos::common::FmdHelper::UNDEF) && proto_fmd.diskchecksum().length() && (proto_fmd.diskchecksum() != proto_fmd.checksum())) { errs_map["d_cx_diff"][fsid].insert(proto_fmd.fid()); } if (proto_fmd.size() && (proto_fmd.size() != eos::common::FmdHelper::UNDEF) && proto_fmd.mgmchecksum().length() && (proto_fmd.mgmchecksum() != proto_fmd.checksum())) { errs_map["m_cx_diff"][fsid].insert(proto_fmd.fid()); } } } if (LayoutId::IsRain(proto_fmd.lid())) { for (auto efsid : proto_fmd.stripeerror()) { errs_map["stripe_err"][efsid].insert(proto_fmd.fid()); } } } //------------------------------------------------------------------------------ // Compute layout error //------------------------------------------------------------------------------ int FmdHelper::LayoutError(eos::common::FileSystem::fsid_t fsid) { uint32_t lid = mProtoFmd.lid(); if (lid == 0) { // An orphan has no lid at the MGM e.g. lid=0 return eos::common::LayoutId::kOrphan; } auto location_set = GetLocations(); size_t nstripes = eos::common::LayoutId::GetStripeNumber(lid) + 1; int lerror = 0; if (nstripes != location_set.size()) { lerror |= eos::common::LayoutId::kReplicaWrong; } if (!location_set.count(fsid)) { lerror |= eos::common::LayoutId::kUnregistered; } return lerror; } //--------------------------------------------------------------------------- // Reset file meta data object //--------------------------------------------------------------------------- void FmdHelper::Reset() { mProtoFmd.set_fid(0); mProtoFmd.set_cid(0); mProtoFmd.set_ctime(0); mProtoFmd.set_ctime_ns(0); mProtoFmd.set_mtime(0); mProtoFmd.set_mtime_ns(0); mProtoFmd.set_atime(0); mProtoFmd.set_atime_ns(0); mProtoFmd.set_checktime(0); mProtoFmd.set_size(UNDEF); mProtoFmd.set_disksize(UNDEF); mProtoFmd.set_mgmsize(UNDEF); mProtoFmd.set_checksum(""); mProtoFmd.set_diskchecksum(""); mProtoFmd.set_mgmchecksum(""); mProtoFmd.set_lid(0); mProtoFmd.set_uid(0); mProtoFmd.set_gid(0); mProtoFmd.set_filecxerror(0); mProtoFmd.set_blockcxerror(0); mProtoFmd.set_layouterror(0); mProtoFmd.set_locations(""); mProtoFmd.clear_stripeerror(); } //------------------------------------------------------------------------------ // Get set of valid (not unlinked) locations for the given fmd //------------------------------------------------------------------------------ std::set FmdHelper::GetLocations() const { std::vector location_vector; eos::common::StringConversion::Tokenize(mProtoFmd.locations(), location_vector, ","); std::set location_set; for (size_t i = 0; i < location_vector.size(); i++) { if (location_vector[i].length()) { // Exclude unlinked locations i.e. they have a ! in front if (location_vector[i][0] != '!') { location_set.insert(strtoul(location_vector[i].c_str(), 0, 10)); } } } return location_set; } //------------------------------------------------------------------------------- // Convert fmd object to env representation //------------------------------------------------------------------------------- std::unique_ptr FmdHelper::FmdToEnv() { std::ostringstream oss; oss << "id=" << mProtoFmd.fid() << "&cid=" << mProtoFmd.cid() << "&fsid=" << mProtoFmd.fsid() << "&ctime=" << mProtoFmd.ctime() << "&ctime_ns=" << mProtoFmd.ctime_ns() << "&mtime=" << mProtoFmd.mtime() << "&mtime_ns=" << mProtoFmd.mtime_ns() << "&atime=" << mProtoFmd.atime() << "&atime_ns=" << mProtoFmd.atime_ns() << "&size=" << mProtoFmd.size() << "&disksize=" << mProtoFmd.disksize() << "&mgmsize=" << mProtoFmd.mgmsize() << "&lid=0x" << std::hex << mProtoFmd.lid() << std::dec << "&uid=" << mProtoFmd.uid() << "&gid=" << mProtoFmd.gid() << "&filecxerror=0x" << std::hex << mProtoFmd.filecxerror() << "&blockcxerror=0x" << mProtoFmd.blockcxerror() << "&layouterror=0x" << mProtoFmd.layouterror(); // Take care at string fields since XrdOucEnv does not deal well with empty // values if (mProtoFmd.checksum().empty()) { oss << "&checksum=none"; } else { oss << "&checksum=" << mProtoFmd.checksum(); } if (mProtoFmd.diskchecksum().empty()) { oss << "&diskchecksum=none"; } else { oss << "&diskchecksum=" << mProtoFmd.diskchecksum(); } if (mProtoFmd.mgmchecksum().empty()) { oss << "&mgmchecksum=none"; } else { oss << "&mgmchecksum=" << mProtoFmd.mgmchecksum(); } if (mProtoFmd.locations().empty()) { oss << "&locations=none"; } else { oss << "&locations=" << std::dec << mProtoFmd.locations(); } if (mProtoFmd.stripeerror_size() == 0) { oss << "&stripeerror=none"; } else { oss << "&stripeerror=" << eos::common::StringTokenizer::merge( mProtoFmd.stripeerror(), ','); } oss << '&'; return std::unique_ptr (new XrdOucEnv(oss.str().c_str())); } EOSCOMMONNAMESPACE_END