// ---------------------------------------------------------------------- // File: Quota.cc // Author: Andreas-Joachim Peters - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 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 "mgm/Quota.hh" #include "mgm/Policy.hh" #include "mgm/XrdMgmOfs.hh" #include "common/table_formatter/TableFormatterBase.hh" #include "namespace/interface/IView.hh" #include "namespace/ns_quarkdb/NamespaceGroup.hh" #include "namespace/ns_quarkdb/flusher/MetadataFlusher.hh" #include "mgm/config/IConfigEngine.hh" #include EOSMGMNAMESPACE_BEGIN std::map Quota::pMapQuota; std::map Quota::pMapInodeQuota; eos::common::RWMutex Quota::pMapMutex; gid_t Quota::gProjectId = 99; #ifdef __APPLE__ #define ENONET 64 #endif //------------------------------------------------------------------------------ // *** Class SpaceQuota implementaion *** //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Constructor - requires the eosViewRWMutex write-lock //------------------------------------------------------------------------------ SpaceQuota::SpaceQuota(const char* path): eos::common::LogId(), pPath(path), mQuotaNode(nullptr), mLastEnableCheck(0), mLastRefresh(0), mLayoutSizeFactor(1.0), mDirtyTarget(true) { std::shared_ptr quotadir; try { quotadir = gOFS->eosView->getContainer(path); } catch (const eos::MDException& e) { eos_err("No such path=%s", path); } if (quotadir == nullptr) { try { quotadir = gOFS->eosView->createContainer(path, true); quotadir->setMode(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_IFDIR); gOFS->eosView->updateContainerStore(quotadir.get()); } catch (eos::MDException& e) { eos_crit("Cannot create quota directory %s", path); throw; } } if (quotadir) { try { mQuotaNode = gOFS->eosView->getQuotaNode(quotadir.get(), false); if (mQuotaNode) { eos_info("Found ns quota node for path=%s", path); } else { eos_info("No ns quota found for path=%s", path); } } catch (const eos::MDException& e) { mQuotaNode = nullptr; } if (!mQuotaNode) { try { mQuotaNode = gOFS->eosView->registerQuotaNode(quotadir.get()); } catch (eos::MDException& e) { mQuotaNode = nullptr; eos_crit("Cannot register quota node %s, errmsg=%s", path, e.what()); throw; } } } else { eos_crit("Failed to create quota dir=%s", path); } } //------------------------------------------------------------------------------ // Get quota status //------------------------------------------------------------------------------ const char* SpaceQuota::GetQuotaStatus(unsigned long long is, unsigned long long avail) { if (!avail) { return "ignored"; } double p = (100.0 * is / avail); if (p < 90) { return "ok"; } else if (p < 99) { return "warning"; } else { return "exceeded"; } } //------------------------------------------------------------------------------ // Get current quota value as percentage of the available one //------------------------------------------------------------------------------ const float SpaceQuota::GetQuotaPercentage(unsigned long long is, unsigned long long avail) { float fp = avail ? (100.0 * is / avail) : 100.0; if (fp > 100.0) { fp = 100.0; } if (fp < 0) { fp = 0; } return fp; } //------------------------------------------------------------------------------ // Update ns quota node address referred to by current space quota //------------------------------------------------------------------------------ bool SpaceQuota::UpdateQuotaNodeAddress() { try { std::shared_ptr quotadir = gOFS->eosView->getContainer(pPath.c_str()); mQuotaNode = gOFS->eosView->getQuotaNode(quotadir.get(), false); if (!mQuotaNode) { return false; } } catch (eos::MDException& e) { mQuotaNode = nullptr; return false; } return true; } //------------------------------------------------------------------------------ // Calculate the size factor used to estimate the logical available bytes //------------------------------------------------------------------------------ void SpaceQuota::UpdateLogicalSizeFactor() { XrdOucErrInfo error; eos::common::VirtualIdentity vid = eos::common::VirtualIdentity::Root(); vid.sudoer = 1; eos::IContainerMD::XAttrMap map; int retc = gOFS->_attr_ls(pPath.c_str(), error, vid, 0, map); if (!retc) { unsigned long layoutId; XrdOucEnv env; unsigned long forcedfsid; long forcedgroup; XrdOucString spn = pPath.c_str(); std::string bandwidth; bool schedule = false; std::string iopriority; std::string iotype; // get the layout in this quota node Policy::GetLayoutAndSpace(pPath.c_str(), map, vid, layoutId, spn, env, forcedfsid, forcedgroup, bandwidth, schedule, iopriority, iotype, false); mLayoutSizeFactor = eos::common::LayoutId::GetSizeFactor(layoutId); } else { mLayoutSizeFactor = 1.0; } // Protect for division by 0 if (mLayoutSizeFactor < 1.0) { mLayoutSizeFactor = 1.0; } } //------------------------------------------------------------------------------ // Remove quota //------------------------------------------------------------------------------ bool SpaceQuota::RmQuota(unsigned long tag, unsigned long id) { eos_debug("rm quota tag=%lu id=%lu", tag, id); XrdSysMutexHelper scope_lock(mMutex); if (mMapIdQuota.count(Index(tag, id))) { mMapIdQuota.erase(Index(tag, id)); mDirtyTarget = true; return true; } return false; } //------------------------------------------------------------------------------ // Get quota value //------------------------------------------------------------------------------ long long SpaceQuota::GetQuota(unsigned long tag, unsigned long id) { XrdSysMutexHelper scope_lock(mMutex); auto it = mMapIdQuota.find(Index(tag, id)); if (it != mMapIdQuota.end()) { return static_cast(it->second); } return 0; } //------------------------------------------------------------------------------ // Set quota //------------------------------------------------------------------------------ void SpaceQuota::SetQuota(unsigned long tag, unsigned long id, unsigned long long value) { eos_debug("set quota tag=%lu id=%lu value=%llu", tag, id, value); XrdSysMutexHelper scope_lock(mMutex); mMapIdQuota[Index(tag, id)] = value; if ((tag == kUserBytesTarget) || (tag == kGroupBytesTarget) || (tag == kUserFilesTarget) || (tag == kGroupFilesTarget) || (tag == kUserLogicalBytesTarget) || (tag == kGroupLogicalBytesTarget)) { mDirtyTarget = true; } } //------------------------------------------------------------------------------ // Reset quota //------------------------------------------------------------------------------ void SpaceQuota::ResetQuota(unsigned long tag, unsigned long id) { mMapIdQuota[Index(tag, id)] = 0; if ((tag == kUserBytesTarget) || (tag == kGroupBytesTarget) || (tag == kUserFilesTarget) || (tag == kGroupFilesTarget) || (tag == kUserLogicalBytesTarget) || (tag == kGroupLogicalBytesTarget)) { mDirtyTarget = true; } } //------------------------------------------------------------------------------ // Add quota //------------------------------------------------------------------------------ void SpaceQuota::AddQuota(unsigned long tag, unsigned long id, long long value) { eos_debug("add quota tag=%lu id=%lu value=%llu", tag, id, value); // Avoid negative numbers if (((long long) mMapIdQuota[Index(tag, id)] + value) >= 0) { mMapIdQuota[Index(tag, id)] += value; } eos_debug("sum quota tag=%lu id=%lu value=%llu", tag, id, mMapIdQuota[Index(tag, id)]); } //------------------------------------------------------------------------------ // Update //------------------------------------------------------------------------------ void SpaceQuota::UpdateTargetSums() { if (!mDirtyTarget) { return; } eos_debug("updating targets"); XrdSysMutexHelper scope_lock(mMutex); mDirtyTarget = false; mMapIdQuota[Index(kAllUserBytesTarget, 0)] = 0; mMapIdQuota[Index(kAllUserFilesTarget, 0)] = 0; mMapIdQuota[Index(kAllGroupBytesTarget, 0)] = 0; mMapIdQuota[Index(kAllGroupFilesTarget, 0)] = 0; mMapIdQuota[Index(kAllUserLogicalBytesTarget, 0)] = 0; mMapIdQuota[Index(kAllGroupLogicalBytesTarget, 0)] = 0; for (auto it = mMapIdQuota.begin(); it != mMapIdQuota.end(); it++) { if ((UnIndex(it->first) == kUserBytesTarget)) { AddQuota(kAllUserBytesTarget, 0, it->second); AddQuota(kAllUserLogicalBytesTarget, 0, it->second / mLayoutSizeFactor); } if ((UnIndex(it->first) == kUserFilesTarget)) { AddQuota(kAllUserFilesTarget, 0, it->second); } if ((UnIndex(it->first) == kGroupBytesTarget)) { AddQuota(kAllGroupBytesTarget, 0, it->second); AddQuota(kAllGroupLogicalBytesTarget, 0, it->second / mLayoutSizeFactor); } if ((UnIndex(it->first) == kGroupFilesTarget)) { AddQuota(kAllGroupFilesTarget, 0, it->second); } } } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void SpaceQuota::UpdateIsSums() { eos_debug("updating IS values"); XrdSysMutexHelper scope_lock(mMutex); mMapIdQuota[Index(kAllUserBytesIs, 0)] = 0; mMapIdQuota[Index(kAllUserLogicalBytesIs, 0)] = 0; mMapIdQuota[Index(kAllUserFilesIs, 0)] = 0; mMapIdQuota[Index(kAllGroupBytesIs, 0)] = 0; mMapIdQuota[Index(kAllGroupFilesIs, 0)] = 0; mMapIdQuota[Index(kAllGroupLogicalBytesIs, 0)] = 0; bool has_project_quota = false; auto it = mMapIdQuota.find(Index(kGroupLogicalBytesTarget, Quota::gProjectId)); if ((it != mMapIdQuota.end()) && it->second) { has_project_quota = true; } // If project quota is defined for the current quota node then use that // value to avoid possible double counting if (has_project_quota) { AddQuota(kAllGroupFilesIs, 0, mMapIdQuota[Index(kGroupFilesIs, Quota::gProjectId)]); AddQuota(kAllGroupBytesIs, 0, mMapIdQuota[Index(kGroupBytesIs, Quota::gProjectId)]); AddQuota(kAllGroupLogicalBytesIs, 0, mMapIdQuota[Index(kGroupLogicalBytesIs, Quota::gProjectId)]); } else { for (auto it = mMapIdQuota.begin(); it != mMapIdQuota.end(); it++) { if ((UnIndex(it->first) == kUserBytesIs)) { AddQuota(kAllUserBytesIs, 0, it->second); } if ((UnIndex(it->first) == kUserLogicalBytesIs)) { AddQuota(kAllUserLogicalBytesIs, 0, it->second); } if ((UnIndex(it->first) == kUserFilesIs)) { AddQuota(kAllUserFilesIs, 0, it->second); } if ((UnIndex(it->first) == kGroupFilesIs)) { AddQuota(kAllGroupFilesIs, 0, it->second); } if ((UnIndex(it->first) == kGroupBytesIs)) { AddQuota(kAllGroupBytesIs, 0, it->second); } if ((UnIndex(it->first) == kGroupLogicalBytesIs)) { AddQuota(kAllGroupLogicalBytesIs, 0, it->second); } } } } //------------------------------------------------------------------------------ // Update uid/gid values from quota node //------------------------------------------------------------------------------ void SpaceQuota::UpdateFromQuotaNode(uid_t uid, gid_t gid, bool upd_proj_quota) { eos_debug("updating uid/gid values from quota node"); XrdSysMutexHelper scope_lock(mMutex); if (mQuotaNode) { mMapIdQuota[Index(kUserBytesIs, uid)] = 0; mMapIdQuota[Index(kUserLogicalBytesIs, uid)] = 0; mMapIdQuota[Index(kUserFilesIs, uid)] = 0; mMapIdQuota[Index(kGroupBytesIs, gid)] = 0; mMapIdQuota[Index(kGroupFilesIs, gid)] = 0; mMapIdQuota[Index(kGroupLogicalBytesIs, gid)] = 0; AddQuota(kUserBytesIs, uid, mQuotaNode->getPhysicalSpaceByUser(uid)); AddQuota(kUserLogicalBytesIs, uid, mQuotaNode->getUsedSpaceByUser(uid)); AddQuota(kUserFilesIs, uid, mQuotaNode->getNumFilesByUser(uid)); AddQuota(kGroupBytesIs, gid, mQuotaNode->getPhysicalSpaceByGroup(gid)); AddQuota(kGroupLogicalBytesIs, gid, mQuotaNode->getUsedSpaceByGroup(gid)); AddQuota(kGroupFilesIs, gid, mQuotaNode->getNumFilesByGroup(gid)); mMapIdQuota[Index(kUserBytesIs, Quota::gProjectId)] = 0; mMapIdQuota[Index(kUserLogicalBytesIs, Quota::gProjectId)] = 0; mMapIdQuota[Index(kUserFilesIs, Quota::gProjectId)] = 0; mMapIdQuota[Index(kUserLogicalBytesTarget, uid)] = mMapIdQuota[Index(kUserBytesTarget, uid)] / mLayoutSizeFactor; mMapIdQuota[Index(kGroupLogicalBytesTarget, gid)] = mMapIdQuota[Index(kGroupBytesTarget, gid)] / mLayoutSizeFactor; if (upd_proj_quota) { // Recalculate the project quota only every 5 seconds to boost perf. static XrdSysMutex lMutex; static time_t lUpdateTime = 0; bool docalc = false; { XrdSysMutexHelper lock(lMutex); time_t now = time(NULL); if (lUpdateTime < now) { // Next recalculation in 5 second docalc = true; lUpdateTime = now + 5; } } if (docalc || (gid == Quota::gProjectId)) { mMapIdQuota[Index(kGroupBytesIs, Quota::gProjectId)] = 0; mMapIdQuota[Index(kGroupFilesIs, Quota::gProjectId)] = 0; mMapIdQuota[Index(kGroupLogicalBytesIs, Quota::gProjectId)] = 0; // update logical target mMapIdQuota[Index(kGroupLogicalBytesTarget, Quota::gProjectId)] = mMapIdQuota[Index(kGroupBytesTarget, Quota::gProjectId)] / mLayoutSizeFactor; // Loop over users and fill project quota auto uids = mQuotaNode->getUids(); for (auto itu = uids.begin(); itu != uids.end(); ++itu) { AddQuota(kGroupBytesIs, Quota::gProjectId, mQuotaNode->getPhysicalSpaceByUser(*itu)); AddQuota(kGroupLogicalBytesIs, Quota::gProjectId, mQuotaNode->getUsedSpaceByUser(*itu)); AddQuota(kGroupFilesIs, Quota::gProjectId, mQuotaNode->getNumFilesByUser(*itu)); } } } } } //------------------------------------------------------------------------------ // Refresh counters //------------------------------------------------------------------------------ void SpaceQuota::Refresh(time_t age) { time_t now = time(NULL); // since this loads all quota node info all the time, we don't do this for GetIndividualQuota in realtime all the time if (age) { if ((now - age) < mLastRefresh) { return; } } mLastRefresh = now; AccountNsToSpace(); UpdateLogicalSizeFactor(); UpdateIsSums(); UpdateTargetSums(); } //------------------------------------------------------------------------------ // Print quota information //------------------------------------------------------------------------------ void SpaceQuota::PrintOut(XrdOucString& output, long long int uid_sel, long long int gid_sel, bool monitoring, bool translate_ids) { using eos::common::StringConversion; // Make a map containing once all the defined uid's and gid's std::vector> uids, gids; { XrdSysMutexHelper scope_lock(mMutex); // For project space we just print the user/group entry gProjectId if (mMapIdQuota[Index(kGroupBytesTarget, Quota::gProjectId)] > 0) { gid_sel = Quota::gProjectId; } for (auto it = mMapIdQuota.begin(); it != mMapIdQuota.end(); ++it) { if ((UnIndex(it->first) >= kUserBytesIs) && (UnIndex(it->first) <= kUserFilesTarget)) { long long int uid = (long long int)((it->first) & 0xffffffff); // uid selection filter if ((uid_sel >= 0LL) && (uid != uid_sel)) { continue; } // we don't print the users if a gid is selected if (gid_sel >= 0LL) { continue; } // Translate IDs std::string name = std::to_string(uid).c_str(); uids.push_back(std::make_pair(name, uid)); } if ((UnIndex(it->first) >= kGroupBytesIs) && (UnIndex(it->first) <= kGroupFilesTarget)) { long long int gid = (it->first) & 0xfffffff; // uid selection filter if ((gid_sel >= 0LL) && (gid != gid_sel)) { continue; } // We don't print the group if a uid is selected if (uid_sel >= 0LL) { continue; } // Translate IDs std::string name = std::to_string(gid).c_str(); gids.push_back(std::make_pair(name, gid)); } } } // translate ids without mutex held if (translate_ids) { std::string name; std::vector> tuids, tgids; for (auto u:uids) { if (gid_sel == Quota::gProjectId) { name = "project"; } else { int errc = 0; name = eos::common::Mapping::UidToUserName(u.second, errc); } tuids.push_back(std::make_pair(name,u.second)); } for (auto g:gids) { if (gid_sel == Quota::gProjectId) { name = "project"; } else { int errc = 0; name = eos::common::Mapping::GidToGroupName(g.second, errc); } tgids.push_back(std::make_pair(name,g.second)); } uids=tuids; gids=tgids; } // Sort and erase duplicated uids and gids eos_info("uids_size=%i, gids_size=%i", uids.size(), gids.size()); std::sort(uids.begin(), uids.end()); uids.erase(std::unique(uids.begin(), uids.end()), uids.end()); std::sort(gids.begin(), gids.end()); gids.erase(std::unique(gids.begin(), gids.end()), gids.end()); eos_debug("sorted"); for (unsigned int i = 0; i < uids.size(); ++i) { eos_debug("sort %d %d", i, uids[i].second); } for (unsigned int i = 0; i < gids.size(); ++i) { eos_debug("sort %d %d", i, gids[i].second); } // Print the header for selected uid/gid's only if there is something to print // If we have a full listing we print even empty quota nodes (=header only) if (!monitoring) { if (((uid_sel < 0) && (gid_sel < 0)) || !uids.empty() || !gids.empty()) { output += "\nā”ā”> Quota Node: "; output += pPath.c_str(); output += "\n"; } } //! Quota node - Users TableFormatterBase table_user; if (!uids.empty()) { // Table header if (!monitoring) { table_user.SetHeader({ std::make_tuple(GetTagCategory(kUserBytesIs), 10, "-s"), std::make_tuple(GetTagName(kUserBytesIs), 10, "+l"), std::make_tuple(GetTagName(kUserLogicalBytesIs), 10, "+l"), std::make_tuple(GetTagName(kUserFilesIs), 10, "+l"), std::make_tuple(GetTagName(kUserBytesTarget), 10, "+l"), std::make_tuple(GetTagName(kUserLogicalBytesTarget), 10, "+l"), std::make_tuple(GetTagName(kUserFilesTarget), 10, "+l"), std::make_tuple("filled[%]", 10, "f"), std::make_tuple("vol-status", 10, "s"), std::make_tuple("ino-status", 10, "s") }); } else { table_user.SetHeader({ std::make_tuple("quota", 0, "os"), std::make_tuple("uid", 0, "os"), std::make_tuple("space", 0, "os"), std::make_tuple("usedbytes", 0, "ol"), std::make_tuple("usedlogicalbytes", 0, "ol"), std::make_tuple("usedfiles", 0, "ol"), std::make_tuple("maxbytes", 0, "ol"), std::make_tuple("maxlogicalbytes", 0, "ol"), std::make_tuple("maxfiles", 0, "ol"), std::make_tuple("percentageusedbytes", 0, "of"), std::make_tuple("statusbytes", 0, "os"), std::make_tuple("statusfiles", 0, "os") }); } for (auto it : uids) { std::string name = it.first.c_str(); unsigned int id = it.second; eos_debug("loop with id=%d", id); TableData table_data; table_data.emplace_back(); if (!monitoring) { table_data.back().push_back(TableCell(name.c_str(), "-s")); table_data.back().push_back(TableCell( GetQuota(kUserBytesIs, id), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kUserLogicalBytesIs, id), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kUserFilesIs, id), "+l")); table_data.back().push_back(TableCell( GetQuota(kUserBytesTarget, id), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kUserBytesTarget, id) / mLayoutSizeFactor, "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kUserFilesTarget, id), "+l")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kUserBytesIs, id), GetQuota(kUserBytesTarget, id)), "f", "%")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kUserBytesIs, id), GetQuota(kUserBytesTarget, id)), "s")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kUserFilesIs, id), GetQuota(kUserFilesTarget, id)), "s")); } else { table_data.back().push_back(TableCell("node", "os")); table_data.back().push_back(TableCell(name.c_str(), "os")); table_data.back().push_back(TableCell(pPath.c_str(), "os")); table_data.back().push_back(TableCell( GetQuota(kUserBytesIs, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kUserLogicalBytesIs, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kUserFilesIs, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kUserBytesTarget, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kUserBytesTarget, id) / mLayoutSizeFactor, "ol")); table_data.back().push_back(TableCell( GetQuota(kUserFilesTarget, id), "ol")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kUserBytesIs, id), GetQuota(kUserBytesTarget, id)), "of")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kUserBytesIs, id), GetQuota(kUserBytesTarget, id)), "os")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kUserFilesIs, id), GetQuota(kUserFilesTarget, id)), "os")); } table_user.AddRows(table_data); } } if ((uid_sel < 0) && (gid_sel < 0)) { output += table_user.GenerateTable(HEADER).c_str(); } else { output += table_user.GenerateTable(HEADER2).c_str(); } //! Quota node - Group TableFormatterBase table_group; if (!gids.empty()) { // group loop if (!monitoring) { table_group.SetHeader({ std::make_tuple(GetTagCategory(kGroupBytesIs), 10, "-s"), std::make_tuple(GetTagName(kGroupBytesIs), 10, "+l"), std::make_tuple(GetTagName(kGroupLogicalBytesIs), 10, "+l"), std::make_tuple(GetTagName(kGroupFilesIs), 10, "+l"), std::make_tuple(GetTagName(kGroupBytesTarget), 10, "+l"), std::make_tuple(GetTagName(kGroupLogicalBytesTarget), 10, "+l"), std::make_tuple(GetTagName(kGroupFilesTarget), 10, "+l"), std::make_tuple("filled[%]", 10, "f"), std::make_tuple("vol-status", 10, "s"), std::make_tuple("ino-status", 10, "s") }); } else { table_group.SetHeader({ std::make_tuple("quota", 0, "os"), std::make_tuple("gid", 0, "os"), std::make_tuple("space", 0, "os"), std::make_tuple("usedbytes", 0, "ol"), std::make_tuple("usedlogicalbytes", 0, "ol"), std::make_tuple("usedfiles", 0, "ol"), std::make_tuple("maxbytes", 0, "ol"), std::make_tuple("maxlogicalbytes", 0, "ol"), std::make_tuple("maxfiles", 0, "ol"), std::make_tuple("percentageusedbytes", 0, "of"), std::make_tuple("statusbytes", 0, "os"), std::make_tuple("statusfiles", 0, "os") }); } for (auto it : gids) { std::string name = it.first.c_str(); unsigned int id = it.second; eos_debug("loop with id=%d", id); TableData table_data; table_data.emplace_back(); if (!monitoring) { table_data.back().push_back(TableCell(name.c_str(), "-s")); table_data.back().push_back(TableCell( GetQuota(kGroupBytesIs, id), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kGroupLogicalBytesIs, id), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kGroupFilesIs, id), "+l")); table_data.back().push_back(TableCell( GetQuota(kGroupBytesTarget, id), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kGroupBytesTarget, id) / mLayoutSizeFactor, "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kGroupFilesTarget, id), "+l")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kGroupBytesIs, id), GetQuota(kGroupBytesTarget, id)), "f", "%")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kGroupBytesIs, id), GetQuota(kGroupBytesTarget, id)), "s")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kGroupFilesIs, id), GetQuota(kGroupFilesTarget, id)), "s")); } else { table_data.back().push_back(TableCell("node", "os")); table_data.back().push_back(TableCell(name.c_str(), "os")); table_data.back().push_back(TableCell(pPath.c_str(), "os")); table_data.back().push_back(TableCell( GetQuota(kGroupBytesIs, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kGroupLogicalBytesIs, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kGroupFilesIs, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kGroupBytesTarget, id), "ol")); table_data.back().push_back(TableCell( GetQuota(kGroupBytesTarget, id) / mLayoutSizeFactor, "ol")); table_data.back().push_back(TableCell( GetQuota(kGroupFilesTarget, id), "ol")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kGroupBytesIs, id), GetQuota(kGroupBytesTarget, id)), "of")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kGroupBytesIs, id), GetQuota(kGroupBytesTarget, id)), "os")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kGroupFilesIs, id), GetQuota(kGroupFilesTarget, id)), "os")); } table_group.AddRows(table_data); } } if ((uid_sel < 0) && (gid_sel < 0)) { output += table_group.GenerateTable(HEADER).c_str(); } else { output += table_group.GenerateTable(HEADER2).c_str(); } //! Quota node - Summary if ((uid_sel < 0) && (gid_sel < 0)) { if (!monitoring) { TableFormatterBase table_summary; table_summary.SetHeader({ std::make_tuple("summary", 10, "-s"), std::make_tuple(GetTagName(kAllUserBytesIs), 10, "+l"), std::make_tuple(GetTagName(kAllUserLogicalBytesIs), 10, "+l"), std::make_tuple(GetTagName(kAllUserFilesIs), 10, "+l"), std::make_tuple(GetTagName(kAllUserBytesTarget), 10, "+l"), std::make_tuple(GetTagName(kAllUserLogicalBytesTarget), 10, "+l"), std::make_tuple(GetTagName(kAllUserFilesTarget), 10, "+l"), std::make_tuple("filled[%]", 10, "f"), std::make_tuple("vol-status", 10, "s"), std::make_tuple("ino-status", 10, "s") }); TableData table_data; table_data.emplace_back(); table_data.back().push_back(TableCell("All users", "-s")); table_data.back().push_back(TableCell( GetQuota(kAllUserBytesIs, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllUserLogicalBytesIs, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllUserFilesIs, 0), "+l")); table_data.back().push_back(TableCell( GetQuota(kAllUserBytesTarget, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllUserLogicalBytesTarget, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllUserFilesTarget, 0), "+l")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kAllUserBytesIs, 0), GetQuota(kAllUserBytesTarget, 0)), "f", "%")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllUserBytesIs, 0), GetQuota(kAllUserBytesTarget, 0)), "s")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllUserFilesIs, 0), GetQuota(kAllUserFilesTarget, 0)), "s")); table_data.emplace_back(); table_data.back().push_back(TableCell("All groups", "-ls")); table_data.back().push_back(TableCell( GetQuota(kAllGroupBytesIs, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllGroupLogicalBytesIs, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllGroupFilesIs, 0), "+l")); table_data.back().push_back(TableCell( GetQuota(kAllGroupBytesTarget, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllGroupLogicalBytesTarget, 0), "+l", "B")); table_data.back().push_back(TableCell( GetQuota(kAllGroupFilesTarget, 0), "+l")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kAllGroupBytesIs, 0), GetQuota(kAllGroupBytesTarget, 0)), "f", "%")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllGroupBytesIs, 0), GetQuota(kAllGroupBytesTarget, 0)), "s")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllGroupFilesIs, 0), GetQuota(kAllGroupFilesTarget, 0)), "s")); table_summary.AddRows(table_data); output += table_summary.GenerateTable(HEADER2).c_str(); } else { TableFormatterBase table_summary_user; table_summary_user.SetHeader({ std::make_tuple("quota", 0, "os"), std::make_tuple("uid", 0, "os"), std::make_tuple("space", 0, "os"), std::make_tuple("usedbytes", 0, "ol"), std::make_tuple("usedlogicalbytes", 0, "ol"), std::make_tuple("usedfiles", 0, "ol"), std::make_tuple("maxbytes", 0, "ol"), std::make_tuple("maxlogicalbytes", 0, "ol"), std::make_tuple("maxfiles", 0, "ol"), std::make_tuple("percentageusedbytes", 0, "of"), std::make_tuple("statusbytes", 0, "os"), std::make_tuple("statusfiles", 0, "os") }); TableData table_data; table_data.emplace_back(); table_data.back().push_back(TableCell("node", "os")); table_data.back().push_back(TableCell("ALL", "os")); table_data.back().push_back(TableCell(pPath.c_str(), "os")); table_data.back().push_back(TableCell( GetQuota(kAllUserBytesIs, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllUserLogicalBytesIs, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllUserFilesIs, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllUserBytesTarget, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllUserLogicalBytesTarget, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllUserFilesTarget, 0), "ol")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kAllUserBytesIs, 0), GetQuota(kAllUserBytesTarget, 0)), "of")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllUserBytesIs, 0), GetQuota(kAllUserBytesTarget, 0)), "os")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllUserFilesIs, 0), GetQuota(kAllUserFilesTarget, 0)), "os")); table_summary_user.AddRows(table_data); output += table_summary_user.GenerateTable().c_str(); TableFormatterBase table_summary_group; table_summary_group.SetHeader({ std::make_tuple("quota", 0, "os"), std::make_tuple("gid", 0, "os"), std::make_tuple("space", 0, "os"), std::make_tuple("usedbytes", 0, "ol"), std::make_tuple("usedlogicalbytes", 0, "ol"), std::make_tuple("usedfiles", 0, "ol"), std::make_tuple("maxbytes", 0, "ol"), std::make_tuple("maxlogicalbytes", 0, "ol"), std::make_tuple("maxfiles", 0, "ol"), std::make_tuple("percentageusedbytes", 0, "of"), std::make_tuple("statusbytes", 0, "os"), std::make_tuple("statusfiles", 0, "os") }); table_data.clear(); table_data.emplace_back(); table_data.back().push_back(TableCell("node", "os")); table_data.back().push_back(TableCell("ALL", "os")); table_data.back().push_back(TableCell(pPath.c_str(), "os")); table_data.back().push_back(TableCell( GetQuota(kAllGroupBytesIs, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllGroupLogicalBytesIs, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllGroupFilesIs, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllGroupBytesTarget, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllGroupLogicalBytesTarget, 0), "ol")); table_data.back().push_back(TableCell( GetQuota(kAllGroupFilesTarget, 0), "ol")); table_data.back().push_back(TableCell(GetQuotaPercentage( GetQuota(kAllGroupBytesIs, 0), GetQuota(kAllGroupBytesTarget, 0)), "of")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllGroupBytesIs, 0), GetQuota(kAllGroupBytesTarget, 0)), "os")); table_data.back().push_back(TableCell(GetQuotaStatus( GetQuota(kAllGroupFilesIs, 0), GetQuota(kAllGroupFilesTarget, 0)), "os")); table_summary_group.AddRows(table_data); output += table_summary_group.GenerateTable().c_str(); } } } //------------------------------------------------------------------------------ // User/group/project quota checks. If both user and group quotas are defined, // then both need to be satisfied. //------------------------------------------------------------------------------ bool SpaceQuota::CheckWriteQuota(uid_t uid, gid_t gid, long long desired_vol, unsigned int inodes) { bool hasquota = false; // Update info from the ns quota node - user, group and project quotas UpdateFromQuotaNode(uid, gid, GetQuota(kGroupBytesTarget, Quota::gProjectId) ? true : false); eos_info("uid=%d gid=%d size=%llu quota=%llu", uid, gid, desired_vol, GetQuota(kUserBytesTarget, uid)); bool userquota = false; bool groupquota = false; bool projectquota = false; bool hasuserquota = false; bool hasgroupquota = false; bool hasprojectquota = false; bool uservolumequota = false; bool userinodequota = false; bool groupvolumequota = false; bool groupinodequota = false; if (GetQuota(kUserBytesTarget, uid) > 0) { userquota = true; uservolumequota = true; } if (GetQuota(kGroupBytesTarget, gid) > 0) { groupquota = true; groupvolumequota = true; } if (GetQuota(kUserFilesTarget, uid) > 0) { userquota = true; userinodequota = true; } if (GetQuota(kGroupFilesTarget, gid) > 0) { groupquota = true; groupinodequota = true; } if (uservolumequota) { if ((GetQuota(kUserBytesTarget, uid) - GetQuota(kUserBytesIs, uid)) > (long long)desired_vol) { hasuserquota = true; } else { hasuserquota = false; } } if (userinodequota) { // The +1 comes from the fact the the current file is already accounted to // the ns quota by doing ns_quota->addFile previously in the open function. if ((GetQuota(kUserFilesTarget, uid) - GetQuota(kUserFilesIs, uid) + 1) >= inodes) { if (!uservolumequota) { hasuserquota = true; } } else { hasuserquota = false; } } if (groupvolumequota) { if ((GetQuota(kGroupBytesTarget, gid) - GetQuota(kGroupBytesIs, gid)) > desired_vol) { hasgroupquota = true; } else { hasgroupquota = false; } } if (groupinodequota) { if ((GetQuota(kGroupFilesTarget, gid) - GetQuota(kGroupFilesIs, gid)) > inodes) { if (!groupvolumequota) { hasgroupquota = true; } } else { hasgroupquota = false; } } if (((GetQuota(kGroupBytesTarget, Quota::gProjectId) - GetQuota(kGroupBytesIs, Quota::gProjectId)) > desired_vol)) { hasprojectquota = true; if ((GetQuota(kGroupFilesTarget, Quota::gProjectId)) && ((GetQuota(kGroupFilesTarget, Quota::gProjectId) < (GetQuota(kGroupFilesIs, Quota::gProjectId) + inodes)))) { hasprojectquota = false; } } if (!userquota && !groupquota) { projectquota = true; } eos_info("userquota=%d groupquota=%d hasuserquota=%d hasgroupquota=%d " "userinodequota=%d uservolumequota=%d projectquota=%d " "hasprojectquota=%d", userquota, groupquota, hasuserquota, hasgroupquota, userinodequota, uservolumequota, projectquota, hasprojectquota); // If both quotas are defined we need to have both if (userquota && groupquota) { hasquota = hasuserquota & hasgroupquota; } else { hasquota = hasuserquota || hasgroupquota; } if (projectquota && hasprojectquota) { hasquota = true; } // Root does not need any quota if (uid == 0) { hasquota = true; } return hasquota; } //------------------------------------------------------------------------------ // Import ns quota values into current space quota //------------------------------------------------------------------------------ void SpaceQuota::AccountNsToSpace() { if (UpdateQuotaNodeAddress()) { XrdSysMutexHelper scope_lock(mMutex); // Insert current state of a single quota node into a SpaceQuota ResetQuota(kGroupBytesIs, Quota::gProjectId); ResetQuota(kGroupFilesIs, Quota::gProjectId); ResetQuota(kGroupLogicalBytesIs, Quota::gProjectId); // Loop over users auto uids = mQuotaNode->getUids(); for (auto itu = uids.begin(); itu != uids.end(); ++itu) { ResetQuota(kUserBytesIs, *itu); AddQuota(kUserBytesIs, *itu, mQuotaNode->getPhysicalSpaceByUser(*itu)); ResetQuota(kUserFilesIs, *itu); AddQuota(kUserFilesIs, *itu, mQuotaNode->getNumFilesByUser(*itu)); ResetQuota(kUserLogicalBytesIs, *itu); AddQuota(kUserLogicalBytesIs, *itu, mQuotaNode->getUsedSpaceByUser(*itu)); if (mMapIdQuota[Index(kGroupBytesTarget, Quota::gProjectId)] > 0) { // Only account in project quota nodes AddQuota(kGroupBytesIs, Quota::gProjectId, mQuotaNode->getPhysicalSpaceByUser(*itu)); AddQuota(kGroupLogicalBytesIs, Quota::gProjectId, mQuotaNode->getUsedSpaceByUser(*itu)); AddQuota(kGroupFilesIs, Quota::gProjectId, mQuotaNode->getNumFilesByUser(*itu)); } } auto gids = mQuotaNode->getGids(); for (auto itg = gids.begin(); itg != gids.end(); ++itg) { // Don't update the project quota directory from the quota if (*itg == Quota::gProjectId) { continue; } ResetQuota(kGroupBytesIs, *itg); AddQuota(kGroupBytesIs, *itg, mQuotaNode->getPhysicalSpaceByGroup(*itg)); ResetQuota(kGroupFilesIs, *itg); AddQuota(kGroupFilesIs, *itg, mQuotaNode->getNumFilesByGroup(*itg)); ResetQuota(kGroupLogicalBytesIs, *itg); AddQuota(kGroupLogicalBytesIs, *itg, mQuotaNode->getUsedSpaceByGroup(*itg)); } } } //------------------------------------------------------------------------------ // Convert int tag to string representation //------------------------------------------------------------------------------ const char* SpaceQuota::GetTagAsString(int tag) { if (tag == kUserBytesTarget) { return "userbytes"; } if (tag == kUserFilesTarget) { return "userfiles"; } if (tag == kGroupBytesTarget) { return "groupbytes"; } if (tag == kGroupFilesTarget) { return "groupfiles"; } if (tag == kAllUserBytesTarget) { return "alluserbytes"; } if (tag == kAllUserFilesTarget) { return "alluserfiles"; } if (tag == kAllGroupBytesTarget) { return "allgroupbytes"; } if (tag == kAllGroupFilesTarget) { return "allgroupfiles"; } return 0; } //------------------------------------------------------------------------------ // Convert string tag to int representation //------------------------------------------------------------------------------ unsigned long SpaceQuota::GetTagFromString(const std::string& tag) { if (tag == "userbytes") { return kUserBytesTarget; } if (tag == "userfiles") { return kUserFilesTarget; } if (tag == "groupbytes") { return kGroupBytesTarget; } if (tag == "groupfiles") { return kGroupFilesTarget; } if (tag == "alluserbytes") { return kAllUserBytesTarget; } if (tag == "alluserfiles") { return kAllUserFilesTarget; } if (tag == "allgroupbytes") { return kAllGroupBytesTarget; } if (tag == "allgroupfiles") { return kAllGroupFilesTarget; } return 0; } //------------------------------------------------------------------------------ // Convert int tag to user or group category //------------------------------------------------------------------------------ const char* SpaceQuota::GetTagCategory(int tag) { if ((tag == kUserBytesIs) || (tag == kUserBytesTarget) || (tag == kUserLogicalBytesIs) || (tag == kUserLogicalBytesTarget) || (tag == kUserFilesIs) || (tag == kUserFilesTarget) || (tag == kAllUserBytesIs) || (tag == kAllUserBytesTarget) || (tag == kAllUserFilesIs) || (tag == kAllUserFilesTarget)) { return "user"; } if ((tag == kGroupBytesIs) || (tag == kGroupBytesTarget) || (tag == kGroupLogicalBytesIs) || (tag == kGroupLogicalBytesTarget) || (tag == kGroupFilesIs) || (tag == kGroupFilesTarget) || (tag == kAllGroupBytesIs) || (tag == kAllGroupBytesTarget) || (tag == kAllGroupFilesIs) || (tag == kAllGroupFilesTarget)) { return "group"; } return "-----"; } //------------------------------------------------------------------------------ // Convert int tag to string description //------------------------------------------------------------------------------ const char* SpaceQuota::GetTagName(int tag) { if (tag == kUserBytesIs) { return "used bytes"; } if (tag == kUserLogicalBytesIs) { return "logi bytes"; } if (tag == kUserBytesTarget) { return "aval bytes"; } if (tag == kUserFilesIs) { return "used files"; } if (tag == kUserFilesTarget) { return "aval files"; } if (tag == kUserLogicalBytesTarget) { return "aval logib"; } if (tag == kGroupBytesIs) { return "used bytes"; } if (tag == kGroupLogicalBytesIs) { return "logi bytes"; } if (tag == kGroupBytesTarget) { return "aval bytes"; } if (tag == kGroupFilesIs) { return "used files"; } if (tag == kGroupFilesTarget) { return "aval files"; } if (tag == kGroupLogicalBytesTarget) { return "aval logib"; } if (tag == kAllUserBytesIs) { return "used bytes"; } if (tag == kAllUserLogicalBytesIs) { return "logi bytes"; } if (tag == kAllUserBytesTarget) { return "aval bytes"; } if (tag == kAllUserFilesIs) { return "used files"; } if (tag == kAllUserFilesTarget) { return "aval files"; } if (tag == kAllUserLogicalBytesTarget) { return "aval logib"; } if (tag == kAllGroupBytesIs) { return "used bytes"; } if (tag == kAllGroupLogicalBytesIs) { return "logi bytes"; } if (tag == kAllGroupBytesTarget) { return "aval bytes"; } if (tag == kAllGroupFilesIs) { return "used files"; } if (tag == kAllGroupFilesTarget) { return "aval files"; } if (tag == kAllGroupLogicalBytesTarget) { return "aval logib"; } return "---- -----"; } //------------------------------------------------------------------------------ // *** Class Quota implementaion *** //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Get space quota object for exact path - caller has to have a read lock on // pMapMutex. //------------------------------------------------------------------------------ SpaceQuota* Quota::GetSpaceQuota(const std::string& qpath) { std::string path = NormalizePath(qpath); if (pMapQuota.count(path)) { return pMapQuota[path]; } else { return nullptr; } } //------------------------------------------------------------------------------ // Get space quota object responsible for path (find best match) - caller has // to have a read lock on pMapMutex. //------------------------------------------------------------------------------ SpaceQuota* Quota::GetResponsibleSpaceQuota(const std::string& path) { XrdOucString matchpath = path.c_str(); SpaceQuota* squota = nullptr; for (auto it = pMapQuota.begin(); it != pMapQuota.end(); ++it) { if (matchpath.beginswith(it->second->GetSpaceName())) { if (squota == nullptr) { squota = it->second; } // Save if it's a better match if (strlen(it->second->GetSpaceName()) > strlen(squota->GetSpaceName())) { squota = it->second; } } } return squota; } //---------------------------------------------------------------------------- // Get space quota node path //---------------------------------------------------------------------------- std::string Quota::GetResponsibleSpaceQuotaPath(const std::string& path) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetResponsibleSpaceQuota(path); if (squota) { return squota->GetSpaceName(); } else { return ""; } } //------------------------------------------------------------------------------ // Check if space quota exists //------------------------------------------------------------------------------ bool Quota::Exists(const std::string& qpath) { std::string path = NormalizePath(qpath); eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); return (pMapQuota.count(path) != 0); } //------------------------------------------------------------------------------ // Check if there is a SpaceQuota responsible for the given path //------------------------------------------------------------------------------ bool Quota::ExistsResponsible(const std::string& path) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); return (GetResponsibleSpaceQuota(path) != 0); } //------------------------------------------------------------------------------ // Get individual quota - called only from mgm/http/webdav/PropFindResponse //------------------------------------------------------------------------------ void Quota::GetIndividualQuota(eos::common::VirtualIdentity& vid, const std::string& path, long long& max_bytes, long long& free_bytes, long long& max_files, long long& free_files, bool logical) { // Check for sys.auth='*' eos::common::VirtualIdentity m_vid = vid; XrdOucString xownerauth; XrdOucErrInfo error; struct stat buf; if (!gOFS->_stat(path.c_str(), &buf, error, vid, "")) { gOFS->_attr_get(path.c_str(), error, vid, "", "sys.owner.auth", xownerauth); std::string ownerauth = xownerauth.c_str(); if (ownerauth.length()) { if (ownerauth == "*") { eos_static_info("msg=\"client authenticated as directory owner\" " "path=\"%s\"uid=\"%u=>%u\" gid=\"%u=>%u\"", path.c_str(), vid.uid, vid.gid, buf.st_uid, buf.st_gid); // The client can operate as the owner, we rewrite the virtual id m_vid.uid = buf.st_uid; m_vid.gid = buf.st_gid; } else { ownerauth += ","; std::string ownerkey = vid.prot.c_str(); ownerkey += ":"; if (vid.prot == "gsi") { ownerkey += vid.dn.c_str(); } else { ownerkey += vid.uid_string.c_str(); } if ((ownerauth.find(ownerkey)) != std::string::npos) { eos_static_info("msg=\"client authenticated as directory owner\" " "path=\"%s\"uid=\"%u=>%u\" gid=\"%u=>%u\"", path.c_str(), vid.uid, vid.gid, buf.st_uid, buf.st_gid); // The client can operate as the owner, we rewrite the virtual id m_vid.uid = buf.st_uid; m_vid.gid = buf.st_gid; } } } } eos::common::RWMutexReadLock rd_ns_lock(gOFS->eosViewRWMutex); eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* space = GetResponsibleSpaceQuota(path); if (space) { space->Refresh(60); long long max_bytes_usr, max_bytes_grp, max_bytes_prj; long long free_bytes_usr, free_bytes_grp, free_bytes_prj; long long max_files_usr, max_files_grp, max_files_prj; long long free_files_usr, free_files_grp, free_files_prj; free_bytes_usr = free_bytes_grp = free_bytes_prj = 0; max_bytes_usr = max_bytes_grp = max_bytes_prj = 0; free_files_usr = free_files_grp = free_files_prj = 0; (void) free_files_usr; // not used - avoid compile warning max_files_usr = max_files_grp = max_files_prj = 0; (void) max_files_usr; // not used -avoid compile warning max_bytes_usr = space->GetQuota(SpaceQuota::kUserBytesTarget, m_vid.uid); max_bytes_grp = space->GetQuota(SpaceQuota::kGroupBytesTarget, m_vid.gid); max_bytes_prj = space->GetQuota(SpaceQuota::kGroupBytesTarget, Quota::gProjectId); free_bytes_usr = max_bytes_usr - space->GetQuota( SpaceQuota::kUserBytesIs, m_vid.uid); free_bytes_grp = max_bytes_grp - space->GetQuota( SpaceQuota::kGroupBytesIs, m_vid.gid); free_bytes_prj = max_bytes_prj - space->GetQuota( SpaceQuota::kGroupBytesIs, Quota::gProjectId); if (free_bytes_usr > free_bytes) { free_bytes = free_bytes_usr; } if (free_bytes_grp > free_bytes) { free_bytes = free_bytes_grp; } if (free_bytes_prj > free_bytes) { free_bytes = free_bytes_prj; } if (max_bytes_usr > max_bytes) { max_bytes = max_bytes_usr; } if (max_bytes_grp > max_bytes) { max_bytes = max_bytes_grp; } if (max_bytes_prj > max_bytes) { max_bytes = max_bytes_prj; } if (logical && space->GetLayoutSizeFactor()) { free_bytes /= space->GetLayoutSizeFactor(); max_bytes /= space->GetLayoutSizeFactor(); } } } //------------------------------------------------------------------------------ // Set quota type for id //------------------------------------------------------------------------------ bool Quota::SetQuotaTypeForId(const std::string& qpath, long id, Quota::IdT id_type, Quota::Type quota_type, unsigned long long value, std::string& msg, int& retc) { std::ostringstream oss_msg; std::string path = NormalizePath(qpath); retc = EINVAL; // If no path use "/eos/" if (path.empty()) { path = "/eos/"; } // Get type of quota to set and construct config entry std::ostringstream oss_config; SpaceQuota::eQuotaTag quota_tag; oss_config << path << ":"; if (id_type == IdT::kUid) { oss_config << "uid="; if (quota_type == Type::kVolume) { quota_tag = SpaceQuota::kUserBytesTarget; } else { quota_tag = SpaceQuota::kUserFilesTarget; } } else { oss_config << "gid="; if (quota_type == Type::kVolume) { quota_tag = SpaceQuota::kGroupBytesTarget; } else { quota_tag = SpaceQuota::kGroupFilesTarget; } } // Make sure the quota node exist if (!Create(path)) { oss_msg << "error: failed to create quota node: " << path; msg = oss_msg.str(); return false; } eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetSpaceQuota(path); if (!squota) { oss_msg << "error: no quota space defined for node " << path; msg = oss_msg.str(); return false; } squota->SetQuota(quota_tag, id, value); std::string svalue = std::to_string(value); oss_config << id << ":" << SpaceQuota::GetTagAsString(quota_tag); gOFS->ConfEngine->SetConfigValue("quota", oss_config.str().c_str(), svalue.c_str()); oss_msg << "success: updated " << ((quota_type == Type::kVolume) ? "volume" : "inode") << " quota for " << ((id_type == IdT::kUid) ? "uid=" : "gid=") << id << " for node " << path << std::endl; msg = oss_msg.str(); retc = 0; return true; } //------------------------------------------------------------------------------ // Set quota depending on the quota tag. //------------------------------------------------------------------------------ bool Quota::SetQuotaForTag(const std::string& qpath, const std::string& quota_stag, long id, unsigned long long value) { unsigned long spaceq_type = SpaceQuota::GetTagFromString(quota_stag); // Make sure the quota node exists eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetSpaceQuota(qpath); if (squota) { squota->SetQuota(spaceq_type, id, value); return true; } return false; } //------------------------------------------------------------------------------ // Remove quota type for id //------------------------------------------------------------------------------ bool Quota::RmQuotaTypeForId(const std::string& qpath, long id, Quota::IdT id_type, Quota::Type quota_type, std::string& msg, int& retc) { std::ostringstream oss_msg; std::string path = NormalizePath(qpath); retc = EINVAL; // If no path use "/eos/" if (path.empty()) { path = "/eos/"; } // Get type of quota to remove and construct config entry std::ostringstream oss_config; SpaceQuota::eQuotaTag quota_tag; oss_config << path << ":"; if (id_type == IdT::kUid) { oss_config << "uid="; if (quota_type == Type::kVolume) { quota_tag = SpaceQuota::kUserBytesTarget; } else { quota_tag = SpaceQuota::kUserFilesTarget; } } else { oss_config << "gid="; if (quota_type == Type::kVolume) { quota_tag = SpaceQuota::kGroupBytesTarget; } else { quota_tag = SpaceQuota::kGroupFilesTarget; } } eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetSpaceQuota(path); if (!squota) { oss_msg << "error: no quota space defined for node " << path; msg = oss_msg.str(); return false; } if (squota->RmQuota(quota_tag, id)) { oss_config << id << ":" << SpaceQuota::GetTagAsString(quota_tag); gOFS->ConfEngine->DeleteConfigValue("quota", oss_config.str().c_str()); oss_msg << "success: removed " << ((quota_type == Type::kVolume) ? "volume" : "inode") << " quota for " << ((id_type == IdT::kUid) ? "uid=" : "gid=") << id << " from node " << path; msg = oss_msg.str(); retc = 0; return true; } else { oss_msg << "error: no " << ((quota_type == Type::kVolume) ? "volume" : "inode") << " quota defined on node " << path << " for " << ((id_type == IdT::kUid) ? "user id" : "group id"); msg = oss_msg.str(); return false; } } //------------------------------------------------------------------------------ // Remove all quota types for an id //------------------------------------------------------------------------------ bool Quota::RmQuotaForId(const std::string& path, long id, Quota::IdT id_type, std::string& msg, int& retc) { eos_static_debug("path=%s", path.c_str()); std::string msg_vol, msg_inode; bool rm_vol = RmQuotaTypeForId(path, id, id_type, Type::kVolume, msg_vol, retc); bool rm_inode = RmQuotaTypeForId(path, id, id_type, Type::kInode, msg_inode, retc); if (rm_vol || rm_inode) { if (rm_vol) { msg += msg_vol; } if (rm_inode) { msg += msg_inode; } return true; } else { msg = "error: no quota defined for node "; msg += path; return false; } } //------------------------------------------------------------------------------ // Remove space quota //------------------------------------------------------------------------------ bool Quota::RmSpaceQuota(const std::string& qpath, std::string& msg, int& retc) { std::string path = NormalizePath(qpath); eos_static_debug("qpath=%s, path=%s", qpath.c_str(), path.c_str()); eos::common::RWMutexWriteLock wr_ns_lock(gOFS->eosViewRWMutex); eos::common::RWMutexWriteLock wr_quota_lock(pMapMutex); std::unique_ptr squota(GetSpaceQuota(path)); if (!squota) { retc = EINVAL; msg = "error: there is no quota node under path "; msg += path; return false; } else { // Remove space quota from map pMapQuota.erase(path); // Delete also from the pMapInodeQuota (void) pMapInodeQuota.erase(squota->GetQuotaNode()->getId()); // Remove ns quota node try { std::shared_ptr qcont = gOFS->eosView->getContainer(path); gOFS->eosView->removeQuotaNode(qcont.get()); retc = 0; } catch (eos::MDException& e) { retc = e.getErrno(); msg = e.getMessage().str().c_str(); } // Remove all configuration entries std::string match = path; match += ":"; gOFS->ConfEngine->DeleteConfigValueByMatch("quota", match.c_str()); msg = "success: removed space quota for "; msg += path; if (!gOFS->ConfEngine->AutoSave()) { return false; } return true; } } //------------------------------------------------------------------------------ // Remove quota depending on the quota tag. Convenience wrapper around the // default RmQuotaTypeForId. //------------------------------------------------------------------------------ bool Quota::RmQuotaForTag(const std::string& path, const std::string& quota_stag, long id) { unsigned long spaceq_type = SpaceQuota::GetTagFromString(quota_stag); eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetSpaceQuota(path); if (squota) { squota->RmQuota(spaceq_type, id); return true; } return false; } //------------------------------------------------------------------------------ // Callback function to calculate how much pyhisical space a file occupies //------------------------------------------------------------------------------ uint64_t Quota::MapSizeCB(const eos::IFileMD* file) { if (!file) { return 0; } eos::IFileMD::layoutId_t lid = file->getLayoutId(); return (uint64_t) file->getSize() * eos::common::LayoutId::GetSizeFactor(lid); } //------------------------------------------------------------------------------ // Load nodes //------------------------------------------------------------------------------ void Quota::LoadNodes() { std::vector create_quota; // Load all known nodes { std::string quota_path; std::shared_ptr container; eos::common::RWMutexReadLock rd_ns_lock(gOFS->eosViewRWMutex); auto set_ids = gOFS->eosView->getQuotaStats()->getAllIds(); for (const auto elem : set_ids) { try { container = gOFS->eosDirectoryService->getContainerMD(elem); quota_path = gOFS->eosView->getUri(container.get()); // Make sure directories are '/' terminated if (quota_path.back() != '/') { quota_path += '/'; } if (!Exists(quota_path)) { create_quota.push_back(quota_path); } } catch (eos::MDException& e) { errno = e.getErrno(); eos_static_err("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } } } // Create all the necessary space quota nodes for (auto it = create_quota.begin(); it != create_quota.end(); ++it) { eos_static_notice("msg=\"create quota node\" path=\"%s\"", it->c_str()); (void) Create(it->c_str()); } // Refresh the space quota objects { // loop over pMapQuota releasing locks each time in the iteration eos::common::RWMutexReadLock rd_ns_lock(gOFS->eosViewRWMutex); eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); bool first = true; size_t n = 0; do { auto it = pMapQuota.begin(); std::advance(it, n); if (!first) { rd_ns_lock.Grab(gOFS->eosViewRWMutex); rd_quota_lock.Grab(pMapMutex); first = false; } if (it == pMapQuota.end()) { break; } it->second->Refresh(5); n++; rd_quota_lock.Release(); rd_ns_lock.Release(); } while (1); } } //------------------------------------------------------------------------------ // Print out quota information //------------------------------------------------------------------------------ bool Quota::PrintOut(const std::string& path, XrdOucString& output, long long int uid_sel, long long int gid_sel, bool monitoring, bool translate_ids) { output = ""; // Add this to have all quota nodes visible even if they are not in // the configuration file LoadNodes(); eos::common::RWMutexReadLock rd_fs_lock(FsView::gFsView.ViewMutex); eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); if (path.empty()) { for (auto it = pMapQuota.begin(); it != pMapQuota.end(); ++it) { it->second->PrintOut(output, uid_sel, gid_sel, monitoring, translate_ids); } } else { SpaceQuota* squota = GetResponsibleSpaceQuota(path); if (squota) { squota->PrintOut(output, uid_sel, gid_sel, monitoring, translate_ids); } else { output = "error: no quota for path "; output += path.c_str(); return false; } } return true; } //------------------------------------------------------------------------------ // Get group quota values for a particular path and id //------------------------------------------------------------------------------ std::map Quota::GetGroupStatistics(const std::string& qpath, long id) { std::string path = NormalizePath(qpath); std::map map; eos::common::RWMutexReadLock rd_ns_lock(gOFS->eosViewRWMutex); eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetResponsibleSpaceQuota(path); if (!squota) { return map; } squota->Refresh(60); unsigned long long value; // Set of all group related quota keys std::set set_keys = {SpaceQuota::kGroupBytesIs, SpaceQuota::kGroupBytesTarget, SpaceQuota::kGroupFilesIs, SpaceQuota::kGroupFilesTarget, SpaceQuota::kAllGroupBytesTarget, SpaceQuota::kAllGroupBytesIs }; for (auto it = set_keys.begin(); it != set_keys.end(); ++it) { value = squota->GetQuota(*it, id); map.insert(std::make_pair(*it, value)); } return map; } //------------------------------------------------------------------------------ // Update quota from the namespace quota only if the requested path is actually // a ns quota node. //------------------------------------------------------------------------------ bool Quota::UpdateFromNsQuota(const std::string& path, uid_t uid, gid_t gid) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetResponsibleSpaceQuota(path); // No quota or this is not the space quota itself - do nothing if (!squota || (strcmp(squota->GetSpaceName(), path.c_str()))) { return false; } squota->UpdateFromQuotaNode(uid, gid, true); return true; } //---------------------------------------------------------------------------- // Check if the requested volume and inode values respect the quota //---------------------------------------------------------------------------- bool Quota::Check(const std::string& path, uid_t uid, gid_t gid, long long desired_vol, unsigned int desired_inodes) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetResponsibleSpaceQuota(path); if (!squota) { return true; } return squota->CheckWriteQuota(uid, gid, desired_vol, desired_inodes); } //------------------------------------------------------------------------------ // Clean-up all space quotas by deleting them and clearing the maps //------------------------------------------------------------------------------ void Quota::CleanUp() { eos::common::RWMutexWriteLock wr_lock(pMapMutex); for (auto it = pMapQuota.begin(); it != pMapQuota.end(); ++it) { delete it->second; } pMapQuota.clear(); pMapInodeQuota.clear(); } //------------------------------------------------------------------------------ // Take the decision where to place a new file in the system. The core of the // implementation is in the Scheduler and GeoTreeEngine. //------------------------------------------------------------------------------ int Quota::FilePlacement(Scheduler::PlacementArguments* args) { // 0 = 1 replica ! unsigned int nfilesystems = eos::common::LayoutId::GetStripeNumber( args->lid) + 1; // First figure out how many filesystems we need eos_static_debug("uid=%u gid=%u grouptag=%s place filesystems=%u", args->vid->uid, args->vid->gid, args->grouptag, nfilesystems); // Check if quota enabled for current space if (FsView::gFsView.IsQuotaEnabled(*args->spacename)) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetResponsibleSpaceQuota(args->path); if (squota) { bool has_quota = false; long long desired_vol = 1ll * nfilesystems * args->bookingsize; has_quota = squota->CheckWriteQuota(args->vid->uid, args->vid->gid, desired_vol, 1); if (!has_quota) { eos_static_debug("uid=%u gid=%u grouptag=%s place filesystems=%u " "has no quota left!", args->vid->uid, args->vid->gid, args->grouptag, nfilesystems); return EDQUOT; } } } else { eos_static_debug("quota is disabled for space=%s", args->spacename->c_str()); } if (!FsView::gFsView.mSpaceGroupView.count(*args->spacename)) { eos_static_err("msg=\"no filesystem in space\" space=\"%s\"", args->spacename->c_str()); args->selected_filesystems->clear(); return ENOSPC; } if (FsView::gFsView.mSpaceView.count(*args->spacename)) { if (!FsView::gFsView.UnderNominalQuota(*args->spacename, args->vid->sudoer)) { eos_static_err("msg=\"over physical quota limit (nominal space setting)\" space=\"%s\"", args->spacename->c_str()); return ENOSPC; } else { if (EOS_LOGS_DEBUG) { eos_static_debug("nominal quota ok"); } } } // Call the scheduler implementation return Scheduler::FilePlacement(args); } //------------------------------------------------------------------------------ // Create quota node for path //------------------------------------------------------------------------------ bool Quota::Create(const std::string& path) { // Check if path is correct if (path.empty() || path[0] != '/' || (*path.rbegin()) != '/') { return false; } eos::common::RWMutexWriteLock wr_ns_lock(gOFS->eosViewRWMutex); eos::common::RWMutexWriteLock wr_quota_lock(pMapMutex); if (pMapQuota.count(path) == 0) { try { SpaceQuota* squota = new SpaceQuota(path.c_str()); pMapQuota[path] = squota; pMapInodeQuota[squota->GetQuotaNode()->getId()] = squota; } catch (const eos::MDException& e) { eos_static_crit("Failed to create quota node %s", path.c_str()); return false; } } // Synchronize the flusher to avoid a race condition with the slave creating // the same directory when applying the quota auto* qdb_ns_grp = dynamic_cast (gOFS->namespaceGroup.get()); if (qdb_ns_grp) { qdb_ns_grp->getMetadataFlusher()->synchronize(); } return true; } //------------------------------------------------------------------------------ // Retrieve the kAllGroupLogicalBytesIs and kAllGroupLogicalBytesTarget // values for the quota nodes. //------------------------------------------------------------------------------ std::map> Quota::GetAllGroupsLogicalQuotaValues() { std::map> allGroupLogicalByteValues; // Add this to have all quota nodes visible even if they are not in // the configuration file LoadNodes(); eos::common::RWMutexReadLock rd_fs_lock(FsView::gFsView.ViewMutex); eos::common::RWMutexReadLock rd_ns_lock(gOFS->eosViewRWMutex); eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); for (const auto& quotaNode : pMapQuota) { // quotaNode.second->Refresh(); allGroupLogicalByteValues[quotaNode.first] = std::make_tuple (quotaNode.second->GetQuota(SpaceQuota::eQuotaTag::kAllGroupLogicalBytesIs, 0), quotaNode.second->GetQuota(SpaceQuota::eQuotaTag::kAllGroupLogicalBytesTarget, 0), quotaNode.second->GetQuota(SpaceQuota::eQuotaTag::kAllGroupFilesIs, 0)); } return allGroupLogicalByteValues; } //------------------------------------------------------------------------------ // Get quota for requested user and group by path //------------------------------------------------------------------------------ int Quota::QuotaByPath(const char* path, uid_t uid, gid_t gid, long long& avail_files, long long& avail_bytes, eos::IContainerMD::id_t& quota_inode) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* squota = GetResponsibleSpaceQuota(path); if (squota) { quota_inode = squota->GetQuotaNode()->getId(); return GetQuotaInfo(squota, uid, gid, avail_files, avail_bytes); } return -1; } //------------------------------------------------------------------------------ // Get quota for requested user and group by quota inode //------------------------------------------------------------------------------ int Quota::QuotaBySpace(const eos::IContainerMD::id_t qino, uid_t uid, gid_t gid, long long& avail_files, long long& avail_bytes) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); auto it = pMapInodeQuota.find(qino); if (it != pMapInodeQuota.end()) { return GetQuotaInfo(it->second, uid, gid, avail_files, avail_bytes); } return -1; } //------------------------------------------------------------------------------ // Private method to collect desired info from a quota node // returns std::numeric_limits::max() / 2; in avail_files or avail_bytes if not quota is set //------------------------------------------------------------------------------ int Quota::GetQuotaInfo(SpaceQuota* squota, uid_t uid, gid_t gid, long long& avail_files, long long& avail_bytes) { long long maxbytes_user, maxbytes_group, maxbytes_project; long long freebytes_user, freebytes_group, freebytes_project; long long freebytes = 0 ; long long maxbytes = 0; freebytes_user = freebytes_group = freebytes_project = 0; maxbytes_user = maxbytes_group = maxbytes_project = 0; squota->UpdateFromQuotaNode(uid, gid, squota->GetQuota(SpaceQuota::kGroupBytesTarget, Quota::gProjectId) ? true : false); maxbytes_user = squota->GetQuota(SpaceQuota::kUserBytesTarget, uid); maxbytes_group = squota->GetQuota(SpaceQuota::kGroupBytesTarget, gid); maxbytes_project = squota->GetQuota(SpaceQuota::kGroupBytesTarget, Quota::gProjectId); freebytes_user = maxbytes_user - squota->GetQuota( SpaceQuota::kUserBytesIs, uid); freebytes_group = maxbytes_group - squota->GetQuota( SpaceQuota::kGroupBytesIs, gid); freebytes_project = maxbytes_project - squota->GetQuota( SpaceQuota::kGroupBytesIs, Quota::gProjectId); // rescale the leftover physical space to the default layout and report the recomputed logical quota maxbytes_user /= squota->GetLayoutSizeFactor(); maxbytes_group /= squota->GetLayoutSizeFactor(); maxbytes_project /= squota->GetLayoutSizeFactor(); freebytes_user /= squota->GetLayoutSizeFactor(); freebytes_group /= squota->GetLayoutSizeFactor(); freebytes_project /= squota->GetLayoutSizeFactor(); if (freebytes_user > freebytes) { freebytes = freebytes_user; } if (freebytes_group > freebytes) { freebytes = freebytes_group; } if (freebytes_project > freebytes) { freebytes = freebytes_project; } if (maxbytes_user > maxbytes) { maxbytes = maxbytes_user; } if (maxbytes_group > maxbytes) { maxbytes = maxbytes_group; } if (maxbytes_project > maxbytes) { maxbytes = maxbytes_project; } if (!freebytes && (maxbytes == 0)) { // this is no quota set freebytes = std::numeric_limits::max() / 2; } long long maxfiles_user, maxfiles_group, maxfiles_project; long long freefiles_user, freefiles_group, freefiles_project; long long freefiles = 0; long long maxfiles = 0; freefiles_user = freefiles_group = freefiles_project = 0; maxfiles_user = maxfiles_group = maxfiles_project = 0; maxfiles_user = squota->GetQuota(SpaceQuota::kUserFilesTarget, uid); maxfiles_group = squota->GetQuota(SpaceQuota::kGroupFilesTarget, gid); maxfiles_project = squota->GetQuota(SpaceQuota::kGroupFilesTarget, Quota::gProjectId); freefiles_user = maxfiles_user - squota->GetQuota(SpaceQuota::kUserFilesIs, uid); freefiles_group = maxfiles_group - squota->GetQuota(SpaceQuota::kGroupFilesIs, gid); freefiles_project = maxfiles_project - squota->GetQuota( SpaceQuota::kGroupFilesIs, Quota::gProjectId); if (freefiles_user > freefiles) { freefiles = freefiles_user; } if (freefiles_group > freefiles) { freefiles = freefiles_group; } if (freefiles_project > freefiles) { freefiles = freefiles_project; } if (maxfiles_user > maxfiles) { maxfiles = maxfiles_user; } if (maxfiles_group > maxfiles) { maxfiles = maxfiles_group; } if (maxfiles_project > maxfiles) { maxfiles = maxfiles_project; } if (!freefiles && (maxfiles == 0)) { // this is no quota set freefiles = std::numeric_limits::max() / 2; } avail_files = freefiles; avail_bytes = freebytes; return 0; } //------------------------------------------------------------------------------ // Get logical max and free bytes for the given space //------------------------------------------------------------------------------ void Quota::GetStatfs(const std::string& path, unsigned long long& maxbytes, unsigned long long& freebytes) { eos::common::RWMutexReadLock rd_quota_lock(pMapMutex); SpaceQuota* space = GetResponsibleSpaceQuota(path); if (space) { space->Refresh(60); maxbytes = space->GetQuota(SpaceQuota::kAllGroupBytesTarget, 0); freebytes = maxbytes - space->GetQuota(SpaceQuota::kAllGroupBytesIs, 0); maxbytes /= space->GetLayoutSizeFactor(); freebytes /= space->GetLayoutSizeFactor(); } else { maxbytes = freebytes = 0; } } //------------------------------------------------------------------------------ // Remove file from corresponding quota node //------------------------------------------------------------------------------ bool Quota::RemoveFile(eos::IFileMD::id_t fid) { std::shared_ptr fmd {nullptr}; std::shared_ptr cmd {nullptr}; eos::IQuotaNode* ns_quota {nullptr}; eos::common::RWMutexWriteLock ns_wr_lock(gOFS->eosViewRWMutex); try { fmd = gOFS->eosFileService->getFileMD(fid); cmd = gOFS->eosDirectoryService->getContainerMD(fmd->getContainerId()); ns_quota = gOFS->eosView->getQuotaNode(cmd.get()); } catch (const eos::MDException& e) { return false; } if (ns_quota && fmd) { ns_quota->removeFile(fmd.get()); return true; } return false; } //------------------------------------------------------------------------------ // Remove file from corresponding quota node //------------------------------------------------------------------------------ bool Quota::AddFile(eos::IFileMD::id_t fid) { std::shared_ptr fmd {nullptr}; std::shared_ptr cmd {nullptr}; eos::IQuotaNode* ns_quota {nullptr}; eos::common::RWMutexWriteLock ns_wr_lock(gOFS->eosViewRWMutex); try { fmd = gOFS->eosFileService->getFileMD(fid); cmd = gOFS->eosDirectoryService->getContainerMD(fmd->getContainerId()); ns_quota = gOFS->eosView->getQuotaNode(cmd.get()); } catch (const eos::MDException& e) { return false; } if (ns_quota && fmd) { ns_quota->addFile(fmd.get()); return true; } return false; } EOSMGMNAMESPACE_END