// ---------------------------------------------------------------------- // File: Stripes.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 .* ************************************************************************/ // ----------------------------------------------------------------------- // This file is included source code in XrdMgmOfs.cc to make the code more // transparent without slowing down the compilation time. // ----------------------------------------------------------------------- /*----------------------------------------------------------------------------*/ /* * @brief send a verification message to a file system for a given file * * @param path file name to verify * @param error error object * @param vid virtual identity of the client * @param fsid filesystem id where to run the verification * @param option pass-through string for the verification * * @return SFS_OK if success otherwise SFS_ERROR * * The function requires POSIX W_OK & X_OK on the parent directory to succeed. */ /*----------------------------------------------------------------------------*/ int XrdMgmOfs::_verifystripe(const char* path, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, unsigned long fsid, XrdOucString option) { static const char* epname = "verifystripe"; std::shared_ptr dh; std::shared_ptr fmd; EXEC_TIMING_BEGIN("VerifyStripe"); errno = 0; unsigned long long fid = 0; unsigned long long cid = 0; int lid = 0; eos::IContainerMD::XAttrMap attrmap; gOFS->MgmStats.Add("VerifyStripe", vid.uid, vid.gid, 1); eos_debug("verify"); eos::common::Path cPath(path); std::string attr_path; { eos::common::RWMutexReadLock lock(gOFS->eosViewRWMutex); try { dh = gOFS->eosView->getContainer(cPath.GetParentPath()); attr_path = gOFS->eosView->getUri(dh.get()); dh = gOFS->eosView->getContainer(gOFS->eosView->getUri(dh.get())); } catch (eos::MDException& e) { dh.reset(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } // Check permissions if (dh && (!dh->access(vid.uid, vid.gid, X_OK | W_OK))) { if (!errno) { errno = EPERM; } } else { // only root can delete a detached replica if (vid.uid) { errno = EPERM; } } if (errno) { return Emsg(epname, error, errno, "verify stripe", path); } // Get attributes gOFS->_attr_ls(attr_path.c_str(), error, vid, 0, attrmap); // Get the file try { fmd = gOFS->eosView->getFile(path); fid = fmd->getId(); lid = fmd->getLayoutId(); cid = fmd->getContainerId(); } catch (eos::MDException& e) { fmd.reset(); errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); return Emsg(epname, error, errno, "verify stripe - not file metadata", path); } } int fst_port; std::string fst_path, fst_queue, fst_host; { eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex); auto* verify_fs = FsView::gFsView.mIdView.lookupByID(fsid); if (!verify_fs) { errno = EINVAL; return Emsg(epname, error, ENOENT, "verify stripe - filesystem does not exist", path); } // @todo(esindril) only issue verify for booted filesystems fst_path = verify_fs->GetPath(); fst_queue = verify_fs->GetQueue(); fst_host = verify_fs->GetHost(); fst_port = verify_fs->getCoreParams().getLocator().getPort(); } XrdOucString receiver = fst_queue.c_str(); XrdOucString opaquestring = ""; // build the opaquestring contents opaquestring += "&mgm.localprefix="; opaquestring += fst_path.c_str(); opaquestring += "&mgm.fid="; const std::string hex_fid = eos::common::FileId::Fid2Hex(fid); opaquestring += hex_fid.c_str(); opaquestring += "&mgm.manager="; opaquestring += gOFS->ManagerId.c_str(); opaquestring += "&mgm.access=verify"; opaquestring += "&mgm.fsid="; opaquestring += std::to_string(fsid).c_str(); if (attrmap.count("user.tag")) { opaquestring += "&mgm.container="; opaquestring += attrmap["user.tag"].c_str(); } XrdOucString sizestring = ""; opaquestring += "&mgm.cid="; opaquestring += eos::common::StringConversion::GetSizeString(sizestring, cid); opaquestring += "&mgm.path="; XrdOucString safepath = path; eos::common::StringConversion::SealXrdPath(safepath); opaquestring += safepath; opaquestring += "&mgm.lid="; opaquestring += lid; if (option.length()) { opaquestring += option; } std::string qreq = "/?fst.pcmd=verify"; qreq += opaquestring.c_str(); std::string qresp; if (SendQuery(fst_host, fst_port, qreq, qresp)) { eos_static_err("msg=\"unable to send verification message\" target=%s", fst_queue.c_str()); errno = ECOMM; } else { errno = 0; } EXEC_TIMING_END("VerifyStripe"); if (errno) { return Emsg(epname, error, errno, "verify stripe", path); } else { return SFS_OK; } } /*----------------------------------------------------------------------------*/ /* * @brief send a drop message to a file system for a given file * * @param path file name to drop stripe * #param file id of the file to drop stripe * @param error error object * @param vid virtual identity of the client * @param fsid filesystem id where to run the drop * @param forceRemove if true the stripe is immediately dropped * * @return SFS_OK if success otherwise SFS_ERROR * * The function requires POSIX W_OK & X_OK on the parent directory to succeed. */ /*----------------------------------------------------------------------------*/ int XrdMgmOfs::_dropstripe(const char* path, eos::common::FileId::fileid_t fid, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, unsigned long fsid, bool forceRemove) { static const char* epname = "dropstripe"; std::shared_ptr dh; std::shared_ptr fmd; int errc = 0; EXEC_TIMING_BEGIN("DropStripe"); gOFS->MgmStats.Add("DropStripe", vid.uid, vid.gid, 1); eos_debug("drop"); eos::common::Path cPath(path); // --------------------------------------------------------------------------- eos::common::RWMutexWriteLock lock(gOFS->eosViewRWMutex); try { dh = gOFS->eosView->getContainer(cPath.GetParentPath()); dh = gOFS->eosView->getContainer(gOFS->eosView->getUri(dh.get())); } catch (eos::MDException& e) { dh.reset(); errc = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } // Check permissions if (dh && (!dh->access(vid.uid, vid.gid, X_OK | W_OK))) { if (!errc) { errc = EPERM; } } else { // only root can drop by file id if (vid.uid) { errc = EPERM; } } if (errc) { return Emsg(epname, error, errc, "drop stripe", path); } // get the file try { if (fid) { fmd = gOFS->eosFileService->getFileMD(fid); } else { fmd = gOFS->eosView->getFile(path); } std::string locations; try { locations = fmd->getAttribute("sys.fs.tracking"); } catch (...) {} if (!forceRemove) { // we only unlink a location if (fmd->hasLocation(fsid)) { fmd->unlinkLocation(fsid); locations += "-"; locations += std::to_string(fsid); fmd->setAttribute("sys.fs.tracking", eos::common::StringConversion::ReduceString(locations).c_str()); gOFS->eosView->updateFileStore(fmd.get()); eos_debug("unlinking location %u", fsid); } else { errc = ENOENT; } } else { // we unlink and remove a location by force if (fmd->hasLocation(fsid)) { fmd->unlinkLocation(fsid); locations += "-"; locations += std::to_string(fsid); fmd->setAttribute("sys.fs.tracking", eos::common::StringConversion::ReduceString(locations).c_str()); } fmd->removeLocation(fsid); // eraseEntry is only needed if the fsview is inconsistent with the // FileMD: It exists on the selected fsview, but not in the fmd locations. // Very rare case. gOFS->eosFsView->eraseEntry(fsid, fmd->getId()); gOFS->eosView->updateFileStore(fmd.get()); eos_debug("removing/unlinking location %u", fsid); } } catch (eos::MDException& e) { fmd.reset(); errc = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } EXEC_TIMING_END("DropStripe"); if (errc) { return Emsg(epname, error, errc, "drop stripe", path); } return SFS_OK; } /*----------------------------------------------------------------------------*/ /* * @brief send a drop message to all filesystems where given file is located * * @param path file name to drop stripe * @param error error object * @param vid virtual identity of the client * @param forceRemove if true the stripe is immediately dropped * * @return SFS_OK if success otherwise SFS_ERROR * * The function requires POSIX W_OK & X_OK on the parent directory to succeed. */ /*----------------------------------------------------------------------------*/ int XrdMgmOfs::_dropallstripes(const char* path, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, bool forceRemove) { static const char* epname = "dropallstripes"; std::shared_ptr dh; std::shared_ptr fmd; errno = 0; EXEC_TIMING_BEGIN("DropAllStripes"); gOFS->MgmStats.Add("DropAllStripes", vid.uid, vid.gid, 1); eos_debug("dropall"); eos::common::Path cPath(path); auto parentPath = cPath.GetParentPath(); // --------------------------------------------------------------------------- { eos::common::RWMutexReadLock rlock(gOFS->eosViewRWMutex); try { dh = gOFS->eosView->getContainer(parentPath); dh = gOFS->eosView->getContainer(gOFS->eosView->getUri(dh.get())); } catch (eos::MDException& e) { dh.reset(); errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } // Check permissions if (dh && (!dh->access(vid.uid, vid.gid, X_OK | W_OK))) if (!errno) { errno = EPERM; } if (errno) { return Emsg(epname, error, errno, "drop all stripes", path); } try { fmd = gOFS->eosView->getFile(path); // only on tape, we don't touch this file here if (fmd && fmd->getLocations().size() == 1 && fmd->hasLocation(eos::common::TAPE_FS_ID)) { return SFS_OK; } } catch (eos::MDException& e) { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); // return error if we don't have the file metadata return e.getErrno(); } } try { // only write lock at this point eos::common::RWMutexWriteLock wlock(gOFS->eosViewRWMutex); for (auto location : fmd->getLocations()) { if (location == eos::common::TAPE_FS_ID) { continue; } if (!forceRemove) { // we only unlink a location fmd->unlinkLocation(location); eos_debug("unlinking location %u", location); } else { // we unlink and remove a location by force if (fmd->hasLocation(location)) { fmd->unlinkLocation(location); } fmd->removeLocation(location); eos_debug("removing/unlinking location %u", location); } } // update the file store only once at the end gOFS->eosView->updateFileStore(fmd.get()); } catch (eos::MDException& e) { fmd.reset(); errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } EXEC_TIMING_END("DropAllStripes"); if (errno) { return Emsg(epname, error, errno, "drop all stripes", path); } return SFS_OK; } //------------------------------------------------------------------------------ // Move file replica/stripe from source to target file system //------------------------------------------------------------------------------ int XrdMgmOfs::_movestripe(const char* path, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, unsigned long sourcefsid, unsigned long targetfsid) { EXEC_TIMING_BEGIN("MoveStripe"); int retc = _replicatestripe(path, error, vid, sourcefsid, targetfsid, true); EXEC_TIMING_END("MoveStripe"); return retc; } //------------------------------------------------------------------------------ // Copy file replica/stripe from source to target file system //------------------------------------------------------------------------------ int XrdMgmOfs::_copystripe(const char* path, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, unsigned long sourcefsid, unsigned long targetfsid) { EXEC_TIMING_BEGIN("CopyStripe"); int retc = _replicatestripe(path, error, vid, sourcefsid, targetfsid, false); EXEC_TIMING_END("CopyStripe"); return retc; } //------------------------------------------------------------------------------ // Copy file replica/stripe from source to target file system - by path //------------------------------------------------------------------------------ int XrdMgmOfs::_replicatestripe(const char* path, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, unsigned long sourcefsid, unsigned long targetfsid, bool dropsource) { static const char* epname = "replicatestripe"; std::shared_ptr dh; std::shared_ptr fmd; errno = 0; EXEC_TIMING_BEGIN("ReplicateStripe"); eos::common::Path cPath(path); eos_debug("msg=\"replicate file\" path=\"%s\" src_fsid=%u dst_fsid=%u drop=%d", path, sourcefsid, targetfsid, dropsource); { eos::common::RWMutexReadLock ns_rd_lock(gOFS->eosViewRWMutex); try { dh = gOFS->eosView->getContainer(cPath.GetParentPath()); dh = gOFS->eosView->getContainer(gOFS->eosView->getUri(dh.get())); } catch (eos::MDException& e) { dh.reset(); errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } // check permissions if (dh && (!dh->access(vid.uid, vid.gid, X_OK | W_OK))) if (!errno) { errno = EPERM; } // get the file try { fmd = gOFS->eosView->getFile(path); if (fmd->hasLocation(sourcefsid)) { if (fmd->hasLocation(targetfsid)) { errno = EEXIST; } } else { // this replica does not exist! errno = ENODATA; } } catch (eos::MDException& e) { fmd.reset(); errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } } if (errno) { return Emsg(epname, error, errno, "replicate stripe", path); } int retc = _replicatestripe(fmd.get(), path, error, vid, sourcefsid, targetfsid, dropsource); EXEC_TIMING_END("ReplicateStripe"); return retc; } //------------------------------------------------------------------------------ // Copy file replica/stripe from source to target file system - by FileMD //------------------------------------------------------------------------------ int XrdMgmOfs::_replicatestripe(eos::IFileMD* fmd, const char* path, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, unsigned long sourcefsid, unsigned long targetfsid, bool dropsource) { static const char* epname = "replicatestripe"; uint64_t fid = fmd->getId(); std::string app_tag = (dropsource ? "MoveStripe" : "CopyStripe");; std::shared_ptr job { new DrainTransferJob(fid, sourcefsid, targetfsid, {}, {}, dropsource, app_tag, false, vid)}; if (!gOFS->mFidTracker.AddEntry(fid, TrackerType::Drain)) { eos_err("msg=\"file already tracked\" fxid=%08llx", fid); return Emsg(epname, error, ENOENT, "replicate stripe - file already " "tracked ", std::to_string(fid).c_str()); } else { gOFS->mDrainEngine.GetThreadPool().PushTask([ = ] { job->UpdateMgmStats(); job->DoIt(); job->UpdateMgmStats(); }); } return SFS_OK; }