// ----------------------------------------------------------------------
// File: Chown.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.
// -----------------------------------------------------------------------
/*----------------------------------------------------------------------------*/
int
XrdMgmOfs::_chown(const char* path,
uid_t uid,
gid_t gid,
XrdOucErrInfo& error,
eos::common::VirtualIdentity& vid,
const char* ininfo,
bool nodereference)
/*----------------------------------------------------------------------------*/
/*
* @brief change the owner of a file or directory
*
* @param path directory path to change
* @param uid user id to set
* @param gid group id to set
* @param error error object
* @param vid virtual identity of the client
* @param ininfo CGI
* @param specify if we shouldn't follow symlinks
* @return SFS_OK on success otherwise SFS_ERROR
*
* Chown has only an internal implementation because XRootD does not support
* this operation in the Ofs interface. root can always run the operation.
* Users with the admin role can run the operation. Normal users can run the operation
* if they have the 'c' permissions in 'sys.acl'. File ownership can only be changed
* with the root or admin role. If uid,gid=0xffffffff, we don't set the uid/group
*/
/*----------------------------------------------------------------------------*/
{
static const char* epname = "chown";
EXEC_TIMING_BEGIN("Chown");
// ---------------------------------------------------------------------------
std::shared_ptr cmd;
std::shared_ptr fmd;
errno = 0;
gOFS->MgmStats.Add("Chown", vid.uid, vid.gid, 1);
eos_info("path=%s uid=%u gid=%u", path, uid, gid);
eos::common::RWMutexWriteLock lock(gOFS->eosViewRWMutex);
// try as a directory
try {
eos::IContainerMD::XAttrMap attrmap;
eos::common::Path cPath(path);
cmd = gOFS->eosView->getContainer(path, !nodereference);
eos::listAttributes(gOFS->eosView, cmd.get(), attrmap, false);
// ACL and permission check
Acl acl;
if (uid != vid.uid) {
// if the user is not the owner, user acls are removed
attrmap["user.acl"] = "";
}
acl.SetFromAttrMap(attrmap, vid); /* also takes care of eval.useracl */
eos_static_debug("sys.acl %s acl.CanChown() %d", attrmap["sys.acl"].c_str(),
acl.CanChown());
if (((vid.uid) && (!vid.hasUid(3) && !vid.hasGid(4)) &&
!acl.CanChown()) ||
((vid.uid) && !acl.IsMutable())) {
errno = EPERM;
} else {
if ((unsigned int) uid != 0xffffffff) {
// Change the owner
cmd->setCUid(uid);
}
if (((!vid.uid) || (vid.uid == 3) || (vid.gid == 4)) &&
((unsigned int)gid != 0xffffffff)) {
// Change the group
cmd->setCGid(gid);
}
cmd->setCTimeNow();
eosView->updateContainerStore(cmd.get());
lock.Release();
gOFS->FuseXCastRefresh(cmd->getIdentifier(), cmd->getParentIdentifier());
errno = 0;
}
} catch (eos::MDException& e) {
errno = e.getErrno();
}
if (!cmd) {
errno = 0;
try {
// Try as a file
eos::common::Path cPath(path);
cmd = gOFS->eosView->getContainer(cPath.GetParentPath());
if (!nodereference) {
// Translate to path without symlinks
std::string uri_cmd = eosView->getUri(cmd.get());
cmd = eosView->getContainer(uri_cmd);
}
eos::IQuotaNode* ns_quota = gOFS->eosView->getQuotaNode(cmd.get());
// ACL and permission check
eos::IContainerMD::XAttrMap attrmap;
gOFS->_attr_ls(cPath.GetParentPath(), error, vid, 0, attrmap, false);
Acl acl;
if (uid != vid.uid) {
// if the user is not the owner, user acls are removed
attrmap["user.acl"] = "";
}
acl.SetFromAttrMap(attrmap, vid); /* also takes care of eval.useracl */
eos_static_debug("sys.acl %s acl.CanChown() %d", attrmap["sys.acl"].c_str(),
acl.CanChown());
if ((vid.uid) && (!vid.sudoer) && (vid.uid != 3) && (vid.gid != 4) &&
!acl.CanChown()) {
errno = EPERM;
} else {
eos_info("dereference %d", nodereference);
fmd = gOFS->eosView->getFile(path, !nodereference);
eos_info("dereference %d", nodereference);
// Subtract the file
if (ns_quota) {
ns_quota->removeFile(fmd.get());
}
// Change the owner
if ((unsigned int) uid != 0xffffffff) {
fmd->setCUid(uid);
}
// Change the group
if (!vid.uid && ((unsigned int) gid != 0xffffffff)) {
fmd->setCGid(gid);
}
// Re-add the file
if (ns_quota) {
ns_quota->addFile(fmd.get());
}
fmd->setCTimeNow();
eosView->updateFileStore(fmd.get());
lock.Release();
gOFS->FuseXCastRefresh(fmd->getIdentifier(), cmd->getParentIdentifier());
}
} catch (eos::MDException& e) {
errno = e.getErrno();
}
}
// ---------------------------------------------------------------------------
if (cmd && (!errno)) {
EXEC_TIMING_END("Chmod");
return SFS_OK;
}
return Emsg(epname, error, errno, "chown", path);
}