LCOV - code coverage report
Current view: top level - src/client - preload_context.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 173 197 87.8 %
Date: 2024-04-30 13:21:35 Functions: 33 36 91.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
       3             :   Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany
       4             : 
       5             :   This software was partially supported by the
       6             :   EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
       7             : 
       8             :   This software was partially supported by the
       9             :   ADA-FS project under the SPPEXA project funded by the DFG.
      10             : 
      11             :   This file is part of GekkoFS' POSIX interface.
      12             : 
      13             :   GekkoFS' POSIX interface is free software: you can redistribute it and/or
      14             :   modify it under the terms of the GNU Lesser General Public License as
      15             :   published by the Free Software Foundation, either version 3 of the License,
      16             :   or (at your option) any later version.
      17             : 
      18             :   GekkoFS' POSIX interface is distributed in the hope that it will be useful,
      19             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      20             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21             :   GNU Lesser General Public License for more details.
      22             : 
      23             :   You should have received a copy of the GNU Lesser General Public License
      24             :   along with GekkoFS' POSIX interface.  If not, see
      25             :   <https://www.gnu.org/licenses/>.
      26             : 
      27             :   SPDX-License-Identifier: LGPL-3.0-or-later
      28             : */
      29             : 
      30             : #include <client/preload_context.hpp>
      31             : #include <client/env.hpp>
      32             : #include <client/logging.hpp>
      33             : #include <client/open_file_map.hpp>
      34             : #include <client/open_dir.hpp>
      35             : #include <client/path.hpp>
      36             : 
      37             : #include <common/env_util.hpp>
      38             : #include <common/path_util.hpp>
      39             : #include <config.hpp>
      40             : 
      41             : #include <hermes.hpp>
      42             : 
      43             : #include <cassert>
      44             : 
      45             : extern "C" {
      46             : #include <libsyscall_intercept_hook_point.h>
      47             : #include <syscall.h>
      48             : }
      49             : 
      50             : namespace gkfs {
      51             : 
      52             : namespace preload {
      53             : 
      54             : decltype(PreloadContext::MIN_INTERNAL_FD) constexpr PreloadContext::
      55             :         MIN_INTERNAL_FD;
      56             : decltype(PreloadContext::MAX_USER_FDS) constexpr PreloadContext::MAX_USER_FDS;
      57             : 
      58         248 : PreloadContext::PreloadContext()
      59             :     : ofm_(std::make_shared<gkfs::filemap::OpenFileMap>()),
      60         744 :       fs_conf_(std::make_shared<FsConfig>()) {
      61             : 
      62         248 :     internal_fds_.set();
      63         248 :     internal_fds_must_relocate_ = true;
      64             : 
      65         248 :     char host[255];
      66         248 :     gethostname(host, 255);
      67         248 :     hostname = host;
      68         496 :     PreloadContext::set_replicas(
      69         496 :             std::stoi(gkfs::env::get_var(gkfs::env::NUM_REPL, "0")));
      70         248 : }
      71             : 
      72             : void
      73         248 : PreloadContext::init_logging() {
      74             : 
      75         248 :     const std::string log_opts = gkfs::env::get_var(
      76         496 :             gkfs::env::LOG, gkfs::config::log::client_log_level);
      77             : 
      78         248 :     const std::string log_output = gkfs::env::get_var(
      79         744 :             gkfs::env::LOG_OUTPUT, gkfs::config::log::client_log_path);
      80             : 
      81         248 :     const bool log_per_process =
      82         496 :             gkfs::env::get_var(gkfs::env::LOG_PER_PROCESS).empty() ? false
      83         248 :                                                                    : true;
      84             : 
      85             : #ifdef GKFS_DEBUG_BUILD
      86             :     // atoi returns 0 if no int conversion can be performed, which works
      87             :     // for us since if the user provides a non-numeric value we can just treat
      88             :     // it as zero
      89         496 :     const int log_verbosity = std::atoi(
      90         496 :             gkfs::env::get_var(gkfs::env::LOG_DEBUG_VERBOSITY, "0").c_str());
      91             : 
      92         248 :     const std::string log_filter =
      93         744 :             gkfs::env::get_var(gkfs::env::LOG_SYSCALL_FILTER, "");
      94             : #endif
      95             : 
      96         248 :     const std::string trunc_val =
      97         744 :             gkfs::env::get_var(gkfs::env::LOG_OUTPUT_TRUNC);
      98             : 
      99         248 :     const bool log_trunc = (!trunc_val.empty() && trunc_val[0] != '0');
     100             : 
     101         248 :     gkfs::log::create_global_logger(log_opts, log_output, log_per_process,
     102             :                                     log_trunc
     103             : #ifdef GKFS_DEBUG_BUILD
     104             :                                     ,
     105             :                                     log_filter, log_verbosity
     106             : #endif
     107             :     );
     108         248 : }
     109             : 
     110             : void
     111         248 : PreloadContext::mountdir(const std::string& path) {
     112         248 :     assert(gkfs::path::is_absolute(path));
     113         248 :     assert(!gkfs::path::has_trailing_slash(path));
     114         248 :     mountdir_components_ = gkfs::path::split_path(path);
     115         248 :     mountdir_ = path;
     116         248 : }
     117             : 
     118             : const std::string&
     119        1527 : PreloadContext::mountdir() const {
     120        1527 :     return mountdir_;
     121             : }
     122             : 
     123             : const std::vector<std::string>&
     124        9201 : PreloadContext::mountdir_components() const {
     125        9201 :     return mountdir_components_;
     126             : }
     127             : 
     128             : void
     129         256 : PreloadContext::cwd(const std::string& path) {
     130         256 :     cwd_ = path;
     131         256 : }
     132             : 
     133             : const std::string&
     134         263 : PreloadContext::cwd() const {
     135         263 :     return cwd_;
     136             : }
     137             : 
     138             : const std::vector<hermes::endpoint>&
     139        3415 : PreloadContext::hosts() const {
     140        3415 :     return hosts_;
     141             : }
     142             : 
     143             : void
     144         248 : PreloadContext::hosts(const std::vector<hermes::endpoint>& endpoints) {
     145         248 :     hosts_ = endpoints;
     146         248 : }
     147             : 
     148             : void
     149         248 : PreloadContext::clear_hosts() {
     150         248 :     hosts_.clear();
     151         248 : }
     152             : 
     153             : uint64_t
     154         496 : PreloadContext::local_host_id() const {
     155         496 :     return local_host_id_;
     156             : }
     157             : 
     158             : void
     159         248 : PreloadContext::local_host_id(uint64_t id) {
     160         248 :     local_host_id_ = id;
     161         248 : }
     162             : 
     163             : uint64_t
     164           0 : PreloadContext::fwd_host_id() const {
     165           0 :     return fwd_host_id_;
     166             : }
     167             : 
     168             : void
     169           0 : PreloadContext::fwd_host_id(uint64_t id) {
     170           0 :     fwd_host_id_ = id;
     171           0 : }
     172             : 
     173             : const std::string&
     174         496 : PreloadContext::rpc_protocol() const {
     175         496 :     return rpc_protocol_;
     176             : }
     177             : 
     178             : void
     179         248 : PreloadContext::rpc_protocol(const std::string& rpc_protocol) {
     180         248 :     rpc_protocol_ = rpc_protocol;
     181         248 : }
     182             : 
     183             : bool
     184         496 : PreloadContext::auto_sm() const {
     185         496 :     return auto_sm_;
     186             : }
     187             : 
     188             : void
     189           0 : PreloadContext::auto_sm(bool auto_sm) {
     190           0 :     PreloadContext::auto_sm_ = auto_sm;
     191           0 : }
     192             : 
     193             : RelativizeStatus
     194        9129 : PreloadContext::relativize_fd_path(int dirfd, const char* raw_path,
     195             :                                    std::string& relative_path, int flags,
     196             :                                    bool resolve_last_link) const {
     197             : 
     198             :     // Relativize path should be called only after the library constructor has
     199             :     // been executed
     200        9129 :     assert(interception_enabled_);
     201             :     // If we run the constructor we also already setup the mountdir
     202        9129 :     assert(!mountdir_.empty());
     203             : 
     204             :     // We assume raw path is valid
     205        9129 :     assert(raw_path != nullptr);
     206             : 
     207       18258 :     std::string path;
     208             : 
     209        9129 :     if(raw_path != nullptr && raw_path[0] != gkfs::path::separator) {
     210             :         // path is relative
     211           3 :         if(dirfd == AT_FDCWD) {
     212             :             // path is relative to cwd
     213           3 :             path = gkfs::path::prepend_path(cwd_, raw_path);
     214             :         } else {
     215           0 :             if(!ofm_->exist(dirfd)) {
     216           0 :                 return RelativizeStatus::fd_unknown;
     217             :             } else {
     218             :                 // check if we have the AT_EMPTY_PATH flag
     219             :                 // for fstatat.
     220           0 :                 if(flags & AT_EMPTY_PATH) {
     221           0 :                     relative_path = ofm_->get(dirfd)->path();
     222           0 :                     return RelativizeStatus::internal;
     223             :                 }
     224             :             }
     225             :             // path is relative to fd
     226           0 :             auto dir = ofm_->get_dir(dirfd);
     227           0 :             if(dir == nullptr) {
     228           0 :                 return RelativizeStatus::fd_not_a_dir;
     229             :             }
     230           0 :             path = mountdir_;
     231           0 :             path.append(dir->path());
     232           0 :             path.push_back(gkfs::path::separator);
     233           0 :             path.append(raw_path);
     234             :         }
     235             :     } else {
     236        9126 :         path = raw_path;
     237             :     }
     238             : 
     239        9129 :     if(gkfs::path::resolve(path, relative_path, resolve_last_link)) {
     240        1207 :         return RelativizeStatus::internal;
     241             :     }
     242             :     return RelativizeStatus::external;
     243             : }
     244             : 
     245             : bool
     246          72 : PreloadContext::relativize_path(const char* raw_path,
     247             :                                 std::string& relative_path,
     248             :                                 bool resolve_last_link) const {
     249             :     // Relativize path should be called only after the library constructor has
     250             :     // been executed
     251          72 :     assert(interception_enabled_);
     252             :     // If we run the constructor we also already setup the mountdir
     253          72 :     assert(!mountdir_.empty());
     254             : 
     255             :     // We assume raw path is valid
     256          72 :     assert(raw_path != nullptr);
     257             : 
     258          72 :     std::string path;
     259             : 
     260          72 :     if(raw_path != nullptr && raw_path[0] != gkfs::path::separator) {
     261             :         /* Path is not absolute, we need to prepend CWD;
     262             :          * First reserve enough space to minimize memory copy
     263             :          */
     264           4 :         path = gkfs::path::prepend_path(cwd_, raw_path);
     265             :     } else {
     266          68 :         path = raw_path;
     267             :     }
     268             : 
     269          72 :     return gkfs::path::resolve(path, relative_path, resolve_last_link);
     270             : }
     271             : 
     272             : const std::shared_ptr<gkfs::filemap::OpenFileMap>&
     273      373254 : PreloadContext::file_map() const {
     274      373254 :     return ofm_;
     275             : }
     276             : 
     277             : void
     278         248 : PreloadContext::distributor(std::shared_ptr<gkfs::rpc::Distributor> d) {
     279         248 :     distributor_ = d;
     280         248 : }
     281             : 
     282             : std::shared_ptr<gkfs::rpc::Distributor>
     283        2716 : PreloadContext::distributor() const {
     284        2716 :     return distributor_;
     285             : }
     286             : 
     287             : const std::shared_ptr<FsConfig>&
     288        2355 : PreloadContext::fs_conf() const {
     289        2355 :     return fs_conf_;
     290             : }
     291             : 
     292             : void
     293         496 : PreloadContext::enable_interception() {
     294         496 :     interception_enabled_ = true;
     295         496 : }
     296             : 
     297             : void
     298         248 : PreloadContext::disable_interception() {
     299         248 :     interception_enabled_ = false;
     300         248 : }
     301             : 
     302             : bool
     303    11351081 : PreloadContext::interception_enabled() const {
     304    11351081 :     return interception_enabled_;
     305             : }
     306             : 
     307             : int
     308       13640 : PreloadContext::register_internal_fd(int fd) {
     309             : 
     310       13640 :     assert(fd >= 0);
     311             : 
     312       13640 :     if(!internal_fds_must_relocate_) {
     313        7688 :         LOG(DEBUG, "registering fd {} as internal (no relocation needed)", fd);
     314        7688 :         assert(fd >= MIN_INTERNAL_FD);
     315        7688 :         internal_fds_.reset(fd - MIN_INTERNAL_FD);
     316        7688 :         return fd;
     317             :     }
     318             : 
     319        5952 :     LOG(DEBUG, "registering fd {} as internal (needs relocation)", fd);
     320             : 
     321       19592 :     std::lock_guard<std::mutex> lock(internal_fds_mutex_);
     322        5952 :     const int pos = internal_fds_._Find_first();
     323             : 
     324        5952 :     if(static_cast<std::size_t>(pos) == internal_fds_.size()) {
     325           0 :         throw std::runtime_error(
     326             :                 "Internal GekkoFS file descriptors exhausted, increase GKFS_MAX_INTERNAL_FDS in "
     327           0 :                 "CMake, rebuild GekkoFS and try again.");
     328             :     }
     329        5952 :     internal_fds_.reset(pos);
     330             : 
     331             : 
     332             : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
     333        5952 :     long args[gkfs::syscall::MAX_ARGS]{fd, pos + MIN_INTERNAL_FD, O_CLOEXEC};
     334             : #endif
     335             : 
     336        5952 :     LOG(SYSCALL,
     337             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     338             :                 gkfs::syscall::not_executed,
     339        5952 :         SYS_dup3, args);
     340             : 
     341        5952 :     const int ifd = ::syscall_no_intercept(SYS_dup3, fd, pos + MIN_INTERNAL_FD,
     342        5952 :                                            O_CLOEXEC);
     343             : 
     344        5952 :     LOG(SYSCALL,
     345             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     346             :                 gkfs::syscall::executed,
     347        5952 :         SYS_dup3, args, ifd);
     348             : 
     349        5952 :     assert(::syscall_error_code(ifd) == 0);
     350             : 
     351             : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
     352        5952 :     long args2[gkfs::syscall::MAX_ARGS]{fd};
     353             : #endif
     354             : 
     355        5952 :     LOG(SYSCALL,
     356             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     357             :                 gkfs::syscall::not_executed,
     358        5952 :         SYS_close, args2);
     359             : 
     360             : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
     361        5952 :     int rv = ::syscall_no_intercept(SYS_close, fd);
     362             : #else
     363             :     ::syscall_no_intercept(SYS_close, fd);
     364             : #endif
     365             : 
     366        5952 :     LOG(SYSCALL,
     367             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     368             :                 gkfs::syscall::executed,
     369        5952 :         SYS_close, args2, rv);
     370             : 
     371        5952 :     LOG(DEBUG, "    (fd {} relocated to ifd {})", fd, ifd);
     372             : 
     373        5952 :     return ifd;
     374             : }
     375             : 
     376             : void
     377        9424 : PreloadContext::unregister_internal_fd(int fd) {
     378             : 
     379        9424 :     LOG(DEBUG, "unregistering internal fd {}", fd);
     380             : 
     381        9424 :     assert(fd >= MIN_INTERNAL_FD);
     382             : 
     383        9424 :     const auto pos = fd - MIN_INTERNAL_FD;
     384             : 
     385        9424 :     std::lock_guard<std::mutex> lock(internal_fds_mutex_);
     386        9424 :     internal_fds_.set(pos);
     387        9424 : }
     388             : 
     389             : bool
     390       10853 : PreloadContext::is_internal_fd(int fd) const {
     391             : 
     392       10853 :     if(fd < MIN_INTERNAL_FD) {
     393             :         return false;
     394             :     }
     395             : 
     396        2946 :     const auto pos = fd - MIN_INTERNAL_FD;
     397             : 
     398       13799 :     std::lock_guard<std::mutex> lock(internal_fds_mutex_);
     399        2946 :     return !internal_fds_.test(pos);
     400             : }
     401             : 
     402             : void
     403         248 : PreloadContext::protect_user_fds() {
     404             : 
     405         248 :     LOG(DEBUG, "Protecting application fds [{}, {}]", 0, MAX_USER_FDS - 1);
     406             : 
     407         248 :     const int nullfd =
     408         248 :             ::syscall_no_intercept(SYS_openat, 0, "/dev/null", O_RDONLY);
     409         248 :     assert(::syscall_error_code(nullfd) == 0);
     410         248 :     protected_fds_.set(nullfd);
     411             : 
     412     2226296 :     const auto fd_is_open = [](int fd) -> bool {
     413     2226048 :         const int ret = ::syscall_no_intercept(SYS_fcntl, fd, F_GETFD);
     414     2226048 :         const int error = ::syscall_error_code(ret);
     415     2226048 :         return error == 0 || error != EBADF;
     416             :     };
     417             : 
     418     2226296 :     for(int fd = 0; fd < MAX_USER_FDS; ++fd) {
     419     2226048 :         if(fd_is_open(fd)) {
     420         992 :             LOG(DEBUG, "  fd {} was already in use, skipping", fd);
     421         992 :             continue;
     422             :         }
     423             : 
     424     2225056 :         const int ret = ::syscall_no_intercept(SYS_dup3, nullfd, fd, O_CLOEXEC);
     425     2225056 :         assert(::syscall_error_code(ret) == 0);
     426     2225056 :         protected_fds_.set(fd);
     427             :     }
     428             : 
     429         248 :     internal_fds_must_relocate_ = false;
     430         248 : }
     431             : 
     432             : void
     433         248 : PreloadContext::unprotect_user_fds() {
     434             : 
     435     2226296 :     for(std::size_t fd = 0; fd < protected_fds_.size(); ++fd) {
     436     2226048 :         if(!protected_fds_[fd]) {
     437         744 :             continue;
     438             :         }
     439             : 
     440     2225304 :         const int ret =
     441     2226048 :                 ::syscall_error_code(::syscall_no_intercept(SYS_close, fd));
     442             : 
     443           0 :         if(ret != 0) {
     444           0 :             LOG(ERROR, "Failed to unprotect fd")
     445             :         }
     446             :     }
     447             : 
     448         248 :     internal_fds_must_relocate_ = true;
     449         248 : }
     450             : 
     451             : 
     452             : std::string
     453          28 : PreloadContext::get_hostname() {
     454          28 :     return hostname;
     455             : }
     456             : 
     457             : void
     458         248 : PreloadContext::set_replicas(const int repl) {
     459         248 :     replicas_ = repl;
     460         248 : }
     461             : 
     462             : int
     463        2720 : PreloadContext::get_replicas() {
     464        2720 :     return replicas_;
     465             : }
     466             : 
     467             : } // namespace preload
     468             : } // namespace gkfs

Generated by: LCOV version 1.16