// ----------------------------------------------------------------------
// File: Remdir.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 .*
************************************************************************/
/*----------------------------------------------------------------------------*/
int
XrdMgmOfs::remdir(const char* inpath,
XrdOucErrInfo& error,
const XrdSecEntity* client,
const char* ininfo)
/*----------------------------------------------------------------------------*/
/*
* @brief delete a directory from the namespace
*
* @param inpath directory to delete
* @param error error object
* @param client XRootD authentication object
* @param ininfo CGI
* @return SFS_OK on success otherwise SFS_ERROR
*
*/
/*----------------------------------------------------------------------------*/
{
static const char* epname = "remdir";
const char* tident = error.getErrUser();
eos::common::VirtualIdentity vid;
EXEC_TIMING_BEGIN("IdMap");
eos::common::Mapping::IdMap(client, ininfo, tident, vid, gOFS->mTokenAuthz,
AOP_Delete, inpath);
EXEC_TIMING_END("IdMap");
NAMESPACEMAP;
NAMESPACE_NO_TRAILING_SLASH;
BOUNCE_ILLEGAL_NAMES;
TOKEN_SCOPE;
XrdOucEnv remdir_Env(ininfo);
AUTHORIZE(client, &remdir_Env, AOP_Delete, "remove", inpath, error);
gOFS->MgmStats.Add("IdMap", vid.uid, vid.gid, 1);
BOUNCE_NOT_ALLOWED;
ACCESSMODE_W;
MAYSTALL;
MAYREDIRECT;
return _remdir(path, error, vid, ininfo);
}
/*----------------------------------------------------------------------------*/
int
XrdMgmOfs::_remdir(const char* path,
XrdOucErrInfo& error,
eos::common::VirtualIdentity& vid,
const char* ininfo,
bool simulate)
/*----------------------------------------------------------------------------*/
/*
* @brief delete a directory from the namespace
*
* @param inpath directory to delete
* @param error error object
* @param vid virtual identity of the client
* @param ininfo CGI
* @return SFS_OK on success otherwise SFS_ERROR
*
* We support a special ACL to forbid deletion if it would be allowed by the
* normal POSIX settings (ACL !d flag).
*/
/*----------------------------------------------------------------------------*/
{
static const char* epname = "remdir";
errno = 0;
eos_info("path=%s", path);
EXEC_TIMING_BEGIN("RmDir");
gOFS->MgmStats.Add("RmDir", vid.uid, vid.gid, 1);
std::shared_ptr dhpar;
std::shared_ptr dh;
eos::common::Path cPath(path);
eos::IContainerMD::XAttrMap attrmap;
// Make sure this is not a quota node
std::string qpath = path;
if (Quota::Exists(qpath)) {
errno = EBUSY;
return Emsg(epname, error, errno, "rmdir - this is a quota node",
qpath.c_str());
}
eos::common::RWMutexWriteLock viewLock(gOFS->eosViewRWMutex);
std::string aclpath;
uid_t container_owner_uid = 0;
bool container_vtx = false;
try {
dh = gOFS->eosView->getContainer(path);
eos::common::Path pPath(gOFS->eosView->getUri(dh.get()).c_str());
dhpar = gOFS->eosView->getContainer(pPath.GetParentPath());
container_owner_uid = dh->getCUid();
container_vtx = dhpar->getMode() & S_ISVTX;
aclpath = pPath.GetParentPath();
} catch (eos::MDException& e) {
dh.reset();
dhpar.reset();
errno = e.getErrno();
eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n",
e.getErrno(), e.getMessage().str().c_str());
}
eos_info("path=\"%s\" scope=\"%s\" aclpath=\"%s\"", path, vid.scope.c_str(),
aclpath.c_str());
// check existence
if (!dh) {
errno = ENOENT;
return Emsg(epname, error, errno, "rmdir", path);
}
// ACL and permission check
Acl acl(aclpath.c_str(), error, vid, attrmap, false);
if (vid.uid && !acl.IsMutable()) {
errno = EPERM;
return Emsg(epname, error, EPERM, "rmdir - immutable", path);
}
if (!gOFS->allow_public_access(aclpath.c_str(), vid)) {
errno = EACCES;
return Emsg(epname, error, EACCES, "access - public access level restriction",
aclpath.c_str());
}
if (ininfo) {
XrdOucEnv env_info(ininfo);
if (env_info.Get("mgm.option")) {
XrdOucString option = env_info.Get("mgm.option");
if (option == "r") {
// Recursive delete - need to unlock before calling the proc function
viewLock.Release();
ProcCommand cmd;
XrdOucString info = "mgm.cmd=rm&mgm.option=r&mgm.path=";
info += path;
cmd.open("/proc/user", info.c_str(), vid, &error);
cmd.close();
int rc = cmd.GetRetc();
if (rc) {
return Emsg(epname, error, rc, "rmdir", path);
}
return SFS_OK;
}
}
}
bool stdpermcheck = false;
bool aclok = false;
if (acl.HasAcl() && !container_vtx) {
// acls only if this is not a VTX directory
if ((dh->getCUid() != vid.uid) &&
(vid.uid) && // not the root user
(vid.uid != 3) && // not the admin user
(vid.gid != 4) && // not the admin group
(acl.CanNotDelete())) { // acl does not allow deletion
// deletion is explicitly forbidden
errno = EPERM;
return Emsg(epname, error, EPERM, "rmdir by ACL", path);
}
if ((!acl.CanWrite())) {
// we have to check the standard permissions
stdpermcheck = true;
} else {
aclok = true;
}
} else {
stdpermcheck = true;
}
// Check permissions
bool permok = stdpermcheck ? (dhpar ? (dhpar->access(vid.uid, vid.gid,
X_OK | W_OK)) : false) : aclok;
if (container_vtx) {
if (vid.uid) {
if (container_owner_uid != vid.uid) {
// only the owner can delete
errno = EPERM;
return Emsg(epname, error, errno, "rmdir", path);
}
}
} else {
// need for standard perm check
if (!permok) {
errno = EPERM;
return Emsg(epname, error, errno, "rmdir", path);
}
}
if ((dh->getFlags() && eos::QUOTA_NODE_FLAG) && (vid.uid)) {
errno = EADDRINUSE;
eos_err("%s is a quota node - deletion canceled", path);
return Emsg(epname, error, errno, "rmdir - this is a quota node", path);
}
if (!simulate) {
try {
eos::ContainerIdentifier dhpar_id;
eos::ContainerIdentifier dhpar_pid;
std::string dh_name;
// update the in-memory modification time of the parent directory
if (dhpar) {
dhpar->setMTimeNow();
dhpar->notifyMTimeChange(gOFS->eosDirectoryService);
eosView->updateContainerStore(dhpar.get());
dhpar_id = dhpar->getIdentifier();
dhpar_pid = dhpar->getParentIdentifier();
dh_name = dh->getName();
}
eosView->removeContainer(path);
viewLock.Release();
if (dhpar) {
gOFS->FuseXCastDeletion(dhpar_id, dh_name);
gOFS->FuseXCastRefresh(dhpar_id, dhpar_pid);
}
} catch (eos::MDException& e) {
errno = e.getErrno();
eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n",
e.getErrno(), e.getMessage().str().c_str());
}
}
viewLock.Release();
EXEC_TIMING_END("RmDir");
if (errno) {
if (errno == ENOTEMPTY) {
return Emsg(epname, error, errno, "rmdir - Directory not empty", path);
} else {
return Emsg(epname, error, errno, "rmdir", path);
}
} else {
return SFS_OK;
}
}