// ---------------------------------------------------------------------- // File: Find.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. // ----------------------------------------------------------------------- //------------------------------------------------------------------------------ // Low-level recursive namespace clone handling //------------------------------------------------------------------------------ /* cFlag (cloneId defaults to 0): * '>' - list files modified after * '=' - list files with clone-id * '-' - clean up clone-id , * '+' - clone if modified after after , * '?' - list all files/directories with cloneId/stime detail * '!' - list all files where with non-zero cloneId different from * */ #include "common/FileId.hh" #include "namespace/interface/IContainerMD.hh" #include "json/json.h" #include class _cloneFoundItem { public: eos::IContainerMD::id_t id; int depth; bool isContainer; _cloneFoundItem(eos::IContainerMD::id_t i, int d, bool cont) : id(i), depth(d), isContainer(cont) { }; }; /* curl encode string if needed */ static std::string _clone_escape(std::string s) { if (strpbrk(s.c_str(), " %") == NULL) { return s; /* only use escape sequences when needed */ } std::string t = eos::common::StringConversion::curl_default_escaped(s); #ifdef notNeededThereAintNoSlashesInFilenames size_t pos = 0; while (pos = t.find("%2F", pos)) { t.replace(pos, 3, "/"); pos += 1; } #endif return (t); } static void _cloneResp(XrdOucErrInfo& out_error, XrdOucString& stdErr, eos::common::VirtualIdentity& vid, std::list<_cloneFoundItem>& _found, bool json_output, FILE* fstdout) { std::stack pp; std::shared_ptr cmd; int depth = 0; std::string p; eos::IContainerMD::tmtime_t stime; Json::Value j; Json::StreamWriterBuilder jfw; jfw["indentation"] = ""; if (! _found.empty()) { /* first element is root of tree */ p = gOFS->eosView->getUri(gOFS->eosDirectoryService->getContainerMD( _found.front().id).get()); pp.push(p.substr(0, p.rfind('/', p.length() - 2) + 1)); /* "parent" path: /eos/a1/a2/ -> /eos/a1/ */ pp.push(std::string("/eos/a1/dummy/")); /* expect 1st element container @ depth 0, here's a dummy */ } // typedef std::tuple s_tuple; char sts[2048]; const char* sts_format = "(%d," /*st_mode*/ "%ld," /*st_ino*/ "%d," /*st_dev*/ "%d," /*st_nlink*/ "%ld," /*st_uid*/ "%ld,"/*st_gid*/ "%ld," /*st_size*/ "%9.7f," /*st_atime*/ "%9.7f," /*st_mtime*/ "%9.7f)" /*st_ctime*/; for (auto i : _found) { eos::IContainerMD::XAttrMap attrmap; if (i.isContainer) { try { cmd = gOFS->eosDirectoryService->getContainerMD(i.id); } catch (eos::MDException& e) { errno = e.getErrno(); eos_static_err("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); return; } while (i.depth <= depth) { /* pop previous container(s) */ pp.pop(); depth--; }; while (i.depth > depth) { pp.push(pp.top() + _clone_escape(cmd->getName()) + "/"); depth++; } cmd->getTMTime(stime); if (json_output) { struct timespec ts; j.clear(); j["n"] = pp.top(); j["t"] = (Json::Value::UInt64) stime.tv_sec; j["c"] = (Json::Value::UInt64) cmd->getCloneId(); j["T"] = cmd->getCloneFST(); cmd->getMTime(ts); j["mt"] = (Json::Value::UInt64) ts.tv_sec; cmd->getCTime(ts); j["ct"] = (Json::Value::UInt64) ts.tv_sec; eos::listAttributes(gOFS->eosView, cmd.get(), attrmap, false); eos::IContainerMD::ctime_t ctime, mtime; cmd->getCTime(ctime); cmd->getMTime(mtime); snprintf(sts, sizeof(sts), sts_format, cmd->getMode() | S_IFDIR, cmd->getId(), 42, cmd->getNumFiles(), /*st_mode,st_ino,st_dev,st_nlink*/ cmd->getCUid(), cmd->getCGid(), cmd->getNumContainers(), /*st_uid,st_gid,st_size*/ 0.0, /*st_atime*/ mtime.tv_sec + mtime.tv_nsec * 10E-9, ctime.tv_sec + ctime.tv_nsec * 10E-9); } else { fprintf(fstdout, "%s %ld:%ld:%s\n", pp.top().c_str(), stime.tv_sec, cmd->getCloneId(), cmd->getCloneFST().c_str()); } } else { /* a file */ std::shared_ptr fmd, gmd; uint64_t mdino = 0, hardlinkTgt = 0; try { gmd = gOFS->eosFileService->getFileMD(i.id); if (gmd->getName().substr(0, 13) == "...eos.ino...") { /* This is a zombie hard link target, kept around simply because another file points to it; * drop it from the dump - if that other file is backed up it'll get picked up again. */ continue; } if (!gmd->hasAttribute(XrdMgmOfsFile::k_mdino)) { fmd = gmd; if (fmd->hasAttribute(XrdMgmOfsFile::k_nlink)) { /* a (no-zombie) target for hard link(s), goes into the log */ hardlinkTgt = eos::common::FileId::FidToInode(fmd->getId()); } } else { /* this is a hard link to another file */ /* * for hard links: * name is filled from the named file, * time stamps, clone id, clone path, attributes from the hard link larget; * * on restore they could be fiddled back together over the clone_path; * from above: we do not report the zombie targets themselves */ mdino = std::stoll(gmd->getAttribute(XrdMgmOfsFile::k_mdino)); fmd = gOFS->eosFileService->getFileMD(eos::common::FileId::InodeToFid(mdino)); eos_static_debug("hlnk switched from %s to file %s (%#llx)", gmd->getName().c_str(), fmd->getName().c_str(), mdino); } } catch (eos::MDException& e) { eos_static_err("exception ec=%d emsg=\"%s\" dir %s id %#lx\n", e.getErrno(), e.getMessage().str().c_str(), p.c_str()); return; } gOFS->FuseXCastRefresh(fmd->getIdentifier(), eos::ContainerIdentifier( fmd->getContainerId())); fmd->getSyncTime(stime); if (json_output) { char sbuff[256]; struct timespec ts; j.clear(); sprintf(sbuff, "%lx/%lx", cmd->getId(), fmd->getId()); j["n"] = pp.top() + gmd->getName(); // Name j["t"] = (Json::Value::UInt64) stime.tv_sec; // time stamp j["c"] = (Json::Value::UInt64) fmd->getCloneId(); // cloneId j["T"] = fmd->getCloneFST(); // tag j["p"] = sbuff; // clone path if (mdino) { j["H"] = (Json::Value::UInt64) mdino; // a hard link alias: the mdino can be used to find the peers on restore } if (hardlinkTgt) { j["L"] = (Json::Value::UInt64) hardlinkTgt; // a hard link target: the inum can be used to find the peers on restore } if (fmd->isLink()) { j["S"] = fmd->getLink(); // the target of the symlink } fmd->getMTime(ts); j["mt"] = (Json::Value::UInt64) ts.tv_sec; fmd->getCTime(ts); j["ct"] = (Json::Value::UInt64) ts.tv_sec; eos::listAttributes(gOFS->eosView, fmd.get(), attrmap, false); eos::IContainerMD::ctime_t ctime, mtime; cmd->getCTime(ctime); cmd->getMTime(mtime); size_t nlink = (attrmap.count("sys.eos.nlink") > 0) ? std::stol( attrmap["sys.eos.nlink"]) : 1; snprintf(sts, sizeof(sts), sts_format, fmd->getFlags() | S_IFREG, fmd->getId(), 42, nlink, /*st_mode,st_ino,st_dev,st_nlink*/ fmd->getCUid(), fmd->getCGid(), fmd->getSize(), /*st_uid,st_gid,st_size*/ 0.0, /*st_atime*/ mtime.tv_sec + mtime.tv_nsec * 10E-9, ctime.tv_sec + ctime.tv_nsec * 10E-9); } else fprintf(fstdout, "%s%s %ld:%ld/%lx/%lx:%s\n", pp.top().c_str(), _clone_escape(gmd->getName()).c_str(), stime.tv_sec, fmd->getCloneId(), cmd->getId(), fmd->getId(), fmd->getCloneFST().c_str()); } if (json_output) { Json::Value attr; for (auto it = attrmap.begin(); it != attrmap.end(); it++) { if (it->first == "sys.vtrace" || it->first == XrdMgmOfsFile::k_mdino || it->first == XrdMgmOfsFile::k_nlink) { continue; } attr[it->first] = it->second; } j["attr"] = attr; j["st"] = sts; fprintf(fstdout, "%s\n", Json::writeString(jfw,j).c_str()); } } }; static bool _cloneMD(std::shared_ptr& cloneMd, char cFlag, uint64_t cloneId, std::shared_ptr& cmd) { char buff[1024]; snprintf(buff, sizeof(buff), "%s/clone/%ld", gOFS->MgmProcPath.c_str(), cloneId); std::string clonePath(buff); try { cloneMd = gOFS->eosView->getContainer(clonePath); if (cFlag == '+') { eos_static_err("clone directory %s already exists!", clonePath.c_str()); return false; } } catch (eos::MDException& e) { eos_static_debug("clonePath %s exception ec=%d emsg=\"%s\" cFlag '%c'", buff, e.getErrno(), e.getMessage().str().c_str(), cFlag); if (cFlag == '+' || cFlag == '-') { /* for '-': the clone directory may have been incorrectly removed, this should * not prevent a cleanup */ eos::common::RWMutexWriteLock lock(gOFS->eosViewRWMutex); eos::common::Path mdPath(buff); try { std::shared_ptr pCloneMd = gOFS->eosView->getContainer( mdPath.GetParentPath()); cloneMd = gOFS->eosView->createContainer(clonePath); cloneMd->setMode(S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); eos_static_info("%s permissions are %#o", clonePath.c_str(), cloneMd->getMode()); cloneMd->setAttribute("sys.clone.root", gOFS->eosView->getUri(cmd.get())); gOFS->eosDirectoryService->updateStore(cloneMd.get()); gOFS->eosDirectoryService->updateStore(pCloneMd.get()); eos::ContainerIdentifier md_id = cloneMd->getIdentifier(); /* see "mkdir" */ eos::ContainerIdentifier d_id = pCloneMd->getIdentifier(); eos::ContainerIdentifier d_pid = pCloneMd->getParentIdentifier(); lock.Release(); gOFS->FuseXCastRefresh(md_id, d_id); gOFS->FuseXCastRefresh(d_id, d_pid); } catch (eos::MDException& e) { eos_static_err("cannot create the %s directory mode 755", clonePath.c_str()); return false; } } else { return false; } } return true; } static int _clone(std::shared_ptr& cmd, XrdOucErrInfo& out_error, XrdOucString& stdErr, eos::common::VirtualIdentity& vid, std::list<_cloneFoundItem>& _found, char cFlag, uint64_t cloneId, time_t newId, std::shared_ptr cloneMd, int depth) { // cmd could almost be passed "by value", except for the "-" (purge) case; hence the cmd_ref passed by reference std::shared_ptr ccmd; /* container pointer for recursion */ int rc = SFS_OK; std::shared_ptr fmd; std::string link; eos::IContainerMD::tmtime_t stime; eos::common::RWMutexWriteLock rwlock; eos::common::VirtualIdentity rootvid = eos::common::VirtualIdentity::Root(); /* Only at depth 0: find/create clone anchor directory for operations that require it */ if (cloneMd == NULL && cFlag != '?' && cFlag != '>') { if (! _cloneMD(cloneMd, cFlag, (cFlag == '+') ? newId : cloneId, cmd)) { return SFS_ERROR; } /* The eosViewRWMutex lock is explicitly grabbed (for '+') only at the "root" level of the tree and * "quickly" released and re-grabbed at each directory in lower levels. Hence at deeper * recursion levels the lock is already held on entry */ if (cFlag == '+') { rwlock.Grab(gOFS->eosViewRWMutex); } else if (cFlag == '-' && cloneMd->hasAttribute("sys.clone.root")) { /* reset start of purge */ std::string rootDir = cloneMd->getAttribute("sys.clone.root"); try { cmd = gOFS->eosView->getContainer( rootDir); /* this only happens @ depth 0! */ eos_static_info("clone %ld purge hint %s", cloneId, rootDir.c_str()); } catch (eos::MDException& e) { eos_static_info("clone %ld root hint %s ignored ec=%d emsg='%s'", cloneId, rootDir.c_str(), e.getErrno(), e.getMessage().str().c_str()); } } } if (cFlag == '+') { /* cloneId <= 9: special, single level markers * all others: this is a new clone, make the directory part of it */ uint64_t thisId = newId, saveId; if (cloneId < 10) { thisId = cloneId; saveId = cmd->getCloneId(); if (saveId >= 10) { saveId = 0; /* only save an Id serving as marker */ } cmd->setCloneFST(saveId ? std::to_string(saveId) : ""); /* save this for later restore */ } cmd->setCloneId(thisId); gOFS->eosDirectoryService->updateStore(cmd.get()); if (cloneId <= 9) { return 0; } } else if (cFlag == '-' && (uint64_t)cmd->getCloneId() == cloneId) { /* clean the directory flag if it is part of this clone */ std::string prev_marker = cmd->getCloneFST(); /* reset cloneId to a potential previous marker */ uint64_t cleanId = 0; if (!prev_marker.empty()) { cmd->setCloneFST(""); cleanId = std::stol(prev_marker); } cmd->setCloneId(cleanId); gOFS->eosDirectoryService->updateStore(cmd.get()); } if (cFlag != '!' or (cmd->getCloneId() != 0 and (uint64_t)cmd->getCloneId() != cloneId)) { _found.emplace_back(cmd->getId(), depth, true); /* log this directory */ } if (EOS_LOGS_DEBUG) { eos_static_debug("_found container %#lx depth %d %s cloneId=%d", cmd->getId(), depth, cmd->getName().c_str(), cloneId); } for (auto fit = eos::FileMapIterator(cmd); fit.valid(); fit.next()) { if (EOS_LOGS_DEBUG) { eos_static_debug("%c depth %d file %s id %#lx", cFlag, depth, fit.key().c_str(), fit.value()); } try { fmd = gOFS->eosFileService->getFileMD(fit.value()); } catch (eos::MDException& e) { char sbuff[1024]; snprintf(sbuff, sizeof(sbuff), "msg=\"exception\" ec=%d fn=%s/%s emsg=\"%s\"\n", e.getErrno(), cmd->getName().c_str(), fit.key().c_str(), e.getMessage().str().c_str()); eos_static_info(sbuff); stdErr += sbuff; stdErr += "\n"; continue; } if (fmd->isLink()) { link = fmd->getLink(); } fmd->getSyncTime(stime); switch (cFlag) { case '>': if ((uint64_t) stime.tv_sec < cloneId) { continue; } case '+': if ((uint64_t) stime.tv_sec < cloneId) { break; } fmd->setCloneId((uint64_t) newId); fmd->setCloneFST(""); /* clean clone fid */ gOFS->eosFileService->updateStore(fmd.get()); break; case '=': case '-': if (fmd->getCloneId() != cloneId) { continue; } if (cFlag == '-' && cloneId > 9) { eos::common::RWMutexWriteLock lock(gOFS->eosViewRWMutex); std::string hex_fid = fmd->getCloneFST(); fmd->setCloneId(0); /* clear cloneId */ fmd->setCloneFST(""); /* clean up clone fid */ gOFS->eosFileService->updateStore(fmd.get()); lock.Release(); if (hex_fid != "") { eos::common::FileId::fileid_t clFid = eos::common::FileId::Hex2Fid( hex_fid.c_str()); try { std::shared_ptr gmd = gOFS->eosFileService->getFileMD(clFid); gOFS->_rem(gOFS->eosView->getUri(gmd.get()).c_str(), out_error, rootvid, "", false, true, true, true); } catch (eos::MDException& e) { eos_static_info("msg=\"exception\" ec=%d fid=%#lx emsg=\"%s\"\n", e.getErrno(), clFid, e.getMessage().str().c_str()); } } continue; } break; case '!': if (fmd->getCloneId() == 0 || (uint64_t)fmd->getCloneId() == cloneId) { continue; } break; case '?': break; default: /* do something intelligent */ ; } /* The output is produced in _cloneResp, outside the big lock */ _found.emplace_back(fmd->getId(), depth, false); } for (auto dit = eos::ContainerMapIterator(cmd); dit.valid(); dit.next()) { if (cFlag == '+') { gOFS->eosViewRWMutex.UnLockWrite(); } eos::Prefetcher::prefetchContainerMDWithChildrenAndWait(gOFS->eosView, dit.value(), false); if (cFlag == '+') { gOFS->eosViewRWMutex.LockWrite(); } try { ccmd = gOFS->eosDirectoryService->getContainerMD(dit.value()); } catch (eos::MDException& e) { errno = e.getErrno(); ccmd.reset(); eos_static_info("msg=\"exception\" ec=%d cid %#lx emsg=\"%s\"\n", e.getErrno(), dit.value(), e.getMessage().str().c_str()); continue; } ccmd->getTMTime(stime); /* if (cFlag == '?' && stime.tv_sec < cloneId) continue; only if stime reliably percolates down to the root */ uint64_t ccId = ccmd->getCloneId(); /* current container's cloneId */ if (ccId == 0 or cloneId == 0 or cFlag == '+' or cFlag == '!' or ((cFlag == '-' or cFlag == '=') && ccId == cloneId) ) { /* Only descend for matching subdirs */ int rc2 = _clone(ccmd, out_error, stdErr, vid, _found, cFlag, cloneId, newId, cloneMd, depth + 1); if (rc2 > rc) { rc = rc2; } } else eos_static_debug("Not descending into did:%lld ccId %lld cFlag '%c'", ccmd->getId(), ccId, cFlag); } if (cloneMd != NULL && depth == 0 && cFlag == '-') { /* clean up clone directory */ std::list ctrs2remove; std::list ctrs2zap; for (auto dit = eos::ContainerMapIterator(cloneMd); dit.valid(); dit.next()) { try { ccmd = gOFS->eosDirectoryService->getContainerMD(dit.value()); std::list files2remove; std::list files2zap; for (auto fit = eos::FileMapIterator(ccmd); fit.valid(); fit.next()) { try { fmd = gOFS->eosFileService->getFileMD(fit.value()); files2remove.push_back(gOFS->eosView->getUri(fmd.get())); } catch (eos::MDException& e) { char sbuff[1024]; int sblen = snprintf(sbuff, sizeof(sbuff), "exception ec=%d emsg=\"%s\" cid %#lx %s fid %#lx %s\n", e.getErrno(), e.getMessage().str().c_str(), dit.value(), ccmd->getName().c_str(), fit.value(), fit.key().c_str()); stdErr += sbuff; sbuff[sblen - 1] = '\0' /* no new-line */; eos_static_info(sbuff); files2zap.push_back(fit.key()); } } for (auto it = files2remove.begin(); it != files2remove.end(); it++) { try { gOFS->_rem((*it).c_str(), out_error, rootvid, "", false, true, true, true); } catch (eos::MDException& e) { eos_static_err("exception ec=%d emsg=\"%s\" cid %#lx uri %s\n", e.getErrno(), e.getMessage().str().c_str(), dit.value(), (*it).c_str()); } } for (auto it = files2zap.begin(); it != files2zap.end(); it++) { eos_static_info("zapping file %s in %s", it->c_str(), ccmd->getName().c_str()); ccmd->removeFile(*it); } ctrs2remove.push_back(gOFS->eosView->getUri(ccmd.get())); } catch (eos::MDException& e) { ccmd.reset(); eos_static_info("exception ec=%d emsg=\"%s\" cid %#lx name %s\n", e.getErrno(), e.getMessage().str().c_str(), dit.value(), dit.key().c_str()); ctrs2zap.push_back(dit.key()); continue; } } for (auto it = ctrs2remove.begin(); it != ctrs2remove.end(); it++) { try { gOFS->eosView->removeContainer(*it); } catch (eos::MDException& e) { char sbuff[4096]; int sblen = snprintf(sbuff, sizeof(sbuff), "exception ec=%d emsg=\"%s\" name %s\n", e.getErrno(), e.getMessage().str().c_str(), it->c_str()); stdErr += sbuff; out_error.setErrInfo(e.getErrno(), sbuff); sbuff[sblen - 1] = '\0' /* no new-line */; eos_static_info(sbuff); return SFS_ERROR; } } for (auto it = ctrs2zap.begin(); it != ctrs2zap.end(); it++) { eos_static_info("zapping %s", (*it).c_str()); try { cloneMd->removeContainer(*it); gOFS->eosDirectoryService->updateStore(cloneMd.get()); } catch (eos::MDException& e) { char sbuff[4096]; int sblen = snprintf(sbuff, sizeof(sbuff), "exception ec=%d emsg=\"%s\" name %s\n", e.getErrno(), e.getMessage().str().c_str(), it->c_str()); eos_static_debug(sbuff); out_error.setErrInfo(e.getErrno(), sbuff); sbuff[sblen - 1] = '\0' /* no new-line */; eos_static_info(sbuff); return SFS_ERROR; } } try { std::string cname = cloneMd->getName(); eos::ContainerIdentifier cloneDir = cloneMd->getParentIdentifier(); gOFS->eosView->removeContainer(gOFS->eosView->getUri(cloneMd.get())); gOFS->FuseXCastDeletion(cloneDir, cname); } catch (eos::MDException& e) { char sbuff[4096]; int sblen = snprintf(sbuff, sizeof(sbuff), "exception ec=%d emsg=\"%s\" name %s\n", e.getErrno(), e.getMessage().str().c_str(), cloneMd->getName().c_str()); out_error.setErrInfo(e.getErrno(), sbuff); sbuff[sblen - 1] = '\0' /* no new-line */; eos_static_info(sbuff); return SFS_ERROR; } } return rc; } //------------------------------------------------------------------------------ // Low-level namespace find command //------------------------------------------------------------------------------ int XrdMgmOfs::_find(const char* path, XrdOucErrInfo& out_error, XrdOucString& stdErr, eos::common::VirtualIdentity& vid, std::map >& found, const char* key, const char* val, bool no_files, time_t millisleep, bool nscounter, int maxdepth, const char* filematch, bool json_output, FILE* fstdout, time_t max_ctime_dir, time_t max_ctime_file, std::map* found_ctime_sec, int max_ctime_dir_min_deepness, int max_ctime_file_min_deepness) { std::vector< std::vector > found_dirs; std::shared_ptr cmd; std::string Path = path; EXEC_TIMING_BEGIN("Find"); if (nscounter) { gOFS->MgmStats.Add("Find", vid.uid, vid.gid, 1); } if (*Path.rbegin() != '/') { Path += '/'; } errno = 0; found_dirs.resize(1); found_dirs[0].resize(1); found_dirs[0][0] = Path.c_str(); int deepness = 0; // Users cannot return more than 100k files and 50k dirs with one find, // unless there is an access rule allowing deeper queries static uint64_t dir_limit = 50000; static uint64_t file_limit = 100000; Access::GetFindLimits(vid, dir_limit, file_limit); uint64_t filesfound = 0; uint64_t dirsfound = 0; bool limitresult = false; bool limited = false; bool fail_if_limited = (out_error.getErrInfo() == E2BIG); if ((vid.uid != 0) && (!vid.hasUid(3)) && (!vid.hasGid(4)) && (!vid.sudoer)) { limitresult = true; } bool isClone = (key != NULL) && (strcmp(key, "sys.clone") == 0); if (isClone) { /* sys.clone==, flag ~= [>=?-+], id ~= \d+ (a numeric timestamp) * flag: '>' list files modified after , '=' list files with clone-id * '-' = clean up clone-id , '+' - reclone after , * '?' = list all with clone-id and stime data */ char cFlag = val[0]; if (strchr(">=?-+!", cFlag) == NULL) { return SFS_ERROR; /* invalid argugment */ } time_t clone_id = atol(val + 1); /* could be 0 */ if (limitresult) { return SFS_ERROR; } eos::Prefetcher::prefetchContainerMDAndWait(gOFS->eosView, Path.c_str(), false); try { cmd = gOFS->eosView->getContainer(Path.c_str(), false); } catch (eos::MDException& e) { errno = e.getErrno(); cmd.reset(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } time_t newId = time(NULL); eos_static_info("sys.clone=%c%lld %s >%lld", cFlag, clone_id, Path.c_str(), newId); std::list<_cloneFoundItem> _found; int rc = _clone(cmd, out_error, stdErr, vid, _found, cFlag, clone_id, newId, NULL, 0); /* clone releases and re-acquires the eosViewRWMutex! */ if (rc == 0) { _cloneResp(out_error, stdErr, vid, _found, json_output, fstdout); } return rc; } do { bool permok = false; found_dirs.resize(deepness + 2); // Loop over all directories in that deepness for (unsigned int i = 0; i < found_dirs[deepness].size(); i++) { Path = found_dirs[deepness][i].c_str(); eos_static_debug("Listing files in directory %s", Path.c_str()); // Slow down the find command without holding locks if (millisleep) { std::this_thread::sleep_for(std::chrono::milliseconds(millisleep)); } // Held only for the current loop eos::Prefetcher::prefetchContainerMDWithChildrenAndWait(gOFS->eosView, Path.c_str(), false, no_files, limitresult, dir_limit, file_limit); eos::IContainerMD::IContainerMDReadLockerPtr cmdLock; try { cmdLock = gOFS->eosView->getContainerReadLocked(Path.c_str(), false); cmd = cmdLock->getUnderlyingPtr(); permok = cmd->access(vid.uid, vid.gid, R_OK | X_OK); } catch (eos::MDException& e) { errno = e.getErrno(); cmd.reset(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } eos::IContainerMD::ctime_t ctime; if (cmd) { cmd->getCTime(ctime); } if (cmd && max_ctime_dir && (deepness >= max_ctime_dir_min_deepness) && (ctime.tv_sec > max_ctime_dir)) { // skip directory entries which are newer than max_ctime continue; } if (!gOFS->allow_public_access(Path.c_str(), vid)) { stdErr += "error: public access level restriction - no access in "; stdErr += Path.c_str(); stdErr += "\n"; continue; } if (cmd) { if (!permok) { // check-out for ACLs permok = _access(Path.c_str(), R_OK | X_OK, out_error, vid, "") ? false : true; } if (!permok) { stdErr += "error: no permissions to read directory "; stdErr += Path.c_str(); stdErr += "\n"; continue; } // Add all children into the 2D vectors for (auto dit = eos::ContainerMapIterator(cmd); dit.valid(); dit.next()) { std::string fpath = Path.c_str(); fpath += dit.key(); fpath += "/"; // check if we select by tag if (key) { XrdOucString wkey = key; if (wkey.find("*") != STR_NPOS) { // this is a search for 'beginswith' match eos::IContainerMD::XAttrMap attrmap; if (!gOFS->_attr_ls(fpath.c_str(), out_error, vid, (const char*) 0, attrmap)) { for (auto it = attrmap.begin(); it != attrmap.end(); it++) { XrdOucString akey = it->first.c_str(); if (akey.matches(wkey.c_str())) { // Trick to add element (void)found[fpath].size(); } } } found_dirs[deepness + 1].push_back(fpath.c_str()); } else { // This is a search for a full match or a key search std::string sval = val; XrdOucString attr = ""; if (!gOFS->_attr_get(fpath.c_str(), out_error, vid, (const char*) 0, key, attr)) { found_dirs[deepness + 1].push_back(fpath.c_str()); if ((val == std::string("*")) || (attr == val)) { (void)found[fpath].size(); } } } } else { if (limitresult) { // Apply user limits for non root/admin/sudoers if (dirsfound >= dir_limit) { stdErr += "warning: find results are limited for you to ndirs="; stdErr += (int) dir_limit; stdErr += " - result is truncated!\n"; limited = true; break; } } found_dirs[deepness + 1].push_back(fpath.c_str()); (void)found[fpath].size(); dirsfound++; } } if (!no_files) { std::string link; std::string fname; for (auto fit = eos::FileMapIterator(cmd); fit.valid(); fit.next()) { fname = fit.key(); std::shared_ptr fmd; eos::IFileMD::IFileMDReadLockerPtr fmdLock; try { fmdLock = cmd->findFileReadLocked(fname); fmd = fmdLock->getUnderlyingPtr(); } catch (eos::MDException& e) { fmd = 0; } if (fmd) { // apply threadshold eos::IContainerMD::ctime_t ctime; fmd->getCTime(ctime); if (max_ctime_file && (deepness >= max_ctime_file_min_deepness) && (ctime.tv_sec > max_ctime_file)) { // skip file entries which are newer than max ctime continue; } // Skip symbolic links if (fmd->isLink()) { link = fmd->getLink(); } else { link.clear(); } if (limitresult) { // Apply user limits for non root/admin/sudoers if (filesfound >= file_limit) { stdErr += "warning: find results are limited for you to nfiles="; stdErr += (int) file_limit; stdErr += " - result is truncated!\n"; limited = true; break; } } if (!filematch) { if (link.length()) { std::string ip = fname; ip += " -> "; ip += link; found[Path].insert(ip); } else { found[Path].insert(fname); } if (found_ctime_sec) { (*found_ctime_sec)[Path] = ctime.tv_sec; } filesfound++; } else { XrdOucString name = fname.c_str(); if (name.matches(filematch)) { found[Path].insert(fname); if (found_ctime_sec) { (*found_ctime_sec)[Path] = ctime.tv_sec; } filesfound++; } } } } } } if (limited) { break; } } deepness++; if (limited) { break; } } while (found_dirs[deepness].size() && ((!maxdepth) || (deepness < maxdepth))); if (!no_files) { // If the result is empty, maybe this was a find by file if (!found.size()) { XrdSfsFileExistence file_exists; if (((_exists(Path.c_str(), file_exists, out_error, vid, 0)) == SFS_OK) && (file_exists == XrdSfsFileExistIsFile)) { eos::common::Path cPath(Path.c_str()); found[cPath.GetParentPath()].insert(cPath.GetName()); } } } // Include also the directory which was specified in the query if it is // accessible and a directory since it can evt. be missing if it is empty XrdSfsFileExistence dir_exists; if (((_exists(found_dirs[0][0].c_str(), dir_exists, out_error, vid, 0)) == SFS_OK) && (dir_exists == XrdSfsFileExistIsDirectory)) { eos::common::Path cPath(found_dirs[0][0].c_str()); (void) found[found_dirs[0][0].c_str()].size(); } if (nscounter) { EXEC_TIMING_END("Find"); } if (fail_if_limited && limited) { errno = E2BIG; return Emsg("_find", out_error, E2BIG, "query incomplete - too many files/dirs in tree", path); } else { errno = 0; return SFS_OK; } }