// ---------------------------------------------------------------------- // File: Rename.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 "namespace/utils/BulkNsObjectLocker.hh" // ----------------------------------------------------------------------- // This file is included source code in XrdMgmOfs.cc to make the code more // transparent without slowing down the compilation time. // ----------------------------------------------------------------------- /*----------------------------------------------------------------------------*/ int XrdMgmOfs::rename(const char* old_name, const char* new_name, XrdOucErrInfo& error, const XrdSecEntity* client, const char* infoO, const char* infoN) /*----------------------------------------------------------------------------*/ /* * @brief rename a file or directory * * @param old_name old name * @param new_name new name * @param error error object * @param client XRootD authentication object * @param infoO CGI of the old name * @param infoN CGI of the new name * @return SFS_OK on success otherwise SFS_ERROR * * There are three flavours of rename function, two external and one internal * implementation. See the internal implementation _rename for details. */ /*----------------------------------------------------------------------------*/ { static const char* epname = "rename"; const char* tident = error.getErrUser(); errno = 0; XrdOucString source, destination; XrdOucEnv renameo_Env(infoO); XrdOucEnv renamen_Env(infoN); XrdOucString oldn = old_name; XrdOucString newn = new_name; if (!renameo_Env.Get("eos.encodepath")) { oldn.replace("#space#", " "); } if (!renamen_Env.Get("eos.encodepath")) { newn.replace("#space#", " "); } if ((oldn.find(EOS_COMMON_PATH_VERSION_PREFIX) != STR_NPOS) || (newn.find(EOS_COMMON_PATH_VERSION_PREFIX) != STR_NPOS)) { errno = EINVAL; return Emsg(epname, error, EINVAL, "rename version files - use 'file versions' !"); } // Use a thread private vid eos::common::VirtualIdentity vid; EXEC_TIMING_BEGIN("IdMap"); eos::common::Mapping::IdMap(client, infoO, tident, vid, gOFS->mTokenAuthz, AOP_Update, newn.c_str()); EXEC_TIMING_END("IdMap"); eos_info("old-name=%s new-name=%s", old_name, new_name); gOFS->MgmStats.Add("IdMap", vid.uid, vid.gid, 1); const char* inpath = 0; const char* ininfo = 0; { inpath = oldn.c_str(); ininfo = infoO; AUTHORIZE(client, &renameo_Env, AOP_Delete, "rename", inpath, error); NAMESPACEMAP; BOUNCE_ILLEGAL_NAMES; oldn = path; } { inpath = newn.c_str(); ininfo = infoN; AUTHORIZE(client, &renamen_Env, AOP_Update, "rename", inpath, error); NAMESPACEMAP; BOUNCE_ILLEGAL_NAMES; newn = path; } BOUNCE_NOT_ALLOWED; ACCESSMODE_W; MAYSTALL; { const char* path = inpath; MAYREDIRECT; } return rename(oldn.c_str(), newn.c_str(), error, vid, infoO, infoN, true); } /*----------------------------------------------------------------------------*/ int XrdMgmOfs::rename(const char* old_name, const char* new_name, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, const char* infoO, const char* infoN, bool overwrite) /*----------------------------------------------------------------------------*/ /* * @brief rename a file or directory * * @param old_name old name * @param new_name new name * @param error error object * @param vid virtual identity of the client * @param infoO CGI of the old name * @param infoN CGI of the new name * @return SFS_OK on success otherwise SFS_ERROR * * There are three flavours of rename function, two external and one internal * implementation. See the internal implementation _rename for details. */ /*----------------------------------------------------------------------------*/ { static const char* epname = "rename"; errno = 0; XrdOucString source, destination; XrdOucEnv renameo_Env(infoO); XrdOucEnv renamen_Env(infoN); XrdOucString oldn = old_name; XrdOucString newn = new_name; const char* inpath = 0; const char* ininfo = 0; { inpath = old_name; ininfo = infoO; NAMESPACEMAP; BOUNCE_ILLEGAL_NAMES; oldn = path; } { inpath = new_name; ininfo = infoN; NAMESPACEMAP; BOUNCE_ILLEGAL_NAMES; newn = path; } BOUNCE_NOT_ALLOWED; ACCESSMODE_W; MAYSTALL; { const char* path = inpath; MAYREDIRECT; } // check access permissions on source if ((_access(oldn.c_str(), W_OK, error, vid, infoO) != SFS_OK)) { return SFS_ERROR; } // check access permissions on target if ((_access(newn.c_str(), W_OK, error, vid, infoN) != SFS_OK)) { return SFS_ERROR; } return _rename(oldn.c_str(), newn.c_str(), error, vid, infoO, infoN, true, false, overwrite); } /*----------------------------------------------------------------------------*/ /* Rename a file or directory * * @param old_name old name * @param new_name new name * @param error error object * @param vid virtual identity of the client * @param infoO CGI of the old name * @param infoN CGI of the new name * @param updateCTime indicates to update the change time of a directory * @param checkQuota indicates to check the quota during a rename operation * @param overwrite indicates if the target name can be overwritten * @return SFS_OK on success otherwise SFS_ERROR * * There are three flavours of rename function, two external and one internal * implementation. * Rename within a directory is simple since the quota accounting has not to * be modified. Rename of directories between quota nodes need to recompute * all the quota of the subtree which is moving and in case reject the operation * if there is not enough quota left. Overall it is a quite complex function. */ /*----------------------------------------------------------------------------*/ int XrdMgmOfs::_rename(const char* old_name, const char* new_name, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, const char* infoO, const char* infoN, bool updateCTime, bool checkQuota, bool overwrite, bool fusexcast) { static const char* epname = "_rename"; eos_info("source=%s target=%s overwrite=%d", old_name, new_name, overwrite); errno = 0; EXEC_TIMING_BEGIN("Rename"); eos::common::Timing tm("_rename"); COMMONTIMING("begin", &tm); eos::common::Path nPath(new_name); eos::common::Path oPath(old_name); std::string oP = oPath.GetParentPath(); std::string nP = nPath.GetParentPath(); if ((!old_name) || (!new_name)) { errno = EINVAL; return Emsg(epname, error, EINVAL, "rename - 0 source or target name"); } // If source and target are the same return success if (!strcmp(old_name, new_name)) { return SFS_OK; } gOFS->MgmStats.Add("Rename", vid.uid, vid.gid, 1); std::shared_ptr dir; std::shared_ptr newdir; std::shared_ptr rdir; std::shared_ptr file; bool renameFile = false; bool renameDir = false; bool renameVersion = false; bool findOk = false; bool quotaMove = false; XrdSfsFileExistence file_exists; std::string new_path = new_name; eos::Prefetcher::prefetchContainerMDAndWait(gOFS->eosView, nPath.GetParentPath()); eos::Prefetcher::prefetchContainerMDAndWait(gOFS->eosView, oPath.GetParentPath()); eos::Prefetcher::prefetchItemAndWait(gOFS->eosView, oPath.GetPath()); eos::Prefetcher::prefetchItemAndWait(gOFS->eosView, nPath.GetPath()); COMMONTIMING("prefetchItems", &tm); if (_exists(old_name, file_exists, error, vid, infoN)) { errno = ENOENT; return Emsg(epname, error, ENOENT, "rename - source does not exist"); } else { if (file_exists == XrdSfsFileExistNo) { errno = ENOENT; return Emsg(epname, error, ENOENT, "rename - source does not exist"); } if (file_exists == XrdSfsFileExistIsFile) { XrdSfsFileExistence version_exists; renameFile = true; XrdOucString vpath = nPath.GetPath(); if ((!_exists(oPath.GetVersionDirectory(), version_exists, error, vid, infoN)) && (version_exists == XrdSfsFileExistIsDirectory) && (!vpath.beginswith(oPath.GetVersionDirectory())) && (!vpath.beginswith(Recycle::gRecyclingPrefix.c_str()))) { renameVersion = true; } } if (file_exists == XrdSfsFileExistIsDirectory) { std::string n_path = nPath.GetPath(); std::string o_path = oPath.GetPath(); if ((n_path.at(n_path.length() - 1) != '/')) { n_path += "/"; } if ((o_path.at(o_path.length() - 1) != '/')) { o_path += "/"; } renameDir = true; // Check if old path is a subpath of new path if ((n_path.length() > o_path.length()) && (!n_path.compare(0, o_path.length(), o_path))) { errno = EINVAL; return Emsg(epname, error, EINVAL, "rename - old path is subpath of new path"); } // Check if old path is a quota node - this is forbidden try { auto rdirLocked = eosView->getContainerReadLocked(oPath.GetPath()); if (rdirLocked->getUnderlyingPtr()->getFlags() & eos::QUOTA_NODE_FLAG) { errno = EACCES; return Emsg(epname, error, EACCES, "rename - source is a quota node"); } } catch (eos::MDException& e) { errno = ENOENT; return Emsg(epname, error, ENOENT, "rename - source does not exist"); } } } if (!_exists(new_name, file_exists, error, vid, infoN)) { if (file_exists == XrdSfsFileExistIsFile) { if (new_path.back() == '/') { errno = ENOTDIR; return Emsg(epname, error, ENOTDIR, "rename - target is a not directory"); } if (overwrite && renameFile) { // Check if we are renaming a version to the primary copy bool keepversion = false; { XrdOucString op = oPath.GetParentPath(); XrdOucString vp = nPath.GetVersionDirectory(); if (op == vp) { keepversion = true; } } // Delete the existing target if (gOFS->_rem(new_name, error, vid, infoN, false, keepversion)) { return SFS_ERROR; } } else { errno = EEXIST; return Emsg(epname, error, EEXIST, "rename - target file name exists"); } } if (file_exists == XrdSfsFileExistIsDirectory) { // append the previous last name to the target path if (new_path.back() != '/') { new_path += "/"; } new_path += oPath.GetName(); new_name = new_path.c_str(); nPath = new_path; nP = nPath.GetParentPath(); // check if this directory exists already if (!_exists(new_name, file_exists, error, vid, infoN)) { if (file_exists == XrdSfsFileExistIsFile) { errno = EEXIST; return Emsg(epname, error, EEXIST, "rename - target directory is an existing file"); } if (file_exists == XrdSfsFileExistIsDirectory) { // Delete the existing target, if it empty it will work, otherwise it will fail if (gOFS->_remdir(new_name, error, vid, infoN)) { return SFS_ERROR; } } } } } else { if (!renameDir) { if (new_path.back() == '/') { // append the previous last name to the target path - nevertheless the parent won't exist new_path += oPath.GetName(); new_name = new_path.c_str(); nPath = new_path; nP = nPath.GetParentPath(); } } } COMMONTIMING("exists", &tm); // List of source files if a directory is renamed std::map > found; if (renameDir) { { // figure out if this is a move within the same quota node eos::IContainerMD::id_t q1 {0ull}; eos::IContainerMD::id_t q2 {0ull}; long long avail_files, avail_bytes; Quota::QuotaByPath(oPath.GetParentPath(), 0, 0, avail_files, avail_bytes, q1); Quota::QuotaByPath(nPath.GetParentPath(), 0, 0, avail_files, avail_bytes, q2); if (q1 != q2) { quotaMove = true; } } if (EOS_LOGS_DEBUG) { eos_debug("quotaMove = %d", quotaMove); } // For directory renaming which move into a different directory, we build // the list of files which we are moving if they move between quota nodes if ((oP != nP) && quotaMove) { XrdOucString stdErr; if (!gOFS->_find(oPath.GetFullPath().c_str(), error, stdErr, vid, found)) { findOk = true; } else { return Emsg(epname, error, errno, "rename - cannot do 'find' inside the source tree"); } COMMONTIMING("rename::dir_find_files_for_quota_move", &tm); } } { eos::mgm::FusexCastBatch fuse_batch; try { dir = eosView->getContainer(oPath.GetParentPath()); newdir = eosView->getContainer(nPath.GetParentPath()); // Translate to paths without symlinks std::string duri = eosView->getUri(dir.get()); std::string newduri = eosView->getUri(newdir.get()); // Get symlink-free dir's dir = eosView->getContainer(duri); newdir = eosView->getContainer(newduri); const eos::ContainerIdentifier did = dir->getIdentifier(); const eos::ContainerIdentifier pdid = dir->getParentIdentifier(); const eos::ContainerIdentifier ndid = newdir->getIdentifier(); const eos::ContainerIdentifier pndid = newdir->getParentIdentifier(); COMMONTIMING("rename::get_old_and_new_containers", &tm); if (renameFile) { if (oP == nP) { file = dir->findFile(oPath.GetName()); COMMONTIMING("rename::rename_file_within_same_container_find_file", &tm); if (file) { eos::IContainerMD::IContainerMDWriteLocker dirWriteLocker(dir); COMMONTIMING("rename::rename_file_within_same_container_dir_write_lock", &tm); eos::IFileMD::IFileMDWriteLocker fileWriteLocker(file); COMMONTIMING("rename::rename_file_within_same_container_file_write_lock", &tm); eosView->renameFile(file.get(), nPath.GetName()); dir->setMTimeNow(); dir->notifyMTimeChange(gOFS->eosDirectoryService); eosView->updateContainerStore(dir.get()); COMMONTIMING("rename::rename_file_within_same_container_file_rename", &tm); if (fusexcast) { const eos::FileIdentifier fid = file->getIdentifier(); fuse_batch.Register([&, did, pdid, fid]() { gOFS->FuseXCastRefresh(did, pdid); gOFS->FuseXCastRefresh(fid, did); }); } } } else { file = dir->findFile(oPath.GetName()); COMMONTIMING("rename::move_file_to_different_container_find_file", &tm); if (file) { // Move to a new directory // TODO: deal with conflicts and proper roll-back in case a file // with the same name already exists in the destination directory eos::IFileMD::IFileMDWriteLocker fileWriteLocker(file); COMMONTIMING("rename::move_file_to_different_container_file_write_lock", &tm); eos::BulkNsObjectLocker helper; helper.add(dir); helper.add(newdir); auto dirsLock = helper.lockAll(); COMMONTIMING("rename::move_file_to_different_container_lock_dirs", &tm); dir->removeFile(oPath.GetName()); dir->setMTimeNow(); dir->notifyMTimeChange(gOFS->eosDirectoryService); newdir->setMTimeNow(); newdir->notifyMTimeChange(gOFS->eosDirectoryService); eosView->updateContainerStore(dir.get()); eosView->updateContainerStore(newdir.get()); if (fusexcast) { const eos::FileIdentifier fid = file->getIdentifier(); const std::string old_name = oPath.GetName(); fuse_batch.Register([&, did, pdid, ndid, pndid, fid, old_name]() { gOFS->FuseXCastRefresh(did, pdid); gOFS->FuseXCastRefresh(ndid, pndid); gOFS->FuseXCastDeletion(did, old_name); gOFS->FuseXCastRefresh(fid, ndid); }); } file->setName(nPath.GetName()); file->setContainerId(newdir->getId()); if (updateCTime) { file->setCTimeNow(); } newdir->addFile(file.get()); eosView->updateFileStore(file.get()); COMMONTIMING("rename::move_file_to_different_container_rename", &tm); // Adjust the ns quota eos::IQuotaNode* old_qnode = eosView->getQuotaNode(dir.get()); eos::IQuotaNode* new_qnode = eosView->getQuotaNode(newdir.get()); if (old_qnode) { old_qnode->removeFile(file.get()); } if (new_qnode) { new_qnode->addFile(file.get()); } COMMONTIMING("rename::move_file_to_different_container_adjust_ns_quota", &tm); } } } if (renameDir) { rdir = dir->findContainer(oPath.GetName()); COMMONTIMING("rename::rename_dir_find_container", &tm); if (rdir) { { eos::BulkNsObjectLocker containerBulkLocker; containerBulkLocker.add(rdir); containerBulkLocker.add(newdir); auto containerLocks = containerBulkLocker.lockAll(); COMMONTIMING("rename::rename_dir_first_is_safe_to_rename_all_dirs_read_lock", &tm); if (!eos::isSafeToRename(gOFS->eosView, rdir.get(), newdir.get())) { errno = EINVAL; return Emsg(epname, error, EINVAL, "rename - old path is subpath of new path"); } COMMONTIMING("rename::rename_dir_first_is_safe_to_rename", &tm); } // Remove all the quota from the source node and add to the target node std::map >::const_reverse_iterator rfoundit; std::set::const_iterator fileit; // Loop over all the files and subtract them from their quota node if (findOk) { if (checkQuota) { { std::map user_del_size; std::map group_del_size; // Compute the total quota we need to rename by uid/gid for (rfoundit = found.rbegin(); rfoundit != found.rend(); rfoundit++) { // To compute the quota, we don't need to read-lock the entire tree as // it will anyway not be an atomic operation without the big namespace lock taken. for (fileit = rfoundit->second.begin(); fileit != rfoundit->second.end(); fileit++) { std::string fspath = rfoundit->first; fspath += *fileit; std::shared_ptr fmd = std::shared_ptr((eos::IFileMD*)0); // Stat this file and add to the deletion maps try { fmd = gOFS->eosView->getFile(fspath.c_str(), false); } catch (eos::MDException& e) { // Check if this is a symbolic link std::string fname = *fileit; size_t link_pos = fname.find(" -> "); if (link_pos != std::string::npos) { fname.erase(link_pos); fspath = rfoundit->first; fspath += fname; try { fmd = gOFS->eosView->getFile(fspath.c_str(), false); } catch (eos::MDException& e) { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); } } else { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); } } if (fmd) { eos::IFileMD::IFileMDReadLocker locker(fmd); if (!fmd->isLink()) { // compute quotas to check user_del_size[fmd->getCUid()] += (fmd->getSize() * eos::common::LayoutId::GetSizeFactor(fmd->getLayoutId())); group_del_size[fmd->getCGid()] += (fmd->getSize() * eos::common::LayoutId::GetSizeFactor(fmd->getLayoutId())); } } else { return Emsg(epname, error, errno, "rename - cannot stat file in subtree", fspath.c_str()); } } } COMMONTIMING("rename::rename_dir_compute_quotas_to_check", &tm); // Verify for each uid/gid that there is enough quota to rename bool userok = true; bool groupok = true; // Either all have user quota therefore userok is true for (auto it = user_del_size.begin(); it != user_del_size.end(); ++it) { if (!Quota::Check(nP, it->first, Quota::gProjectId, it->second, 1)) { userok = false; break; } } // or all have group quota therefore groupok is true for (auto it = group_del_size.begin(); it != group_del_size.end(); it++) { if (!Quota::Check(nP, Quota::gProjectId, it->first, it->second, 1)) { groupok = false; break; } } if ((!userok) || (!groupok)) { // Deletion will fail as there is not enough quota on the target return Emsg(epname, error, ENOSPC, "rename - cannot get all " "the needed quota for the target directory"); } } COMMONTIMING("rename::rename_dir_check_quotas", &tm); } // if (checkQuota) for (rfoundit = found.rbegin(); rfoundit != found.rend(); rfoundit++) { // Loop through every files // To compute the quota, we don't need to read-lock the entire tree as // it will anyway not be an atomic operation without the big namespace lock taken. for (fileit = rfoundit->second.begin(); fileit != rfoundit->second.end(); fileit++) { std::string fspath = rfoundit->first; fspath += *fileit; std::string fname = *fileit; if (fname.find(" -> ") != std::string::npos) { // Skip symlinks continue; } try { file = gOFS->eosView->getFile(fspath.c_str()); } catch (eos::MDException& e) { // Check if this is a symbolic link std::string fname = *fileit; size_t link_pos = fname.find(" -> "); if (link_pos != std::string::npos) { fname.erase(link_pos); fspath = rfoundit->first; fspath += fname; try { file = gOFS->eosView->getFile(fspath.c_str(), false); } catch (eos::MDException& e) { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); } } else { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); } } if (file) { eos::IFileMD::IFileMDReadLocker locker(file); if (!file->isLink()) { // Get quota nodes from file path and target directory eos::IQuotaNode* old_qnode = eosView->getQuotaNode(rdir.get()); eos::IQuotaNode* new_qnode = eosView->getQuotaNode(newdir.get()); if (old_qnode) { old_qnode->removeFile(file.get()); } if (new_qnode) { new_qnode->addFile(file.get()); } } } } } COMMONTIMING("rename::rename_dir_apply_quotas", &tm); } if (nP == oP) { // Rename within a container // Lock the containers eos::BulkNsObjectLocker bulkContainerLocker; bulkContainerLocker.add(rdir); bulkContainerLocker.add(dir); auto containerLocks = bulkContainerLocker.lockAll(); COMMONTIMING("rename::rename_dir_within_same_container_dirs_lock_write", &tm); eosView->renameContainer(rdir.get(), nPath.GetName()); if (updateCTime) { rdir->setCTimeNow(); } dir->setMTimeNow(); dir->notifyMTimeChange(gOFS->eosDirectoryService); eosView->updateContainerStore(rdir.get()); eosView->updateContainerStore(dir.get()); const eos::ContainerIdentifier rdid = rdir->getIdentifier(); fuse_batch.Register([&, rdid, did, pdid]() { gOFS->FuseXCastRefresh(rdid, did); gOFS->FuseXCastRefresh(did, pdid); }); COMMONTIMING("rename::rename_dir_within_same_container", &tm); } else { { eos::BulkNsObjectLocker bulkDirLocker; bulkDirLocker.add(rdir); bulkDirLocker.add(newdir); auto dirLocks = bulkDirLocker.lockAll(); COMMONTIMING("rename::rename_dir_second_is_safe_to_rename_all_dirs_read_lock", &tm); // Do the check once again, because we're paranoid if (!eos::isSafeToRename(gOFS->eosView, rdir.get(), newdir.get())) { eos_static_crit( "%s", SSTR("Unsafe rename of container " << rdir->getId() << " -> " << newdir->getId() << " was prevented at the last resort check") .c_str()); errno = EINVAL; return Emsg( epname, error, EINVAL, "rename - old path is subpath " "of new path - caught by last resort check, quotanodes " "may have become inconsistent"); } COMMONTIMING("rename::rename_dir_second_is_safe_to_rename", &tm); } // Remove from one container to another one eos::BulkNsObjectLocker bulkContainerLocker; bulkContainerLocker.add(dir); bulkContainerLocker.add(rdir); bulkContainerLocker.add(newdir); auto containerLocks = bulkContainerLocker.lockAll(); COMMONTIMING("rename::move_dir_all_dirs_write_lock", &tm); unsigned long long tree_size = rdir->getTreeSize(); { // update the source directory - remove the directory dir->removeContainer(oPath.GetName()); dir->setMTimeNow(); dir->notifyMTimeChange(gOFS->eosDirectoryService); if (gOFS->eosContainerAccounting) { gOFS->eosContainerAccounting->RemoveTree(dir.get(), tree_size); } eosView->updateContainerStore(dir.get()); COMMONTIMING("rename::move_dir_remove_source_tree", &tm); fuse_batch.Register([&, did, pdid]() { gOFS->FuseXCastDeletion(did, oPath.GetName()); gOFS->FuseXCastRefresh(did, pdid); }); } { // rename the moved directory and udpate it's parent ID rdir->setName(nPath.GetName()); rdir->setParentId(newdir->getId()); if (updateCTime) { rdir->setCTimeNow(); } eosView->updateContainerStore(rdir.get()); const eos::ContainerIdentifier rdid = rdir->getIdentifier(); const eos::ContainerIdentifier prdid = rdir->getParentIdentifier(); fuse_batch.Register([&, rdid, prdid]() { gOFS->FuseXCastRefresh(rdid, prdid); }); COMMONTIMING("rename::move_dir_rename_moved_dir", &tm); } { // update the target directory - add the directory newdir->addContainer(rdir.get()); newdir->setMTimeNow(); if (gOFS->eosContainerAccounting) { gOFS->eosContainerAccounting->AddTree(newdir.get(), tree_size); } newdir->notifyMTimeChange(gOFS->eosDirectoryService); eosView->updateContainerStore(newdir.get()); fuse_batch.Register([&, ndid, pndid]() { gOFS->FuseXCastRefresh(ndid, pndid); }); COMMONTIMING("rename::move_dir_update_target_directory_add_old_dir", &tm); } } } file.reset(); } } catch (eos::MDException& e) { dir.reset(); file.reset(); errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n", e.getErrno(), e.getMessage().str().c_str()); } } std::ostringstream oss; oss << "renamed " << oPath.GetFullPath() << " to " << nPath.GetFullPath() << " timing=" << tm.Dump(); eos_static_debug(oss.str().c_str()); if ((!dir) || ((!file) && (!rdir))) { errno = ENOENT; return Emsg(epname, error, ENOENT, "rename", old_name); } // check if this was a versioned file if (renameVersion) { // rename also the version directory if (_rename(oPath.GetVersionDirectory(), nPath.GetVersionDirectory(), error, vid, infoO, infoN, false, false, false)) { return SFS_ERROR; } } COMMONTIMING("end", &tm); EXEC_TIMING_END("Rename"); return SFS_OK; }