LCOV - code coverage report
Current view: top level - src/client - preload_context.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 182 203 89.7 %
Date: 2024-04-23 00:09:24 Functions: 34 36 94.4 %
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             : #include <hermes.hpp>
      41             : 
      42             : #include <cassert>
      43             : #include <utility>
      44             : 
      45             : #ifndef BYPASS_SYSCALL
      46             : #include <libsyscall_intercept_hook_point.h>
      47             : #else
      48             : #include <client/void_syscall_intercept.hpp>
      49             : #endif
      50             : 
      51             : extern "C" {
      52             : #include <syscall.h>
      53             : }
      54             : 
      55             : namespace gkfs {
      56             : 
      57             : namespace preload {
      58             : 
      59             : decltype(PreloadContext::MIN_INTERNAL_FD) constexpr PreloadContext::
      60             :         MIN_INTERNAL_FD;
      61             : decltype(PreloadContext::MAX_USER_FDS) constexpr PreloadContext::MAX_USER_FDS;
      62             : 
      63         272 : PreloadContext::PreloadContext()
      64             :     : ofm_(std::make_shared<gkfs::filemap::OpenFileMap>()),
      65         816 :       fs_conf_(std::make_shared<FsConfig>()) {
      66             : 
      67         272 :     internal_fds_.set();
      68         272 :     internal_fds_must_relocate_ = true;
      69             : 
      70         272 :     char host[255];
      71         272 :     gethostname(host, 255);
      72         272 :     hostname = host;
      73         544 :     PreloadContext::set_replicas(
      74         544 :             std::stoi(gkfs::env::get_var(gkfs::env::NUM_REPL, "0")));
      75         272 : }
      76             : 
      77             : void
      78         269 : PreloadContext::init_logging() {
      79             : 
      80         269 :     const std::string log_opts = gkfs::env::get_var(
      81         538 :             gkfs::env::LOG, gkfs::config::log::client_log_level);
      82             : 
      83         269 :     const std::string log_output = gkfs::env::get_var(
      84         807 :             gkfs::env::LOG_OUTPUT, gkfs::config::log::client_log_path);
      85             : 
      86         269 :     const bool log_per_process =
      87         538 :             gkfs::env::get_var(gkfs::env::LOG_PER_PROCESS).empty() ? false
      88         269 :                                                                    : true;
      89             : 
      90             : #ifdef GKFS_DEBUG_BUILD
      91             :     // atoi returns 0 if no int conversion can be performed, which works
      92             :     // for us since if the user provides a non-numeric value we can just treat
      93             :     // it as zero
      94         538 :     const int log_verbosity = std::atoi(
      95         538 :             gkfs::env::get_var(gkfs::env::LOG_DEBUG_VERBOSITY, "0").c_str());
      96             : 
      97         269 :     const std::string log_filter =
      98         807 :             gkfs::env::get_var(gkfs::env::LOG_SYSCALL_FILTER, "");
      99             : #endif
     100             : 
     101         269 :     const std::string trunc_val =
     102         807 :             gkfs::env::get_var(gkfs::env::LOG_OUTPUT_TRUNC);
     103             : 
     104         269 :     const bool log_trunc = (!trunc_val.empty() && trunc_val[0] != '0');
     105             : 
     106         269 :     gkfs::log::create_global_logger(log_opts, log_output, log_per_process,
     107             :                                     log_trunc
     108             : #ifdef GKFS_DEBUG_BUILD
     109             :                                     ,
     110             :                                     log_filter, log_verbosity
     111             : #endif
     112             :     );
     113         269 : }
     114             : 
     115             : void
     116         274 : PreloadContext::mountdir(const std::string& path) {
     117         274 :     assert(gkfs::path::is_absolute(path));
     118         274 :     assert(!gkfs::path::has_trailing_slash(path));
     119         274 :     mountdir_components_ = gkfs::path::split_path(path);
     120         274 :     mountdir_ = path;
     121         274 : }
     122             : 
     123             : const std::string&
     124       31517 : PreloadContext::mountdir() const {
     125       31517 :     return mountdir_;
     126             : }
     127             : 
     128             : const std::vector<std::string>&
     129           0 : PreloadContext::mountdir_components() const {
     130           0 :     return mountdir_components_;
     131             : }
     132             : 
     133             : void
     134         282 : PreloadContext::cwd(const std::string& path) {
     135         282 :     cwd_ = path;
     136         282 : }
     137             : 
     138             : const std::string&
     139         300 : PreloadContext::cwd() const {
     140         300 :     return cwd_;
     141             : }
     142             : 
     143             : const std::vector<hermes::endpoint>&
     144        3542 : PreloadContext::hosts() const {
     145        3542 :     return hosts_;
     146             : }
     147             : 
     148             : void
     149         269 : PreloadContext::hosts(const std::vector<hermes::endpoint>& endpoints) {
     150         269 :     hosts_ = endpoints;
     151         269 : }
     152             : 
     153             : void
     154         269 : PreloadContext::clear_hosts() {
     155         269 :     hosts_.clear();
     156         269 : }
     157             : 
     158             : uint64_t
     159         517 : PreloadContext::local_host_id() const {
     160         517 :     return local_host_id_;
     161             : }
     162             : 
     163             : void
     164         269 : PreloadContext::local_host_id(uint64_t id) {
     165         269 :     local_host_id_ = id;
     166         269 : }
     167             : 
     168             : uint64_t
     169         105 : PreloadContext::fwd_host_id() const {
     170         105 :     return fwd_host_id_;
     171             : }
     172             : 
     173             : void
     174          42 : PreloadContext::fwd_host_id(uint64_t id) {
     175          42 :     fwd_host_id_ = id;
     176          42 : }
     177             : 
     178             : const std::string&
     179         538 : PreloadContext::rpc_protocol() const {
     180         538 :     return rpc_protocol_;
     181             : }
     182             : 
     183             : void
     184         269 : PreloadContext::rpc_protocol(const std::string& rpc_protocol) {
     185         269 :     rpc_protocol_ = rpc_protocol;
     186         269 : }
     187             : 
     188             : bool
     189         538 : PreloadContext::auto_sm() const {
     190         538 :     return auto_sm_;
     191             : }
     192             : 
     193             : void
     194           0 : PreloadContext::auto_sm(bool auto_sm) {
     195           0 :     PreloadContext::auto_sm_ = auto_sm;
     196           0 : }
     197             : 
     198             : RelativizeStatus
     199        9843 : PreloadContext::relativize_fd_path(int dirfd, const char* raw_path,
     200             :                                    std::string& relative_path, int flags,
     201             :                                    bool resolve_last_link) const {
     202             : 
     203             :     // Relativize path should be called only after the library constructor has
     204             :     // been executed
     205        9843 :     assert(interception_enabled_);
     206             :     // If we run the constructor we also already setup the mountdir
     207        9843 :     assert(!mountdir_.empty());
     208             : 
     209             :     // We assume raw path is valid
     210        9843 :     assert(raw_path != nullptr);
     211             : 
     212       19686 :     std::string path;
     213             : 
     214        9843 :     if(raw_path != nullptr && raw_path[0] != gkfs::path::separator) {
     215             :         // path is relative
     216           3 :         if(dirfd == AT_FDCWD) {
     217             :             // path is relative to cwd
     218           3 :             path = gkfs::path::prepend_path(cwd_, raw_path);
     219             :         } else {
     220           0 :             if(!ofm_->exist(dirfd)) {
     221           0 :                 return RelativizeStatus::fd_unknown;
     222             :             } else {
     223             :                 // check if we have the AT_EMPTY_PATH flag
     224             :                 // for fstatat.
     225           0 :                 if(flags & AT_EMPTY_PATH) {
     226           0 :                     relative_path = ofm_->get(dirfd)->path();
     227           0 :                     return RelativizeStatus::internal;
     228             :                 }
     229             :             }
     230             :             // path is relative to fd
     231           0 :             auto dir = ofm_->get_dir(dirfd);
     232           0 :             if(dir == nullptr) {
     233           0 :                 return RelativizeStatus::fd_not_a_dir;
     234             :             }
     235           0 :             path = mountdir_;
     236           0 :             path.append(dir->path());
     237           0 :             path.push_back(gkfs::path::separator);
     238           0 :             path.append(raw_path);
     239             :         }
     240             :     } else {
     241        9840 :         path = raw_path;
     242             :     }
     243             : 
     244        9843 :     auto [is_in_path, resolved_path] =
     245       19686 :             gkfs::path::resolve(path, resolve_last_link);
     246        9843 :     relative_path = resolved_path;
     247        9843 :     if(is_in_path) {
     248        1228 :         return RelativizeStatus::internal;
     249             :     }
     250             :     return RelativizeStatus::external;
     251             : }
     252             : 
     253             : bool
     254          72 : PreloadContext::relativize_path(const char* raw_path,
     255             :                                 std::string& relative_path,
     256             :                                 bool resolve_last_link) const {
     257             :     // Relativize path should be called only after the library constructor has
     258             :     // been executed
     259          72 :     assert(interception_enabled_);
     260             :     // If we run the constructor we also already setup the mountdir
     261          72 :     assert(!mountdir_.empty());
     262             : 
     263             :     // We assume raw path is valid
     264          72 :     assert(raw_path != nullptr);
     265             : 
     266          72 :     std::string path;
     267             : 
     268          72 :     if(raw_path != nullptr && raw_path[0] != gkfs::path::separator) {
     269             :         /* Path is not absolute, we need to prepend CWD;
     270             :          * First reserve enough space to minimize memory copy
     271             :          */
     272           4 :         path = gkfs::path::prepend_path(cwd_, raw_path);
     273             :     } else {
     274          68 :         path = raw_path;
     275             :     }
     276             : 
     277          72 :     auto [is_in_path, resolved_path] =
     278         144 :             gkfs::path::resolve(path, resolve_last_link);
     279          72 :     relative_path = resolved_path;
     280         144 :     return is_in_path;
     281             : }
     282             : 
     283             : const std::shared_ptr<gkfs::filemap::OpenFileMap>&
     284      403908 : PreloadContext::file_map() const {
     285      403908 :     return ofm_;
     286             : }
     287             : 
     288             : void
     289         269 : PreloadContext::distributor(std::shared_ptr<gkfs::rpc::Distributor> d) {
     290         269 :     distributor_ = d;
     291         269 : }
     292             : 
     293             : std::shared_ptr<gkfs::rpc::Distributor>
     294        2768 : PreloadContext::distributor() const {
     295        2768 :     return distributor_;
     296             : }
     297             : 
     298             : const std::shared_ptr<FsConfig>&
     299        2544 : PreloadContext::fs_conf() const {
     300        2544 :     return fs_conf_;
     301             : }
     302             : 
     303             : void
     304         538 : PreloadContext::enable_interception() {
     305         538 :     interception_enabled_ = true;
     306         538 : }
     307             : 
     308             : void
     309         269 : PreloadContext::disable_interception() {
     310         269 :     interception_enabled_ = false;
     311         269 : }
     312             : 
     313             : bool
     314    14537177 : PreloadContext::interception_enabled() const {
     315    14537177 :     return interception_enabled_;
     316             : }
     317             : 
     318             : int
     319        8665 : PreloadContext::register_internal_fd(int fd) {
     320             : 
     321        8665 :     assert(fd >= 0);
     322             : 
     323        8665 :     if(!internal_fds_must_relocate_) {
     324        8381 :         LOG(DEBUG, "registering fd {} as internal (no relocation needed)", fd);
     325        8381 :         assert(fd >= MIN_INTERNAL_FD);
     326        8381 :         internal_fds_.reset(fd - MIN_INTERNAL_FD);
     327        8381 :         return fd;
     328             :     }
     329             : 
     330         284 :     LOG(DEBUG, "registering fd {} as internal (needs relocation)", fd);
     331             : 
     332        8949 :     std::lock_guard<std::mutex> lock(internal_fds_mutex_);
     333         284 :     const int pos = internal_fds_._Find_first();
     334             : 
     335         284 :     if(static_cast<std::size_t>(pos) == internal_fds_.size()) {
     336           0 :         throw std::runtime_error(
     337             :                 "Internal GekkoFS file descriptors exhausted, increase GKFS_MAX_INTERNAL_FDS in "
     338           0 :                 "CMake, rebuild GekkoFS and try again.");
     339             :     }
     340         284 :     internal_fds_.reset(pos);
     341             : 
     342             : 
     343             : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
     344         284 :     long args[gkfs::syscall::MAX_ARGS]{fd, pos + MIN_INTERNAL_FD, O_CLOEXEC};
     345             : #endif
     346             : 
     347         284 :     LOG(SYSCALL,
     348             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     349             :                 gkfs::syscall::not_executed,
     350         284 :         SYS_dup3, args);
     351             : 
     352         284 :     const int ifd = ::syscall_no_intercept(SYS_dup3, fd, pos + MIN_INTERNAL_FD,
     353         284 :                                            O_CLOEXEC);
     354             : 
     355         284 :     LOG(SYSCALL,
     356             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     357             :                 gkfs::syscall::executed,
     358         284 :         SYS_dup3, args, ifd);
     359             : 
     360         284 :     assert(::syscall_error_code(ifd) == 0);
     361             : 
     362             : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
     363         284 :     long args2[gkfs::syscall::MAX_ARGS]{fd};
     364             : #endif
     365             : 
     366         284 :     LOG(SYSCALL,
     367             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     368             :                 gkfs::syscall::not_executed,
     369         284 :         SYS_close, args2);
     370             : 
     371             : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
     372         284 :     int rv = ::syscall_no_intercept(SYS_close, fd);
     373             : #else
     374             :     ::syscall_no_intercept(SYS_close, fd);
     375             : #endif
     376             : 
     377         284 :     LOG(SYSCALL,
     378             :         gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel |
     379             :                 gkfs::syscall::executed,
     380         284 :         SYS_close, args2, rv);
     381             : 
     382         284 :     LOG(DEBUG, "    (fd {} relocated to ifd {})", fd, ifd);
     383             : 
     384         284 :     return ifd;
     385             : }
     386             : 
     387             : void
     388        4077 : PreloadContext::unregister_internal_fd(int fd) {
     389             : 
     390        4077 :     LOG(DEBUG, "unregistering internal fd {}", fd);
     391             : 
     392        4077 :     assert(fd >= MIN_INTERNAL_FD);
     393             : 
     394        4077 :     const auto pos = fd - MIN_INTERNAL_FD;
     395             : 
     396        4077 :     std::lock_guard<std::mutex> lock(internal_fds_mutex_);
     397        4077 :     internal_fds_.set(pos);
     398        4077 : }
     399             : 
     400             : bool
     401       11720 : PreloadContext::is_internal_fd(int fd) const {
     402             : 
     403       11720 :     if(fd < MIN_INTERNAL_FD) {
     404             :         return false;
     405             :     }
     406             : 
     407        3120 :     const auto pos = fd - MIN_INTERNAL_FD;
     408             : 
     409       14840 :     std::lock_guard<std::mutex> lock(internal_fds_mutex_);
     410        3120 :     return !internal_fds_.test(pos);
     411             : }
     412             : 
     413             : void
     414         269 : PreloadContext::protect_user_fds() {
     415             : 
     416         269 :     LOG(DEBUG, "Protecting application fds [{}, {}]", 0, MAX_USER_FDS - 1);
     417             : 
     418         269 :     const int nullfd =
     419         269 :             ::syscall_no_intercept(SYS_openat, 0, "/dev/null", O_RDONLY);
     420         269 :     assert(::syscall_error_code(nullfd) == 0);
     421         269 :     protected_fds_.set(nullfd);
     422             : 
     423     2414813 :     const auto fd_is_open = [](int fd) -> bool {
     424     2414544 :         const int ret = ::syscall_no_intercept(SYS_fcntl, fd, F_GETFD);
     425     2414544 :         const int error = ::syscall_error_code(ret);
     426     2414544 :         return error == 0 || error != EBADF;
     427             :     };
     428             : 
     429     2414813 :     for(int fd = 0; fd < MAX_USER_FDS; ++fd) {
     430     2414544 :         if(fd_is_open(fd)) {
     431        1076 :             LOG(DEBUG, "  fd {} was already in use, skipping", fd);
     432        1076 :             continue;
     433             :         }
     434             : 
     435     2413468 :         const int ret = ::syscall_no_intercept(SYS_dup3, nullfd, fd, O_CLOEXEC);
     436     2413468 :         assert(::syscall_error_code(ret) == 0);
     437     2413468 :         protected_fds_.set(fd);
     438             :     }
     439             : 
     440         269 :     internal_fds_must_relocate_ = false;
     441         269 : }
     442             : 
     443             : void
     444         269 : PreloadContext::unprotect_user_fds() {
     445             : 
     446     2414813 :     for(std::size_t fd = 0; fd < protected_fds_.size(); ++fd) {
     447     2414544 :         if(!protected_fds_[fd]) {
     448         807 :             continue;
     449             :         }
     450             : 
     451     2413737 :         const int ret =
     452     2414544 :                 ::syscall_error_code(::syscall_no_intercept(SYS_close, fd));
     453             : 
     454           0 :         if(ret != 0) {
     455           0 :             LOG(ERROR, "Failed to unprotect fd")
     456             :         }
     457             :     }
     458             : 
     459         269 :     internal_fds_must_relocate_ = true;
     460         269 : }
     461             : 
     462             : 
     463             : std::string
     464          32 : PreloadContext::get_hostname() {
     465          32 :     return hostname;
     466             : }
     467             : 
     468             : void
     469         272 : PreloadContext::set_replicas(const int repl) {
     470         272 :     replicas_ = repl;
     471         272 : }
     472             : 
     473             : int
     474        2781 : PreloadContext::get_replicas() {
     475        2781 :     return replicas_;
     476             : }
     477             : 
     478             : } // namespace preload
     479             : } // namespace gkfs

Generated by: LCOV version 1.16