// ---------------------------------------------------------------------- // File: proc/user/Rm.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/proc/ProcInterface.hh" #include "mgm/XrdMgmOfs.hh" #include "mgm/XrdMgmOfsDirectory.hh" #include "mgm/Access.hh" #include "mgm/Quota.hh" #include "mgm/Recycle.hh" #include "mgm/Macros.hh" #include "common/Path.hh" #include EOSMGMNAMESPACE_BEGIN int ProcCommand::Rm() { XrdOucString spath = ""; XrdOucString spathid = pOpaque->Get("mgm.file.id"); XrdOucString scontainerid = pOpaque->Get("mgm.container.id"); if (spathid.length()) { GetPathFromFid(spath, std::strtoull(spathid.c_str(), nullptr, 10), "error: "); } else { if (scontainerid.length()) { GetPathFromCid(spath, std::strtoull(scontainerid.c_str(), nullptr, 10), "error: "); } else { spath = pOpaque->Get("mgm.path"); } } const char* inpath = spath.c_str(); XrdOucString option = pOpaque->Get("mgm.option"); XrdOucString deep = pOpaque->Get("mgm.deletion"); eos::common::Path cPath(inpath); bool force = (option.find("f") != STR_NPOS); XrdOucString filter = ""; std::set rmList; NAMESPACEMAP; PROC_BOUNCE_ILLEGAL_NAMES; PROC_BOUNCE_NOT_ALLOWED; spath = path; PROC_TOKEN_SCOPE; if (force && (vid.uid)) { stdErr = "warning: removing the force flag - this is only allowed for the 'root' role!\n"; force = false; } if (!spath.length()) { stdErr = "error: you have to give a path name to call 'rm'"; retc = EINVAL; } else { if (spath.find("*") != STR_NPOS) { // this is wildcard deletion eos::common::Path cPath(spath.c_str()); spath = cPath.GetParentPath(); filter = cPath.GetName(); } // check if this file exists XrdSfsFileExistence file_exists; if (gOFS->_exists(spath.c_str(), file_exists, *mError, *pVid, nullptr)) { stdErr += "error: unable to run exists on path '"; stdErr += spath.c_str(); stdErr += "'"; retc = errno; return SFS_OK; } if (file_exists == XrdSfsFileExistNo) { stdErr += "error: no such file or directory with path '"; stdErr += spath.c_str(); stdErr += "'"; retc = ENOENT; return SFS_OK; } if (file_exists == XrdSfsFileExistIsFile) { // if we have rm -r we remove the -r flag option.replace("r", ""); } if ((file_exists == XrdSfsFileExistIsDirectory) && filter.length()) { regex_t regex_filter; // Adding regex anchors for beginning and end of string XrdOucString filter_temp = "^"; // Changing wildcard * into regex syntax filter.replace("*", ".*"); filter_temp += filter; filter_temp += "$"; int reg_rc = regcomp(&(regex_filter), filter_temp.c_str(), REG_EXTENDED | REG_NEWLINE); if (reg_rc) { stdErr += "error: failed to compile filter regex "; stdErr += filter_temp.c_str(); retc = EINVAL; return SFS_OK; } XrdMgmOfsDirectory dir; // list the path and match against filter int listrc = dir.open(spath.c_str(), *pVid, nullptr); if (!listrc) { const char* val; while ((val = dir.nextEntry())) { XrdOucString mpath = spath; XrdOucString entry = val; mpath += val; if ((entry == ".") || (entry == "..")) { continue; } if (!regexec(®ex_filter, entry.c_str(), 0, nullptr, 0)) { rmList.insert(mpath.c_str()); } } } regfree(®ex_filter); // if we have rm * (whatever wildcard) we remove the -r flag option.replace("r", ""); } else { rmList.insert(spath.c_str()); } // find everything to be deleted if (option.find("r") != STR_NPOS) { std::map > found; std::map >::const_reverse_iterator rfoundit; std::set::const_iterator fileit; if (((cPath.GetSubPathSize() < 4) && (deep != "deep")) || (gOFS->_find(spath.c_str(), *mError, stdErr, *pVid, found))) { if ((cPath.GetSubPathSize() < 4) && (deep != "deep")) { stdErr += "error: deep recursive deletes are forbidden without shell confirmation code!"; retc = EPERM; } else { stdErr += "error: unable to remove file/directory"; retc = errno; } } else { XrdOucString recyclingAttribute; if (!force) { // only recycle if there is no '-f' flag int rpos; if ((rpos = spath.find("/.sys.v#.")) == STR_NPOS) { // check if this path has a recycle attribute gOFS->_attr_get(spath.c_str(), *mError, *pVid, "", Recycle::gRecyclingAttribute.c_str(), recyclingAttribute); } else { XrdOucString ppath = spath; ppath.erase(rpos); // get it from the parent directory for version directories gOFS->_attr_get(ppath.c_str(), *mError, *pVid, "", Recycle::gRecyclingAttribute.c_str(), recyclingAttribute); } } //....................................................................... // see if we have a recycle policy set //....................................................................... if (recyclingAttribute.length() && (!spath.beginswith(Recycle::gRecyclingPrefix.c_str()))) { //..................................................................... // two step deletion via recycle bin //..................................................................... // delete files in simulation mode std::map user_deletion_size; std::map group_deletion_size; for (rfoundit = found.rbegin(); rfoundit != found.rend(); rfoundit++) { int rpos = 0; if ((rpos = rfoundit->first.find("/.sys.v#.")) == STR_NPOS) { // skip to check version files for (fileit = rfoundit->second.begin(); fileit != rfoundit->second.end(); fileit++) { std::string fspath = rfoundit->first; size_t l_pos; std::string entry = *fileit; if ((l_pos = entry.find(" ->")) != std::string::npos) { entry.erase(l_pos); } fspath += entry; if (gOFS->_rem(fspath.c_str(), *mError, *pVid, nullptr, true)) { stdErr += "error: unable to remove file - bulk deletion aborted\n"; retc = errno; return SFS_OK; } } } } // delete directories in simulation mode for (rfoundit = found.rbegin(); rfoundit != found.rend(); rfoundit++) { // don't even try to delete the root directory std::string fspath = rfoundit->first; if (fspath == "/") { continue; } if (gOFS->_remdir(rfoundit->first.c_str(), *mError, *pVid, nullptr, true) && (errno != ENOENT)) { stdErr += "error: unable to remove directory - bulk deletion aborted\n"; retc = errno; return SFS_OK; } } struct stat buf; if (gOFS->_stat(spath.c_str(), &buf, *mError, *pVid, "")) { stdErr = "error: failed to stat bulk deletion directory: "; stdErr += spath.c_str(); retc = errno; return SFS_OK; } spath += "/"; eos::mgm::Recycle lRecycle(spath.c_str(), recyclingAttribute.c_str(), pVid, buf.st_uid, buf.st_gid, (unsigned long long) buf.st_ino); int rc = 0; if ((rc = lRecycle.ToGarbage("rm-r", *mError))) { stdErr = "error: failed to recycle path "; stdErr += path; stdErr += "\n"; stdErr += mError->getErrText(); retc = mError->getErrInfo(); return SFS_OK; } else { stdOut += "success: you can recycle this deletion using 'recycle restore "; char sp[256]; snprintf(sp, sizeof(sp) - 1, "%016llx", (unsigned long long) buf.st_ino); stdOut += sp; stdOut += "'\n"; retc = 0; return SFS_OK; } } else { //..................................................................... // standard way to delete files recursively //..................................................................... // delete files starting at the deepest level for (rfoundit = found.rbegin(); rfoundit != found.rend(); rfoundit++) { for (fileit = rfoundit->second.begin(); fileit != rfoundit->second.end(); fileit++) { std::string fspath = rfoundit->first; size_t l_pos; std::string entry = *fileit; if ((l_pos = entry.find(" ->")) != std::string::npos) { entry.erase(l_pos); } fspath += entry; if (gOFS->_rem(fspath.c_str(), *mError, *pVid, (const char*) 0, false, false, force)) { stdErr += "error: unable to remove file : \n"; stdErr += fspath.c_str(); retc = errno; } } } // delete directories starting at the deepest level for (rfoundit = found.rbegin(); rfoundit != found.rend(); rfoundit++) { // don't even try to delete the root directory std::string fspath = rfoundit->first; if (fspath == "/") { continue; } if (gOFS->_remdir(rfoundit->first.c_str(), *mError, *pVid, nullptr)) { if (errno != ENOENT) { stdErr += "error: unable to remove directory : "; stdErr += rfoundit->first.c_str(); stdErr += "; reason: "; stdErr += mError->getErrText(); retc = errno; } } } } } } else { for (auto it = rmList.begin(); it != rmList.end(); ++it) { if (gOFS->_rem(it->c_str(), *mError, *pVid, nullptr, false, false, force) && (errno != ENOENT)) { stdErr += "error: unable to remove file/directory '"; stdErr += it->c_str(); stdErr += "'"; retc |= errno; } } } } return SFS_OK; } EOSMGMNAMESPACE_END