Program Listing for File metadata.cpp

Return to documentation for file (src/common/metadata.cpp)

/*
  Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
  Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany

  This software was partially supported by the
  EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).

  This software was partially supported by the
  ADA-FS project under the SPPEXA project funded by the DFG.

  This file is part of GekkoFS.

  GekkoFS 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.

  GekkoFS 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 GekkoFS.  If not, see <https://www.gnu.org/licenses/>.

  SPDX-License-Identifier: GPL-3.0-or-later
*/

#include <common/metadata.hpp>
#include <config.hpp>

#include <fmt/format.h>

extern "C" {
#include <sys/stat.h>
#include <unistd.h>
}

#include <ctime>
#include <cassert>
#include <random>

namespace gkfs::metadata {

static const char MSP = '|'; // metadata separator

uint16_t
gen_unique_id(const std::string& path) {
    // Generate a random salt value using a pseudo-random number generator
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0,
                                        std::numeric_limits<uint16_t>::max());
    auto salt = static_cast<uint16_t>(dis(gen));

    // Concatenate the identifier and salt values into a single string
    auto input_str = fmt::format("{}{}", path, salt);

    // Use std::hash function to generate a hash value from the input string
    std::hash<std::string> const hasher;
    auto hash_value = hasher(input_str);

    // Use the lower 16 bits of the hash value as the unique ID
    return static_cast<uint16_t>(hash_value & 0xFFFF);
}

inline void
Metadata::init_time() {
    if constexpr(gkfs::config::metadata::use_ctime) {
        ctime_ = std::time(nullptr);
    }
    if constexpr(gkfs::config::metadata::use_mtime) {
        mtime_ = std::time(nullptr);
    }
    if constexpr(gkfs::config::metadata::use_atime) {
        atime_ = std::time(nullptr);
    }
}

Metadata::Metadata(const mode_t mode)
    : mode_(mode), link_count_(0), size_(0), blocks_(0) {
    assert(S_ISDIR(mode_) || S_ISREG(mode_));
    init_time();
}

#ifdef HAS_SYMLINKS

Metadata::Metadata(const mode_t mode, const std::string& target_path)
    : mode_(mode), link_count_(0), size_(0), blocks_(0),
      target_path_(target_path) {
    assert(S_ISLNK(mode_) || S_ISDIR(mode_) || S_ISREG(mode_));
    // target_path should be there only if this is a link
    assert(target_path_.empty() || S_ISLNK(mode_));
    // target_path should be absolute
    assert(target_path_.empty() || target_path_[0] == '/');
    init_time();
}

#endif

Metadata::Metadata(const std::string& binary_str) {
    size_t read = 0;

    auto ptr = binary_str.data();
    mode_ = static_cast<unsigned int>(std::stoul(ptr, &read));
    // we read something
    assert(read > 0);
    ptr += read;

    // last parsed char is the separator char
    assert(*ptr == MSP);
    // yet we have some character to parse

    size_ = std::stol(++ptr, &read);
    assert(read > 0);
    ptr += read;

    // The order is important. don't change.
    if constexpr(gkfs::config::metadata::use_atime) {
        assert(*ptr == MSP);
        atime_ = static_cast<time_t>(std::stol(++ptr, &read));
        assert(read > 0);
        ptr += read;
    }
    if constexpr(gkfs::config::metadata::use_mtime) {
        assert(*ptr == MSP);
        mtime_ = static_cast<time_t>(std::stol(++ptr, &read));
        assert(read > 0);
        ptr += read;
    }
    if constexpr(gkfs::config::metadata::use_ctime) {
        assert(*ptr == MSP);
        ctime_ = static_cast<time_t>(std::stol(++ptr, &read));
        assert(read > 0);
        ptr += read;
    }
    if constexpr(gkfs::config::metadata::use_link_cnt) {
        assert(*ptr == MSP);
        link_count_ = static_cast<nlink_t>(std::stoul(++ptr, &read));
        assert(read > 0);
        ptr += read;
    }
    if constexpr(gkfs::config::metadata::use_blocks) { // last one will not
                                                       // encounter a
                                                       // delimiter anymore
        assert(*ptr == MSP);
        blocks_ = static_cast<blkcnt_t>(std::stol(++ptr, &read));
        assert(read > 0);
        ptr += read;
    }

#ifdef HAS_SYMLINKS
    // Read target_path
    assert(*ptr == MSP);
    target_path_ = ++ptr;
    // target_path should be there only if this is a link
    ptr += target_path_.size();
#ifdef HAS_RENAME
    // Read rename target, we had captured '|' so we need to recover it
    if(!target_path_.empty()) {
        auto index = target_path_.find_last_of(MSP);
        auto size = target_path_.size();
        target_path_ = target_path_.substr(0, index);
        ptr -= (size - index);
    }
    assert(*ptr == MSP);
    rename_path_ = ++ptr;
    ptr += rename_path_.size();
#endif // HAS_RENAME
#endif // HAS_SYMLINKS

    // we consumed all the binary string
    assert(*ptr == '\0');
}

std::string
Metadata::serialize() const {
    std::string s;
    // The order is important. don't change.
    s += fmt::format_int(mode_).c_str(); // add mandatory mode
    s += MSP;
    s += fmt::format_int(size_).c_str(); // add mandatory size
    if constexpr(gkfs::config::metadata::use_atime) {
        s += MSP;
        s += fmt::format_int(atime_).c_str();
    }
    if constexpr(gkfs::config::metadata::use_mtime) {
        s += MSP;
        s += fmt::format_int(mtime_).c_str();
    }
    if constexpr(gkfs::config::metadata::use_ctime) {
        s += MSP;
        s += fmt::format_int(ctime_).c_str();
    }
    if constexpr(gkfs::config::metadata::use_link_cnt) {
        s += MSP;
        s += fmt::format_int(link_count_).c_str();
    }
    if constexpr(gkfs::config::metadata::use_blocks) {
        s += MSP;
        s += fmt::format_int(blocks_).c_str();
    }

#ifdef HAS_SYMLINKS
    s += MSP;
    s += target_path_;
#ifdef HAS_RENAME
    s += MSP;
    s += rename_path_;
#endif // HAS_RENAME
#endif // HAS_SYMLINKS

    return s;
}

void
Metadata::update_atime_now() {
    auto time = std::time(nullptr);
    atime_ = time;
}

void
Metadata::update_mtime_now() {
    auto time = std::time(nullptr);
    mtime_ = time;
}

//-------------------------------------------- GETTER/SETTER

time_t
Metadata::atime() const {
    return atime_;
}

void
Metadata::atime(time_t atime) {
    Metadata::atime_ = atime;
}

time_t
Metadata::mtime() const {
    return mtime_;
}

void
Metadata::mtime(time_t mtime) {
    Metadata::mtime_ = mtime;
}

time_t
Metadata::ctime() const {
    return ctime_;
}

void
Metadata::ctime(time_t ctime) {
    Metadata::ctime_ = ctime;
}

mode_t
Metadata::mode() const {
    return mode_;
}

void
Metadata::mode(mode_t mode) {
    Metadata::mode_ = mode;
}

nlink_t
Metadata::link_count() const {
    return link_count_;
}

void
Metadata::link_count(nlink_t link_count) {
    Metadata::link_count_ = link_count;
}

size_t
Metadata::size() const {
    return size_;
}

void
Metadata::size(size_t size) {
    Metadata::size_ = size;
}

blkcnt_t
Metadata::blocks() const {
    return blocks_;
}

void
Metadata::blocks(blkcnt_t blocks) {
    Metadata::blocks_ = blocks;
}

#ifdef HAS_SYMLINKS

std::string
Metadata::target_path() const {
    return target_path_;
}

void
Metadata::target_path(const std::string& target_path) {
    target_path_ = target_path;
}

bool
Metadata::is_link() const {
    return S_ISLNK(mode_);
}

#ifdef HAS_RENAME
std::string
Metadata::rename_path() const {
    return rename_path_;
}

void
Metadata::rename_path(const std::string& rename_path) {
    rename_path_ = rename_path;
}

#endif // HAS_RENAME
#endif // HAS_SYMLINKS

} // namespace gkfs::metadata