// ----------------------------------------------------------------------
// File: Touch.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.
// -----------------------------------------------------------------------
#include
#include
#include "fst/checksum/ChecksumPlugins.hh"
#include "common/XattrCompat.hh"
/*----------------------------------------------------------------------------*/
int
XrdMgmOfs::_touch(const char* path,
XrdOucErrInfo& error,
eos::common::VirtualIdentity& vid,
const char* ininfo,
bool doLock,
bool useLayout,
bool truncate,
size_t size,
bool absorb,
const char* linkpath,
const char* xs_hex,
std::string* errmsg)
/*----------------------------------------------------------------------------*/
/*
* @brief create(touch) a no-replica file in the namespace
*
* @param path file to touch
* @param error error object
* @param vid virtual identity of the client
* @param ininfo CGI
* @param doLock take the namespace lock
* @param useLayout create a file using the layout an space policies
* @param size to preset on the file
* @param absorb - if true we will try to move the file into the FST tree without hardlinking it
* @param link path - file to hardlink/symlink to the FST store - requires shared filesystem access
* @param xs_hex - checksum value in hex format to register
*
* @return SFS_OK or SFS_ERROR
*/
/*----------------------------------------------------------------------------*/
{
EXEC_TIMING_BEGIN("Touch");
eos_info("path=%s vid.uid=%u vid.gid=%u", path, vid.uid, vid.gid);
gOFS->MgmStats.Add("Touch", vid.uid, vid.gid, 1);
// Perform the actual deletion
errno = 0;
std::shared_ptr fmd;
bool existedAlready = false;
if (_access(path, W_OK, error, vid, ininfo)) {
return SFS_ERROR;
}
eos::Prefetcher::prefetchFileMDAndWait(gOFS->eosView, path);
eos::common::RWMutexWriteLock lock;
std::string fullpath;
bool verify = true;
std::vector locations;
int linking = 0;
bool create_symlink = false;
bool create_hardlink = false;
if (doLock) {
lock.Grab(gOFS->eosViewRWMutex);
}
try {
fmd = gOFS->eosView->getFile(path);
existedAlready = true;
errno = 0;
} catch (eos::MDException& e) {
errno = e.getErrno();
eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n",
e.getErrno(), e.getMessage().str().c_str());
}
if ((absorb && truncate) || (absorb && !useLayout) ||
(linkpath && strlen(linkpath) && !useLayout)) {
error.setErrInfo(EINVAL,
"error: -a can not be combined with -0 and -n - a linkpath can only be combined with -a!\n");
eos_err("-a can not be combined with -0 and -n - a linkpath can only be combined with -a!\n");
if (errmsg) {
*errmsg +=
"error: -a can not be combined with -0 and -n - a linkpath can only be combined with -a!\n";
}
return SFS_ERROR;
}
if ((absorb || (linkpath && strlen(linkpath))) && vid.uid) {
error.setErrInfo(EINVAL,
"error: external files can only be registered by the root user\n");
eos_err("external files can only be registred by the root user\n");
if (errmsg) {
*errmsg += "error: external files can only be registered by the root user\n";
}
return SFS_ERROR;
}
// ----------------------------------------------------------------------------------
// for external filesystem registration:
// - if this is registration of an existing file, check if this was already adopted
// - check if we have write permission to create a hardlink
// - fallback to a symlink if we do cross-device registration
// ----------------------------------------------------------------------------------
if (linkpath && strlen(linkpath)) {
struct stat buf;
if (!::stat(linkpath, &buf)) {
if (!::access(linkpath, W_OK)) {
size = buf.st_size;
char xv[4096];
// check if target has already an EOS lfn
if (lgetxattr(linkpath, "user.eos.lfn", xv, sizeof(xv)) > 0) {
eos_static_err("file had already an EOS lfn path='%s'", linkpath);
error.setErrInfo(EEXIST,
"error: file has already a registered LFN stored on the extended attributes");
if (errmsg) {
*errmsg +=
"error: file has already a registered LFN stored on the extended attributes";
}
return SFS_ERROR;
}
} else {
eos_static_err("is not writable to us path='%s'", linkpath);
error.setErrInfo(EPERM, "error: provided path is not writable for the MGM");
if (errmsg) {
*errmsg += "error: provided path is not writable for the MGM";
}
return SFS_ERROR;
}
} else {
eos_err("link path does not exist path='%s'", linkpath);
error.setErrInfo(ENOENT,
"error: provided path is not accessible on the MGM or does not exist");
if (errmsg) {
*errmsg +=
"error: provided path is not accessible on the MGM or does not exist";
}
return SFS_ERROR;
}
} else {
if (absorb) {
error.setErrInfo(EINVAL,
"error: link path has to be provdied to absorb a file");
eos_err("link path has to be provided to absorb a file");
if (errmsg) {
*errmsg +=
"error: when using -a to absorb a file you have to privde the source path";
}
return SFS_ERROR;
}
}
try {
if (!fmd) {
if (useLayout) {
lock.Release();
XrdMgmOfsFile* file = new XrdMgmOfsFile(const_cast(vid.tident.c_str()));
XrdOucString opaque = ininfo;
if (file) {
int rc = file->open(&vid, path, SFS_O_RDWR | SFS_O_CREAT, 0755, 0,
"eos.bookingsize=0&eos.app=fuse");
error.setErrInfo(strlen(file->error.getErrText()) + 1,
file->error.getErrText());
if (rc != SFS_REDIRECT) {
error.setErrCode(file->error.getErrInfo());
errno = file->error.getErrInfo();
eos_static_info("open failed");
return SFS_ERROR;
}
delete file;
} else {
const char* emsg = "allocate file object";
error.setErrInfo(strlen(emsg) + 1, emsg);
error.setErrCode(ENOMEM);
return SFS_ERROR;
}
lock.Grab(gOFS->eosViewRWMutex);
fmd = gOFS->eosView->getFile(path);
} else {
fmd = gOFS->eosView->createFile(path, vid.uid, vid.gid);
}
// get the file
fmd->setCUid(vid.uid);
fmd->setCGid(vid.gid);
fmd->setCTimeNow();
fmd->setSize(0);
fullpath = gOFS->eosView->getUri(fmd.get());
if (linkpath && strlen(linkpath)) {
for (unsigned int i = 0; i < fmd->getNumLocation(); i++) {
const auto loc = fmd->getLocation(i);
locations.push_back(loc);
if (loc != 0 && loc != eos::common::TAPE_FS_ID) {
eos::common::FileSystem::fs_snapshot_t local_snapshot;
eos::mgm::FileSystem* local_fs = FsView::gFsView.mIdView.lookupByID(loc);
local_fs->SnapShotFileSystem(local_snapshot);
std::string local_path = eos::common::FileId::FidPrefix2FullPath(
eos::common::FileId::Fid2Hex(fmd->getId()).c_str(),
local_snapshot.mPath.c_str());
if (absorb) {
// try renaming
int rc = ::rename(linkpath, local_path.c_str());
fprintf(stderr, "rename gave %d %d\n", rc, errno);
if (rc) {
linking = errno;
if (errmsg) {
*errmsg += "error: failed to rename path='" + std::string(
linkpath) + std::string("'\n");
}
} else {
eos_info("renamed '%s' => '%s'", linkpath, local_path.c_str());
if (errmsg) {
*errmsg += "info: renamed '";
*errmsg += linkpath;
*errmsg += "' => '";
*errmsg += local_path.c_str();
*errmsg += "'\n";
}
linkpath = local_path.c_str();
}
} else {
// try with links
int rc = ::link(linkpath, local_path.c_str());
if (rc && (errno = EXDEV)) {
eos_info("cross-device registration detected - using symlink for path='%s'",
linkpath);
if (::symlink(linkpath, local_path.c_str())) {
linking = errno;
if (errmsg) {
*errmsg += "error: failed to create symlink for path='" + std::string(
linkpath) + std::string("'\n");
}
} else {
create_symlink = true;
eos_info("created symlink '%s' => '%s'", local_path.c_str(), linkpath);
if (errmsg) {
*errmsg += "info: created symlink '";
}
if (errmsg) {
*errmsg += local_path.c_str();
}
if (errmsg) {
*errmsg += "' => '";
}
if (errmsg) {
*errmsg += linkpath;
}
if (errmsg) {
*errmsg += "\n";
}
}
} else {
create_hardlink = true;
eos_info("created hardlink '%s' => '%s'", local_path.c_str(), linkpath);
if (errmsg) {
*errmsg += "info: created hardlink '";
}
if (errmsg) {
*errmsg += local_path.c_str();
}
if (errmsg) {
*errmsg += "' => '";
}
if (errmsg) {
*errmsg += linkpath;
}
if (errmsg) {
*errmsg += "\n";
}
}
if (create_hardlink) {
if (lsetxattr(linkpath, "user.eos.lfn",
fullpath.c_str(), fullpath.length(), 0)) {
eos_err("can not set user.eos.lfn extended attribute on: '%s'", linkpath);
if (errmsg) {
*errmsg += "error: cannot set user.eos.lfn extended attribute on :'";
}
if (errmsg) {
*errmsg += linkpath;
}
if (errmsg) {
*errmsg += "'\n";
}
}
}
}
}
}
}
}
if (!linking && xs_hex) {
size_t out_sz;
unsigned long lid = fmd->getLayoutId();
std::string checksum_name = eos::common::LayoutId::GetChecksumString(lid);
auto xs_binary = eos::common::StringConversion::Hex2BinDataChar
(std::string(xs_hex), out_sz, SHA256_DIGEST_LENGTH);
if (xs_binary == nullptr) {
if (errmsg) {
*errmsg += "error: failed to store checksum extended attributes on '";
*errmsg += linkpath;
*errmsg += "'\n";
}
} else {
if (linkpath) {
if (lsetxattr(linkpath, "user.eos.checksumtype",
checksum_name.c_str(), checksum_name.length(), 0) ||
lsetxattr(linkpath, "user.eos.checksum",
xs_binary.get(), out_sz, 0)) {
if (errmsg) {
*errmsg += "error: failed to store checksum extended attributes on '";
*errmsg += linkpath;
*errmsg += "'\n";
}
} else {
if (errmsg) {
*errmsg += "info: stored checksum '";
*errmsg += checksum_name.c_str();
*errmsg += ":";
*errmsg += xs_hex;
*errmsg += "' for linked path '";
*errmsg += linkpath;
*errmsg += "'\n";
}
}
// Store this checksum
eos::Buffer xs_buff;
xs_buff.putData(xs_binary.get(), SHA256_DIGEST_LENGTH);
fmd->setChecksum(xs_buff);
*errmsg += "info: stored checksum '";
*errmsg += checksum_name.c_str();
*errmsg += ":";
*errmsg += xs_hex;
*errmsg += "'\n";
}
}
}
fmd->setMTimeNow();
eos::IFileMD::ctime_t mtime;
fmd->getMTime(mtime);
fmd->setCTime(mtime);
if (truncate) {
fmd->setSize(0);
} else {
if (size) {
fmd->setSize(size);
}
}
// Store the birth time as an extended attribute if this is a creation
if (!existedAlready) {
char btime[256];
snprintf(btime, sizeof(btime), "%lu.%lu", mtime.tv_sec, mtime.tv_nsec);
fmd->setAttribute("sys.eos.btime", btime);
}
if (create_hardlink) {
fmd->setAttribute("sys.hardlink.path", linkpath);
}
if (create_symlink) {
fmd->setAttribute("sys.symlink.path", linkpath);
}
if (absorb) {
fmd->setAttribute("sys.absorbed.path", linkpath);
}
gOFS->eosView->updateFileStore(fmd.get());
unsigned long long cid = fmd->getContainerId();
std::shared_ptr cmd =
gOFS->eosDirectoryService->getContainerMD(cid);
cmd->setMTime(mtime);
cmd->notifyMTimeChange(gOFS->eosDirectoryService);
// Check if there is any quota node to be updated
if (!existedAlready) {
try {
eos::IQuotaNode* ns_quota = gOFS->eosView->getQuotaNode(cmd.get());
if (ns_quota) {
ns_quota->addFile(fmd.get());
}
} catch (const eos::MDException& eq) {
// no quota node
}
}
gOFS->eosView->updateContainerStore(cmd.get());
const eos::FileIdentifier fid = fmd->getIdentifier();
const eos::ContainerIdentifier did = cmd->getIdentifier();
const eos::ContainerIdentifier pdid = cmd->getParentIdentifier();
if (doLock) {
lock.Release();
}
gOFS->FuseXCastRefresh(fid, did);
gOFS->FuseXCastRefresh(did, pdid);
errno = 0;
} catch (eos::MDException& e) {
errno = e.getErrno();
eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"\n",
e.getErrno(), e.getMessage().str().c_str());
}
if (linking) {
errno = linking;
} else {
if (verify) {
XrdOucString options;
for (auto loc : locations) {
if (gOFS->_verifystripe(fullpath.c_str(), error, vid, loc, options)) {
// failed
}
}
}
}
if (errno) {
return Emsg("utimes", error, errno, "touch", path);
}
EXEC_TIMING_END("Touch");
return SFS_OK;
}