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
|