// ---------------------------------------------------------------------- // File: proc/user/FuseX.cc // Author: Andreas-Joachim Peters - CERN // ---------------------------------------------------------------------- /************************************************************************ * 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 "XrdOuc/XrdOucEnv.hh" #include "common/SymKeys.hh" #include "mgm/ZMQ.hh" #include "mgm/FuseServer/Server.hh" #include "mgm/XrdMgmOfs.hh" #include "mgm/Access.hh" #include "mgm/Macros.hh" #include "mgm/Stat.hh" #include "namespace/interface/IView.hh" #include "namespace/Prefetcher.hh" EOSMGMNAMESPACE_BEGIN #define FUSEXMAXCHILDREN 64 int ProcCommand::FuseX() { ACCESSMODE_R; FUNCTIONMAYSTALL("Eosxd::prot::LS", *pVid, *mError); { FUNCTIONMAYSTALL("Eosxd::ext::LS", *pVid, *mError); } { FUNCTIONMAYSTALL("Eosxd::ext::LS-Entry", *pVid, *mError); } gOFS->MgmStats.Add("Eosxd::prot::LS", pVid->uid, pVid->gid, 1); EXEC_TIMING_BEGIN("Eosxd::prot::LS"); // ------------------------------------------------------------------------------------------------------- // This function returns meta data by inode or if provided first translates a path into an inode. // The client can provide the meta-data clock. If it is equivalent to the stored clock, this function // return EEXIST and no result stream. // If a path cannot be translated the function returns ENOENT or a relevant errno for namespace failures. // If mgm.op is equal to 'GETCAP' it does not return meta data but a capability. // ------------------------------------------------------------------------------------------------------- XrdOucString sinode = pOpaque->Get("mgm.inode") ? pOpaque->Get("mgm.inode") : "0"; XrdOucString sclock = pOpaque->Get("mgm.clock") ? pOpaque->Get("mgm.clock") : "0"; XrdOucString spath = pOpaque->Get("mgm.path") ? pOpaque->Get("mgm.path") : ""; XrdOucString schild = pOpaque->Get("mgm.child") ? pOpaque->Get("mgm.child") : ""; XrdOucString sop = pOpaque->Get("mgm.op") ? pOpaque->Get("mgm.op") : "GET"; XrdOucString suuid = pOpaque->Get("mgm.uuid") ? pOpaque->Get("mgm.uuid") : ""; XrdOucString cid = pOpaque->Get("mgm.cid") ? pOpaque->Get("mgm.cid") : ""; XrdOucString authid = pOpaque->Get("mgm.authid") ? pOpaque->Get("mgm.authid") : ""; bool inlined = pOpaque->Get("mgm.inline") ? true : false; // clients supports inlined responses in error messages if (spath.length()) { // decode escaped path name spath = eos::common::StringConversion::curl_unescaped(spath.c_str()).c_str(); } const char* inpath = spath.length() ? spath.c_str() : sinode.c_str(); uint64_t inode = strtoull(sinode.c_str(), 0, 16); uint64_t clock = strtoull(sclock.c_str(), 0, 10); uint64_t parentinode = 0; if (EOS_LOGS_DEBUG) { eos_static_debug("vid(%d,%d,%s)", vid.uid, vid.gid, vid.host.c_str()); } PROC_BOUNCE_NOT_ALLOWED; eos::fusex::md md; md.set_clientuuid(suuid.c_str()); md.set_clientid(cid.c_str()); md.set_authid(authid.c_str()); errno = 0; if (spath.length()) { // translate spath into an inode number std::shared_ptr fmd; std::shared_ptr cmd; eos::Prefetcher::prefetchFileMDAndWait(gOFS->eosView, spath.c_str()); errno = 0; eos::common::RWMutexReadLock lock(gOFS->eosViewRWMutex); std::string emsg = "none"; try { fmd = gOFS->eosView->getFile(spath.c_str(), true); inode = eos::common::FileId::FidToInode(fmd->getId()); } catch (eos::MDException& e) { errno = e.getErrno(); emsg = e.getMessage().str().c_str(); } if (!fmd) { lock.Release(); eos::Prefetcher::prefetchContainerMDAndWait(gOFS->eosView, spath.c_str()); errno = 0 ; lock.Grab(gOFS->eosViewRWMutex); try { cmd = gOFS->eosView->getContainer(spath.c_str(), true); inode = cmd->getId(); } catch (eos::MDException& e) { errno = e.getErrno(); emsg = e.getMessage().str().c_str(); } } if (errno) { if (errno != ENOENT) { eos_err("msg=\"exception\" ec=%d emsg=\"%s\"", errno, emsg.c_str()); } else { eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", errno, emsg.c_str()); } return gOFS->Emsg("FuseX", *mError, errno, "get-if-clock", emsg.c_str()); } } if (schild.length()) { // decode escaped child name schild = eos::common::StringConversion::curl_unescaped(schild.c_str()).c_str(); std::shared_ptr cmd; eos::Prefetcher::prefetchContainerMDWithChildrenAndWait(gOFS->eosView, inode); errno = 0; eos::common::RWMutexReadLock lock(gOFS->eosViewRWMutex); std::string emsg = "none"; // lookup by parent dir + name try { cmd = gOFS->eosDirectoryService->getContainerMD(inode); inode = 0; if ((cmd->getNumContainers() + cmd->getNumFiles()) < FUSEXMAXCHILDREN) { parentinode = inode; } std::shared_ptr fmd = cmd->findFile(schild.c_str()); if (fmd) { inode = eos::common::FileId::FidToInode(fmd->getId()); } else { std::shared_ptr ccmd = cmd->findContainer(schild.c_str()); if (ccmd) { inode = ccmd->getId(); } } if (!inode) { errno = ENOENT; emsg = schild.c_str(); emsg += " - no such file or directory"; } else { errno = 0; } } catch (eos::MDException& e) { errno = e.getErrno(); emsg = e.getMessage().str().c_str(); } if (errno) { if (errno != ENOENT) { eos_err("msg=\"exception\" ec=%d emsg=\"%s\"", errno, emsg.c_str()); } else { eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", errno, emsg.c_str()); } return gOFS->Emsg("FuseX", *mError, errno, "get-if-clock", emsg.c_str()); } } uint64_t md_clock = 0; md.set_md_ino(inode); if (parentinode) { // if we have a small response, we return the listing of the parent instead // the 'name' MD only, that saves us future roundtrips if (sop == "GET") { md.set_operation(md.LS); md.set_md_ino(parentinode); } } else { if (sop == "GET") { md.set_operation(md.GET); } if (sop == "LS") { md.set_operation(md.LS); } if (sop == "GETCAP") { md.set_operation(md.GETCAP); } } if (clock) { // if a clock is given, we only retrieve the MD clock without calling the FillXXX functions if (!eos::common::FileId::IsFileInode(md.md_ino())) { try { uint64_t md_clock; gOFS->eosDirectoryService->getContainerMD(md.md_ino(), &md_clock); } catch (eos::MDException& e) { try { gOFS->eosFileService->getFileMD(eos::common::FileId::InodeToFid(inode), &md_clock); } catch (eos::MDException& e) { return gOFS->Emsg("FuseX", *mError, e.getErrno(), e.getMessage().str().c_str()); } } } else { } if (EOS_LOGS_DEBUG) { eos_debug("c1=%llu c2=%llu", md_clock, clock); } if ((sop == "GET") || (sop == "LS")) { if (md_clock == clock) { // if the given clock is ok, we return EEXIST return gOFS->Emsg("FuseX", *mError, EEXIST, "get-if-clock", inpath); } } } std::string result; std::string id = std::string("Fusex::sync:") + vid.tident.c_str(); mResultStream = ""; int rc = gOFS->zMQ->gFuseServer.HandleMD(id, md, *pVid, &result, &md_clock); if (rc) { return gOFS->Emsg("FuseX", *mError, rc, "handle request", ""); } if (EOS_LOGS_DEBUG) { eos_debug("c1=%llu c2=%llu", md_clock, clock); } if (sop == "GETCAP") { // check clock synchronization // the client is supposed to send his current time when requesting a CAP time_t now = time(NULL); if ((uint64_t)now > clock + 2) { eos_err("client-clock %lu %s server-clock %lu", clock, sclock.c_str(), now); return gOFS->Emsg("FuseX", *mError, EL2NSYNC, "get-cap-clock-out-of-sync", inpath); } } if (inlined) { if (result.size() < 2048) { if (EOS_LOGS_DEBUG) { eos_debug("returning in-line result - len=%u", result.size()); } std::string b64response; eos::common::SymKey::Base64(result, b64response); XrdOucBuffer* buff = new XrdOucBuffer(strndup(b64response.c_str(), b64response.size()), b64response.size()); mError->setErrInfo(EIDRM, buff); return SFS_ERROR; } } mResultStream = result; if (EOS_LOGS_DEBUG) eos_debug("returning resultstream len=%u %s", mResultStream.size(), mResultStream.c_str()); mLen = mResultStream.size(); if (EOS_LOGS_DEBUG) eos_debug("result-dump=%s", eos::common::StringConversion::string_to_hex(result.c_str()).c_str()); EXEC_TIMING_END("Eosxd::prot::LS"); return SFS_OK; } EOSMGMNAMESPACE_END