/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2018 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 .*
************************************************************************/
//------------------------------------------------------------------------------
//! @author Georgios Bitzes
//! @brief Namespace etag utilities
//------------------------------------------------------------------------------
#include "namespace/utils/Etag.hh"
#include "namespace/interface/IFileMD.hh"
#include "common/FileId.hh"
#include "common/Fmd.hh"
#include "namespace/utils/Checksum.hh"
EOSNSNAMESPACE_BEGIN
//------------------------------------------------------------------------------
// Compatibility hack: Use old inodes below 34B, and new above.
//
// The old inode scheme breaks down once it reaches 34B files.
// Yes this is kinda awful.
//------------------------------------------------------------------------------
static uint64_t findInode(uint64_t fid) {
uint64_t threshold = 34'000'000'000ull;
if(fid < threshold) {
return common::FileId::LegacyFidToInode(fid);
}
else {
return common::FileId::NewFidToInode(fid);
}
}
//------------------------------------------------------------------------------
// Calculate etag - supply flag to indicate whether to use checksum or not.
//------------------------------------------------------------------------------
void calculateEtag(bool useChecksum, const fst::FmdBase &fmdBase, std::string &out) {
if(useChecksum) {
return calculateEtagInodeAndChecksum(fmdBase, out);
}
else {
return calculateEtagInodeAndMtime(fmdBase.fid(), fmdBase.mtime(), out);
}
}
//------------------------------------------------------------------------------
// Calculate etag based on checksum type + fst fmdproto.
// TODO(gbitzes): Maybe checksumType is not needed? Maybe we can derive
// checksum type from layout id of fmdproto?
//------------------------------------------------------------------------------
void calculateEtagInodeAndChecksum(const fst::FmdBase &fmdBase, std::string &out) {
if(eos::common::LayoutId::GetChecksum(fmdBase.lid()) != eos::common::LayoutId::kMD5) {
// use inode + checksum
char setag[256];
snprintf(setag, sizeof(setag) - 1, "\"%llu:%s\"",
(unsigned long long) findInode(fmdBase.fid()),
fmdBase.checksum().c_str());
out = setag;
} else {
// use checksum, S3 wants the pure MD5
char setag[256];
snprintf(setag, sizeof(setag) - 1, "\"%s\"",
fmdBase.checksum().c_str());
out = setag;
}
}
//------------------------------------------------------------------------------
// Calculate etag based on inode + mtime.
//------------------------------------------------------------------------------
void calculateEtagInodeAndMtime(uint64_t fid, uint64_t mtimeSec, std::string &out) {
char setag[256];
snprintf(setag, sizeof(setag) - 1, "\"%llu:%llu\"",
(unsigned long long) findInode(fid),
(unsigned long long) mtimeSec);
out = setag;
}
//------------------------------------------------------------------------------
// Calculate etag for the given FileMD.
//------------------------------------------------------------------------------
void calculateEtag(const eos::ns::FileMdProto &proto, std::string &out) {
//----------------------------------------------------------------------------
// Forced etag?
//----------------------------------------------------------------------------
constexpr char tmpEtag[] = "sys.tmp.etag";
if(proto.xattrs().count(tmpEtag)) {
out = proto.xattrs().at(tmpEtag);
return;
}
//----------------------------------------------------------------------------
// Nope. Is there a checksum?
//----------------------------------------------------------------------------
size_t cxlen = eos::common::LayoutId::GetChecksumLen(proto.layout_id());
if(cxlen > 0) {
//--------------------------------------------------------------------------
// Yes, use inode + checksum for the etag.
// If MD5 checksums are used we omit the inode number, S3 wants that
//--------------------------------------------------------------------------
if (eos::common::LayoutId::GetChecksum(proto.layout_id()) == eos::common::LayoutId::kMD5) {
out = "\"";
eos::appendChecksumOnStringProtobuf(proto, out);
out += "\"";
}
else {
char setag[256];
snprintf(setag, sizeof(setag) - 1, "\"%llu:", (unsigned long long) findInode(proto.id()));
out = setag;
eos::appendChecksumOnStringProtobuf(proto, out);
out += "\"";
}
return;
}
//----------------------------------------------------------------------------
// Nope, fallback to inode + mtime.
//----------------------------------------------------------------------------
eos::IFileMD::ctime_t mtime;
(void) memcpy(&mtime, proto.mtime().data(), sizeof(eos::IFileMD::ctime_t));
calculateEtagInodeAndMtime(proto.id(), mtime.tv_sec, out);
return;
}
//------------------------------------------------------------------------------
// Calculate etag for the given FileMD.
//------------------------------------------------------------------------------
void calculateEtag(const IFileMD *const fmd, std::string &out) {
//----------------------------------------------------------------------------
// Forced etag?
//----------------------------------------------------------------------------
constexpr char tmpEtag[] = "sys.tmp.etag";
if(fmd->hasAttribute(tmpEtag)) {
out = fmd->getAttribute(tmpEtag);
return;
}
//----------------------------------------------------------------------------
// Nope. Is there a checksum?
//----------------------------------------------------------------------------
size_t cxlen = eos::common::LayoutId::GetChecksumLen(fmd->getLayoutId());
if(cxlen > 0) {
//--------------------------------------------------------------------------
// Yes, use inode + checksum for the etag.
// If MD5 checksums are used we omit the inode number, S3 wants that
//--------------------------------------------------------------------------
if (eos::common::LayoutId::GetChecksum(fmd->getLayoutId()) == eos::common::LayoutId::kMD5) {
out = "\"";
eos::appendChecksumOnStringAsHex(fmd, out);
out += "\"";
}
else {
char setag[256];
snprintf(setag, sizeof(setag) - 1, "\"%llu:", (unsigned long long) findInode(fmd->getId()));
out = setag;
eos::appendChecksumOnStringAsHex(fmd, out);
out += "\"";
}
return;
}
//----------------------------------------------------------------------------
// Nope, fallback to inode + mtime.
//----------------------------------------------------------------------------
eos::IFileMD::ctime_t mtime;
fmd->getCTime(mtime);
calculateEtagInodeAndMtime(fmd->getId(), mtime.tv_sec, out);
return;
}
//----------------------------------------------------------------------------
// Calculate etag for the given ContainerMD.
//----------------------------------------------------------------------------
void calculateEtag(IContainerMD *cmd, std::string &out) {
//----------------------------------------------------------------------------
// Forced etag?
//----------------------------------------------------------------------------
constexpr char tmpEtag[] = "sys.tmp.etag";
if(cmd->hasAttribute(tmpEtag)) {
out = cmd->getAttribute(tmpEtag);
return;
}
//----------------------------------------------------------------------------
// Use inode + tmtime
//----------------------------------------------------------------------------
eos::IFileMD::ctime_t mtime;
cmd->getTMTime(mtime);
char setag[256];
snprintf(setag, sizeof(setag) - 1, "%llx:%llu.%03lu",
(unsigned long long) cmd->getId(),
(unsigned long long) mtime.tv_sec,
(unsigned long) mtime.tv_nsec / 1000000);
out = setag;
return;
}
EOSNSNAMESPACE_END