// ----------------------------------------------------------------------
// File: Link.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::symlink(const char* source_name,
const char* target_name,
XrdOucErrInfo& error,
const XrdSecEntity* client,
const char* infoO,
const char* infoN)
/*----------------------------------------------------------------------------*/
/*
* @brief symlink a file or directory
*
* @param source_name source name
* @param target_name target 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
*
*/
/*----------------------------------------------------------------------------*/
{
static const char* epname = "symlink";
const char* tident = error.getErrUser();
errno = 0;
XrdOucString source, destination;
XrdOucEnv symlinko_Env(infoO);
XrdOucEnv symlinkn_Env(infoN);
XrdOucString sourcen = source_name;
XrdOucString targetn = target_name;
if (!symlinko_Env.Get("eos.encodepath")) {
sourcen.replace("#space#", " ");
}
if (!symlinkn_Env.Get("eos.encodepath")) {
targetn.replace("#space#", " ");
}
// Use a thread private vid
eos::common::VirtualIdentity vid;
EXEC_TIMING_BEGIN("IdMap");
eos::common::Mapping::IdMap(client, infoO, tident, vid, gOFS->mTokenAuthz,
AOP_Create, sourcen.c_str());
EXEC_TIMING_END("IdMap");
eos_info("old-name=%s new-name=%s", source_name, target_name);
gOFS->MgmStats.Add("IdMap", vid.uid, vid.gid, 1);
const char* inpath = 0;
const char* ininfo = 0;
{
inpath = sourcen.c_str();
ininfo = infoO;
AUTHORIZE(client, &symlinko_Env, AOP_Create, "link", inpath, error);
NAMESPACEMAP;
BOUNCE_ILLEGAL_NAMES;
sourcen = path;
}
{
inpath = targetn.c_str();
ininfo = infoN;
NAMESPACEMAP;
BOUNCE_ILLEGAL_NAMES;
targetn = path;
}
BOUNCE_NOT_ALLOWED;
ACCESSMODE_W;
MAYSTALL;
{
const char* path = inpath;
MAYREDIRECT;
}
return symlink(sourcen.c_str(), targetn.c_str(), error, vid, infoO, infoN,
true);
}
/*----------------------------------------------------------------------------*/
int
XrdMgmOfs::symlink(const char* source_name,
const char* target_name,
XrdOucErrInfo& error,
eos::common::VirtualIdentity& vid,
const char* infoO,
const char* infoN,
bool overwrite)
/*----------------------------------------------------------------------------*/
/*
* @brief symlink a file or directory
*
* @param source_name source name
* @param target_name target name
* @param error error object
* @param vid virtual identity of the client
* @param infoO CGI of the source name
* @param infoN CGI of the target name
* @return SFS_OK on success otherwise SFS_ERROR
*
*/
/*----------------------------------------------------------------------------*/
{
static const char* epname = "symlink";
errno = 0;
eos_info("source=%s target=%s", source_name, target_name);
XrdOucString source, destination;
XrdOucEnv symlinko_Env(infoO);
XrdOucEnv symlinkn_Env(infoN);
XrdOucString sourcen = source_name;
XrdOucString targetn = target_name;
const char* inpath = 0;
const char* ininfo = 0;
{
inpath = source_name;
ininfo = infoO;
NAMESPACEMAP;
BOUNCE_ILLEGAL_NAMES;
sourcen = path;
}
BOUNCE_NOT_ALLOWED;
ACCESSMODE_W;
MAYSTALL;
{
const char* path = inpath;
MAYREDIRECT;
}
// check access permissions on source
if ((_access(sourcen.c_str(), W_OK, error, vid, infoO) != SFS_OK)) {
return SFS_ERROR;
}
return _symlink(sourcen.c_str(), targetn.c_str(), error, vid, infoO, infoN);
}
/*----------------------------------------------------------------------------*/
int
XrdMgmOfs::_symlink(const char* source_name,
const char* target_name,
XrdOucErrInfo& error,
eos::common::VirtualIdentity& vid,
const char* infoO,
const char* infoN
)
/*----------------------------------------------------------------------------*/
/*
* @brief symlink a file or directory
*
* @param source_name source name
* @param target_name target name
* @param error error object
* @param vid virtual identity of the client
* @param infoO CGI of the source name
* @param infoN CGI of the target name
* @return SFS_OK on success otherwise SFS_ERROR
*
*/
/*----------------------------------------------------------------------------*/
{
static const char* epname = "_symlink";
errno = 0;
eos_info("source=%s target=%s", source_name, target_name);
EXEC_TIMING_BEGIN("SymLink");
eos::common::Path oPath(source_name);
std::string oP = oPath.GetParentPath();
if ((!source_name) || (!target_name)) {
errno = EINVAL;
return Emsg(epname, error, EINVAL, "symlink - 0 source or target name");
}
if (!strcmp(source_name, target_name)) {
errno = EINVAL;
return Emsg(epname, error, EINVAL, "symlink - source and target are identical");
}
gOFS->MgmStats.Add("Symlink", vid.uid, vid.gid, 1);
XrdSfsFileExistence file_exists = XrdSfsFileExistNo;
_exists(oP.c_str(), file_exists, error, vid, infoN);
if (file_exists != XrdSfsFileExistIsDirectory) {
errno = ENOENT;
return Emsg(epname, error, ENOENT,
"symlink - parent source dir does not exist");
}
file_exists = XrdSfsFileExistNo;
_exists(source_name, file_exists, error, vid, infoN);
if (file_exists != XrdSfsFileExistNo) {
errno = EEXIST;
return Emsg(epname, error, ENOENT, "symlink - source exists");
}
{
eos::common::RWMutexWriteLock lock(gOFS->eosViewRWMutex);
try {
std::shared_ptr dir = eosView->getContainer(
oPath.GetParentPath());
eosView->createLink(oPath.GetPath(), target_name,
vid.uid, vid.gid);
dir->setMTimeNow();
dir->notifyMTimeChange(gOFS->eosDirectoryService);
eosView->updateContainerStore(dir.get());
eos::ContainerIdentifier dir_id = dir->getIdentifier();
eos::ContainerIdentifier dir_pid = dir->getParentIdentifier();
lock.Release();
gOFS->FuseXCastRefresh(dir_id, dir_pid);
} catch (eos::MDException& e) {
eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n",
e.getErrno(), e.getMessage().str().c_str());
errno = e.getErrno();
return Emsg(epname, error, errno, e.getMessage().str().c_str());
}
}
EXEC_TIMING_END("SymLink");
return SFS_OK;
}
/*----------------------------------------------------------------------------*/
int
XrdMgmOfs::readlink(const char* inpath,
XrdOucErrInfo& error,
XrdOucString& link,
const XrdSecEntity* client,
const char* ininfo)
/*----------------------------------------------------------------------------*/
/*
* @brief read symbolic link target
*
* @param name of the link to read
* @param error error object
* @param client XRootD authentication object
* @param link target string if symbolic link otherwise empty
* @return SFS_OK on success otherwise SFS_ERROR
*
*/
/*----------------------------------------------------------------------------*/
{
static const char* epname = "readlink";
const char* tident = error.getErrUser();
// use a thread private vid
eos::common::VirtualIdentity vid;
EXEC_TIMING_BEGIN("IdMap");
eos::common::Mapping::IdMap(client, ininfo, tident, vid, gOFS->mTokenAuthz,
AOP_Read, inpath);
EXEC_TIMING_END("IdMap");
eos_info("path=%s", inpath);
gOFS->MgmStats.Add("IdMap", vid.uid, vid.gid, 1);
errno = 0;
XrdOucEnv readlink_Env(ininfo);
AUTHORIZE(client, &readlink_Env, AOP_Read, "link", inpath, error);
NAMESPACEMAP;
BOUNCE_ILLEGAL_NAMES;
TOKEN_SCOPE;
BOUNCE_NOT_ALLOWED;
ACCESSMODE_R;
MAYSTALL;
MAYREDIRECT;
return _readlink(path, error, vid, link);
}
// ---------------------------------------------------------------------------
// read symbolic link
// ---------------------------------------------------------------------------
int
XrdMgmOfs::_readlink(const char* name,
XrdOucErrInfo& error,
eos::common::VirtualIdentity& vid,
XrdOucString& link)
{
static const char* epname = "_readlink";
errno = 0;
eos_info("name=%s", name);
std::string linktarget;
std::string sname = name;
gOFS->MgmStats.Add("Symlink", vid.uid, vid.gid, 1);
EXEC_TIMING_BEGIN("ReadLink");
eos::Prefetcher::prefetchFileMDAndWait(gOFS->eosView, sname, false);
eos::common::RWMutexReadLock ns_rd_lock(gOFS->eosViewRWMutex);
try {
std::shared_ptr file = eosView->getFile(name, false);
std::string slink = file->getLink();
link = slink.c_str();
} catch (eos::MDException& e) {
eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n",
e.getErrno(), e.getMessage().str().c_str());
errno = e.getErrno();
return Emsg(epname, error, errno, e.getMessage().str().c_str());
}
EXEC_TIMING_END("ReadLink");
return SFS_OK;
}