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/hooks.hpp>
31 : #include <client/preload.hpp>
32 : #include <client/preload_util.hpp>
33 : #include <client/logging.hpp>
34 : #include <client/gkfs_functions.hpp>
35 : #include <client/path.hpp>
36 : #include <client/open_dir.hpp>
37 :
38 : #include <common/path_util.hpp>
39 :
40 : #include <memory>
41 :
42 : extern "C" {
43 : #include <fcntl.h>
44 : #include <sys/stat.h>
45 : #include <sys/statfs.h>
46 : }
47 :
48 : namespace {
49 :
50 : // TODO replace all internal gkfs errno variable usage with LEAF
51 : inline int
52 1362 : with_errno(int ret) {
53 31 : return (ret < 0) ? -errno : ret;
54 : }
55 :
56 : } // namespace
57 :
58 : namespace gkfs::hook {
59 :
60 : int
61 9054 : hook_openat(int dirfd, const char* cpath, int flags, mode_t mode) {
62 :
63 9054 : LOG(DEBUG, "{}() called with fd: {}, path: \"{}\", flags: {}, mode: {}",
64 9054 : __func__, dirfd, cpath, flags, mode);
65 :
66 18108 : std::string resolved;
67 9054 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
68 9054 : switch(rstatus) {
69 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
70 0 : return syscall_no_intercept_wrapper(SYS_openat, dirfd, cpath, flags,
71 0 : mode);
72 :
73 7909 : case gkfs::preload::RelativizeStatus::external:
74 7909 : return syscall_no_intercept_wrapper(SYS_openat, dirfd,
75 7909 : resolved.c_str(), flags, mode);
76 :
77 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
78 : return -ENOTDIR;
79 :
80 1145 : case gkfs::preload::RelativizeStatus::internal:
81 9060 : return with_errno(gkfs::syscall::gkfs_open(resolved, mode, flags));
82 :
83 0 : default:
84 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
85 : return -EINVAL;
86 : }
87 : }
88 :
89 : int
90 11857 : hook_close(int fd) {
91 :
92 11857 : LOG(DEBUG, "{}() called with fd: {}", __func__, fd);
93 :
94 11857 : if(CTX->file_map()->exist(fd)) {
95 : // No call to the daemon is required
96 1004 : CTX->file_map()->remove(fd);
97 1004 : return 0;
98 : }
99 :
100 10853 : if(CTX->is_internal_fd(fd)) {
101 : // the client application (for some reason) is trying to close an
102 : // internal fd: ignore it
103 : return 0;
104 : }
105 :
106 7907 : return syscall_no_intercept_wrapper(SYS_close, fd);
107 : }
108 : #ifdef SYS_stat
109 : int
110 46 : hook_stat(const char* path, struct stat* buf) {
111 :
112 46 : LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path,
113 46 : fmt::ptr(buf));
114 :
115 92 : std::string rel_path;
116 46 : if(CTX->relativize_path(path, rel_path, false)) {
117 61 : return with_errno(gkfs::syscall::gkfs_stat(rel_path, buf));
118 : }
119 :
120 1 : return syscall_no_intercept_wrapper(SYS_stat, rel_path.c_str(), buf);
121 : }
122 : #endif
123 :
124 : #ifdef STATX_TYPE
125 :
126 : int
127 4 : hook_statx(int dirfd, const char* path, int flags, unsigned int mask,
128 : struct ::statx* buf) {
129 :
130 4 : LOG(DEBUG,
131 : "{}() called with dirfd: '{}', path: \"{}\", flags: '{}', mask: '{}', buf: '{}'",
132 4 : __func__, dirfd, path, flags, mask, fmt::ptr(buf));
133 :
134 8 : std::string resolved;
135 4 : auto rstatus = CTX->relativize_fd_path(dirfd, path, resolved);
136 4 : switch(rstatus) {
137 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
138 0 : return syscall_no_intercept_wrapper(SYS_statx, dirfd, path, flags,
139 0 : mask, buf);
140 :
141 0 : case gkfs::preload::RelativizeStatus::external:
142 0 : return syscall_no_intercept_wrapper(
143 0 : SYS_statx, dirfd, resolved.c_str(), flags, mask, buf);
144 :
145 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
146 : return -ENOTDIR;
147 :
148 4 : case gkfs::preload::RelativizeStatus::internal:
149 5 : return with_errno(gkfs::syscall::gkfs_statx(dirfd, resolved.c_str(),
150 8 : flags, mask, buf));
151 :
152 0 : default:
153 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
154 : return -EINVAL;
155 : }
156 :
157 : return syscall_no_intercept(SYS_statx, dirfd, path, flags, mask, buf);
158 : }
159 :
160 : #endif
161 :
162 : #ifdef SYS_lstat
163 : int
164 2 : hook_lstat(const char* path, struct stat* buf) {
165 :
166 2 : LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path,
167 2 : fmt::ptr(buf));
168 :
169 4 : std::string rel_path;
170 2 : if(CTX->relativize_path(path, rel_path)) {
171 2 : return with_errno(gkfs::syscall::gkfs_stat(rel_path, buf));
172 : }
173 1 : return syscall_no_intercept_wrapper(SYS_lstat, rel_path.c_str(), buf);
174 : }
175 : #endif
176 :
177 : int
178 267 : hook_fstat(unsigned int fd, struct stat* buf) {
179 :
180 267 : LOG(DEBUG, "{}() called with fd: {}, buf: {}", __func__, fd, fmt::ptr(buf));
181 :
182 267 : if(CTX->file_map()->exist(fd)) {
183 38 : auto path = CTX->file_map()->get(fd)->path();
184 : #ifdef HAS_RENAME
185 : // Special case for fstat and rename, fd points to new file...
186 : // We can change file_map and recall
187 38 : auto md = gkfs::utils::get_metadata(path, false);
188 19 : if(md.has_value() && md.value().blocks() == -1) {
189 0 : path = md.value().rename_path();
190 : }
191 : #endif
192 19 : return with_errno(gkfs::syscall::gkfs_stat(path, buf));
193 : }
194 248 : return syscall_no_intercept_wrapper(SYS_fstat, fd, buf);
195 : }
196 :
197 : int
198 0 : hook_fstatat(int dirfd, const char* cpath, struct stat* buf, int flags) {
199 :
200 0 : LOG(DEBUG, "{}() called with path: \"{}\", fd: {}, buf: {}, flags: {}",
201 0 : __func__, cpath, dirfd, fmt::ptr(buf), flags);
202 :
203 0 : std::string resolved;
204 0 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved, flags);
205 0 : switch(rstatus) {
206 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
207 0 : return syscall_no_intercept_wrapper(SYS_newfstatat, dirfd, cpath,
208 0 : buf, flags);
209 :
210 0 : case gkfs::preload::RelativizeStatus::external:
211 0 : return syscall_no_intercept_wrapper(SYS_newfstatat, dirfd,
212 0 : resolved.c_str(), buf, flags);
213 :
214 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
215 : return -ENOTDIR;
216 :
217 0 : case gkfs::preload::RelativizeStatus::internal:
218 0 : return with_errno(gkfs::syscall::gkfs_stat(resolved, buf));
219 :
220 0 : default:
221 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
222 : return -EINVAL;
223 : }
224 : }
225 :
226 : int
227 168373 : hook_read(unsigned int fd, void* buf, size_t count) {
228 :
229 168373 : LOG(DEBUG, "{}() called with fd: {}, buf: {} count: {}", __func__, fd,
230 168373 : fmt::ptr(buf), count);
231 :
232 168373 : if(CTX->file_map()->exist(fd)) {
233 24 : return with_errno(gkfs::syscall::gkfs_read(fd, buf, count));
234 : }
235 168349 : return syscall_no_intercept_wrapper(SYS_read, fd, buf, count);
236 : }
237 :
238 : int
239 2 : hook_pread(unsigned int fd, char* buf, size_t count, loff_t pos) {
240 :
241 2 : LOG(DEBUG, "{}() called with fd: {}, buf: {}, count: {}, pos: {}", __func__,
242 2 : fd, fmt::ptr(buf), count, pos);
243 :
244 2 : if(CTX->file_map()->exist(fd)) {
245 1 : return with_errno(gkfs::syscall::gkfs_pread_ws(fd, buf, count, pos));
246 : }
247 : /* Since kernel 2.6: pread() became pread64(), and pwrite() became
248 : * pwrite64(). */
249 1 : return syscall_no_intercept_wrapper(SYS_pread64, fd, buf, count, pos);
250 : }
251 :
252 : int
253 1 : hook_readv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt) {
254 :
255 1 : LOG(DEBUG, "{}() called with fd: {}, iov: {}, iovcnt: {}", __func__, fd,
256 1 : fmt::ptr(iov), iovcnt);
257 :
258 1 : if(CTX->file_map()->exist(fd)) {
259 1 : return with_errno(gkfs::syscall::gkfs_readv(fd, iov, iovcnt));
260 : }
261 0 : return syscall_no_intercept_wrapper(SYS_readv, fd, iov, iovcnt);
262 : }
263 :
264 : int
265 1 : hook_preadv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt,
266 : unsigned long pos_l, unsigned long pos_h) {
267 :
268 1 : LOG(DEBUG,
269 : "{}() called with fd: {}, iov: {}, iovcnt: {}, "
270 : "pos_l: {},"
271 : "pos_h: {}",
272 1 : __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h);
273 :
274 1 : if(CTX->file_map()->exist(fd)) {
275 1 : return with_errno(gkfs::syscall::gkfs_preadv(fd, iov, iovcnt, pos_l));
276 : }
277 0 : return syscall_no_intercept_wrapper(SYS_preadv, fd, iov, iovcnt, pos_l);
278 : }
279 :
280 : int
281 166680 : hook_write(unsigned int fd, const char* buf, size_t count) {
282 :
283 166680 : LOG(DEBUG, "{}() called with fd: {}, buf: {}, count {}", __func__, fd,
284 166680 : fmt::ptr(buf), count);
285 :
286 166680 : if(CTX->file_map()->exist(fd)) {
287 31 : return with_errno(gkfs::syscall::gkfs_write(fd, buf, count));
288 : }
289 166649 : return syscall_no_intercept_wrapper(SYS_write, fd, buf, count);
290 : }
291 :
292 : int
293 3 : hook_pwrite(unsigned int fd, const char* buf, size_t count, loff_t pos) {
294 :
295 3 : LOG(DEBUG, "{}() called with fd: {}, buf: {}, count: {}, pos: {}", __func__,
296 3 : fd, fmt::ptr(buf), count, pos);
297 :
298 3 : if(CTX->file_map()->exist(fd)) {
299 2 : return with_errno(gkfs::syscall::gkfs_pwrite_ws(fd, buf, count, pos));
300 : }
301 : /* Since kernel 2.6: pread() became pread64(), and pwrite() became
302 : * pwrite64(). */
303 1 : return syscall_no_intercept_wrapper(SYS_pwrite64, fd, buf, count, pos);
304 : }
305 :
306 : int
307 2 : hook_writev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt) {
308 :
309 2 : LOG(DEBUG, "{}() called with fd: {}, iov: {}, iovcnt: {}", __func__, fd,
310 2 : fmt::ptr(iov), iovcnt);
311 :
312 2 : if(CTX->file_map()->exist(fd)) {
313 2 : return with_errno(gkfs::syscall::gkfs_writev(fd, iov, iovcnt));
314 : }
315 0 : return syscall_no_intercept_wrapper(SYS_writev, fd, iov, iovcnt);
316 : }
317 :
318 : int
319 2 : hook_pwritev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt,
320 : unsigned long pos_l, unsigned long pos_h) {
321 :
322 2 : LOG(DEBUG,
323 : "{}() called with fd: {}, iov: {}, iovcnt: {}, "
324 : "pos_l: {},"
325 : "pos_h: {}",
326 2 : __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h);
327 :
328 2 : if(CTX->file_map()->exist(fd)) {
329 2 : return with_errno(gkfs::syscall::gkfs_pwritev(fd, iov, iovcnt, pos_l));
330 : }
331 0 : return syscall_no_intercept_wrapper(SYS_pwritev, fd, iov, iovcnt, pos_l);
332 : }
333 :
334 : int
335 15 : hook_unlinkat(int dirfd, const char* cpath, int flags) {
336 :
337 15 : LOG(DEBUG, "{}() called with dirfd: {}, path: \"{}\", flags: {}", __func__,
338 15 : dirfd, cpath, flags);
339 :
340 15 : if((flags & ~AT_REMOVEDIR) != 0) {
341 0 : LOG(ERROR, "{}() Flags unknown: {}", __func__, flags);
342 0 : return -EINVAL;
343 : }
344 :
345 30 : std::string resolved;
346 15 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved, false);
347 15 : switch(rstatus) {
348 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
349 0 : return syscall_no_intercept_wrapper(SYS_unlinkat, dirfd, cpath,
350 0 : flags);
351 :
352 4 : case gkfs::preload::RelativizeStatus::external:
353 4 : return syscall_no_intercept_wrapper(SYS_unlinkat, dirfd,
354 4 : resolved.c_str(), flags);
355 :
356 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
357 : return -ENOTDIR;
358 :
359 11 : case gkfs::preload::RelativizeStatus::internal:
360 11 : if(flags & AT_REMOVEDIR) {
361 4 : return with_errno(gkfs::syscall::gkfs_rmdir(resolved));
362 : } else {
363 18 : return with_errno(gkfs::syscall::gkfs_remove(resolved));
364 : }
365 :
366 0 : default:
367 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
368 : return -EINVAL;
369 : }
370 : }
371 :
372 : int
373 1 : hook_symlinkat(const char* oldname, int newdfd, const char* newname) {
374 :
375 1 : LOG(DEBUG, "{}() called with oldname: \"{}\", newfd: {}, newname: \"{}\"",
376 1 : __func__, oldname, newdfd, newname);
377 :
378 2 : std::string oldname_resolved;
379 1 : if(CTX->relativize_path(oldname, oldname_resolved)) {
380 0 : LOG(WARNING, "{}() operation not supported", __func__);
381 0 : return -ENOTSUP;
382 : }
383 :
384 2 : std::string newname_resolved;
385 1 : auto rstatus =
386 1 : CTX->relativize_fd_path(newdfd, newname, newname_resolved, false);
387 1 : switch(rstatus) {
388 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
389 0 : return syscall_no_intercept_wrapper(SYS_symlinkat, oldname, newdfd,
390 0 : newname);
391 :
392 1 : case gkfs::preload::RelativizeStatus::external:
393 1 : return syscall_no_intercept_wrapper(SYS_symlinkat, oldname, newdfd,
394 1 : newname_resolved.c_str());
395 :
396 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
397 : return -ENOTDIR;
398 :
399 0 : case gkfs::preload::RelativizeStatus::internal:
400 0 : LOG(WARNING, "{}() operation not supported", __func__);
401 : return -ENOTSUP;
402 :
403 0 : default:
404 0 : LOG(ERROR, "{}() relativize status unknown", __func__);
405 : return -EINVAL;
406 : }
407 : }
408 :
409 : int
410 0 : hook_flock(unsigned long fd, int flags) {
411 0 : LOG(ERROR, "{}() called flock (Not Supported) with fd '{}' flags '{}'",
412 0 : __func__, fd, flags);
413 :
414 0 : if(CTX->file_map()->exist(fd)) {
415 : return 0;
416 : } else
417 0 : return -EBADF;
418 : }
419 :
420 : #ifdef SYS_access
421 : int
422 5 : hook_access(const char* path, int mask) {
423 :
424 5 : LOG(DEBUG, "{}() called path: \"{}\", mask: {}", __func__, path, mask);
425 :
426 10 : std::string rel_path;
427 5 : if(CTX->relativize_path(path, rel_path)) {
428 5 : auto ret = gkfs::syscall::gkfs_access(rel_path, mask);
429 5 : if(ret < 0) {
430 2 : return -errno;
431 : }
432 : return ret;
433 : }
434 0 : return syscall_no_intercept_wrapper(SYS_access, rel_path.c_str(), mask);
435 : }
436 : #endif
437 :
438 : int
439 4 : hook_faccessat(int dirfd, const char* cpath, int mode) {
440 :
441 4 : LOG(DEBUG, "{}() called with dirfd: {}, path: \"{}\", mode: {}", __func__,
442 4 : dirfd, cpath, mode);
443 :
444 8 : std::string resolved;
445 4 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
446 4 : switch(rstatus) {
447 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
448 0 : return syscall_no_intercept_wrapper(SYS_faccessat, dirfd, cpath,
449 0 : mode);
450 :
451 2 : case gkfs::preload::RelativizeStatus::external:
452 2 : return syscall_no_intercept_wrapper(SYS_faccessat, dirfd,
453 2 : resolved.c_str(), mode);
454 :
455 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
456 : return -ENOTDIR;
457 :
458 2 : case gkfs::preload::RelativizeStatus::internal:
459 4 : return with_errno(gkfs::syscall::gkfs_access(resolved, mode));
460 :
461 0 : default:
462 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
463 : return -EINVAL;
464 : }
465 : }
466 :
467 : #ifdef SYS_faccessat2
468 : int
469 : hook_faccessat2(int dirfd, const char* cpath, int mode, int flags) {
470 :
471 : LOG(DEBUG,
472 : "{}() called with dirfd: '{}', path: '{}', mode: '{}', flags: '{}'",
473 : __func__, dirfd, cpath, mode, flags);
474 :
475 : std::string resolved;
476 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
477 : switch(rstatus) {
478 : case gkfs::preload::RelativizeStatus::fd_unknown:
479 : return syscall_no_intercept_wrapper(SYS_faccessat2, dirfd, cpath,
480 : mode, flags);
481 :
482 : case gkfs::preload::RelativizeStatus::external:
483 : return syscall_no_intercept_wrapper(SYS_faccessat2, dirfd,
484 : resolved.c_str(), mode, flags);
485 :
486 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
487 : return -ENOTDIR;
488 :
489 : case gkfs::preload::RelativizeStatus::internal:
490 : // we do not use permissions and therefore do not handle `flags` for
491 : // now
492 : return with_errno(gkfs::syscall::gkfs_access(resolved, mode));
493 :
494 : default:
495 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
496 : return -EINVAL;
497 : }
498 : }
499 : #endif
500 :
501 : off_t
502 7917 : hook_lseek(unsigned int fd, off_t offset, unsigned int whence) {
503 :
504 7917 : LOG(DEBUG, "{}() called with fd: {}, offset: {}, whence: {}", __func__, fd,
505 7917 : offset, whence);
506 :
507 7917 : if(CTX->file_map()->exist(fd)) {
508 11 : auto off_ret = gkfs::syscall::gkfs_lseek(
509 11 : fd, static_cast<off64_t>(offset), whence);
510 11 : if(off_ret > std::numeric_limits<off_t>::max()) {
511 : return -EOVERFLOW;
512 11 : } else if(off_ret < 0) {
513 5 : return -errno;
514 : }
515 6 : LOG(DEBUG, "{}() returning {}", __func__, off_ret);
516 6 : return off_ret;
517 : }
518 7906 : return syscall_no_intercept_wrapper(SYS_lseek, fd, offset, whence);
519 : }
520 :
521 : int
522 7 : hook_truncate(const char* path, long length) {
523 :
524 7 : LOG(DEBUG, "{}() called with path: {}, offset: {}", __func__, path, length);
525 :
526 14 : std::string rel_path;
527 7 : if(CTX->relativize_path(path, rel_path)) {
528 8 : return with_errno(gkfs::syscall::gkfs_truncate(rel_path, length));
529 : }
530 1 : return syscall_no_intercept_wrapper(SYS_truncate, rel_path.c_str(), length);
531 : }
532 :
533 : int
534 2 : hook_ftruncate(unsigned int fd, unsigned long length) {
535 :
536 2 : LOG(DEBUG, "{}() called with fd: {}, offset: {}", __func__, fd, length);
537 :
538 2 : if(CTX->file_map()->exist(fd)) {
539 2 : auto path = CTX->file_map()->get(fd)->path();
540 1 : return with_errno(gkfs::syscall::gkfs_truncate(path, length));
541 : }
542 1 : return syscall_no_intercept_wrapper(SYS_ftruncate, fd, length);
543 : }
544 :
545 : int
546 2 : hook_dup(unsigned int fd) {
547 :
548 2 : LOG(DEBUG, "{}() called with oldfd: {}", __func__, fd);
549 :
550 2 : if(CTX->file_map()->exist(fd)) {
551 1 : return with_errno(gkfs::syscall::gkfs_dup(fd));
552 : }
553 1 : return syscall_no_intercept_wrapper(SYS_dup, fd);
554 : }
555 : #ifdef SYS_dup2
556 : int
557 2 : hook_dup2(unsigned int oldfd, unsigned int newfd) {
558 :
559 2 : LOG(DEBUG, "{}() called with oldfd: {}, newfd: {}", __func__, oldfd, newfd);
560 :
561 2 : if(CTX->file_map()->exist(oldfd)) {
562 1 : return with_errno(gkfs::syscall::gkfs_dup2(oldfd, newfd));
563 : }
564 1 : return syscall_no_intercept_wrapper(SYS_dup2, oldfd, newfd);
565 : }
566 : #endif
567 : int
568 2 : hook_dup3(unsigned int oldfd, unsigned int newfd, int flags) {
569 :
570 2 : LOG(DEBUG, "{}() called with oldfd: {}, newfd: {}, flags: {}", __func__,
571 2 : oldfd, newfd, flags);
572 :
573 2 : if(CTX->file_map()->exist(oldfd)) {
574 : // TODO implement O_CLOEXEC flag first which is used with fcntl(2)
575 : // It is in glibc since kernel 2.9. So maybe not that important :)
576 1 : LOG(WARNING, "{}() Not supported", __func__);
577 1 : return -ENOTSUP;
578 : }
579 1 : return syscall_no_intercept_wrapper(SYS_dup3, oldfd, newfd, flags);
580 : }
581 : #ifdef SYS_getdents
582 : int
583 0 : hook_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) {
584 :
585 0 : LOG(DEBUG, "{}() called with fd: {}, dirp: {}, count: {}", __func__, fd,
586 0 : fmt::ptr(dirp), count);
587 :
588 0 : if(CTX->file_map()->exist(fd)) {
589 0 : return with_errno(gkfs::syscall::gkfs_getdents(fd, dirp, count));
590 : }
591 0 : return syscall_no_intercept_wrapper(SYS_getdents, fd, dirp, count);
592 : }
593 : #endif
594 :
595 : int
596 30 : hook_getdents64(unsigned int fd, struct linux_dirent64* dirp,
597 : unsigned int count) {
598 :
599 30 : LOG(DEBUG, "{}() called with fd: {}, dirp: {}, count: {}", __func__, fd,
600 30 : fmt::ptr(dirp), count);
601 :
602 30 : if(CTX->file_map()->exist(fd)) {
603 30 : return with_errno(gkfs::syscall::gkfs_getdents64(fd, dirp, count));
604 : }
605 0 : return syscall_no_intercept_wrapper(SYS_getdents64, fd, dirp, count);
606 : }
607 :
608 :
609 : int
610 19 : hook_mkdirat(int dirfd, const char* cpath, mode_t mode) {
611 :
612 19 : LOG(DEBUG, "{}() called with dirfd: {}, path: \"{}\", mode: {}", __func__,
613 19 : dirfd, cpath, mode);
614 :
615 38 : std::string resolved;
616 19 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
617 19 : switch(rstatus) {
618 2 : case gkfs::preload::RelativizeStatus::external:
619 2 : return syscall_no_intercept_wrapper(SYS_mkdirat, dirfd,
620 2 : resolved.c_str(), mode);
621 :
622 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
623 0 : return syscall_no_intercept_wrapper(SYS_mkdirat, dirfd, cpath,
624 0 : mode);
625 :
626 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
627 : return -ENOTDIR;
628 :
629 17 : case gkfs::preload::RelativizeStatus::internal:
630 19 : return with_errno(
631 : gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR));
632 :
633 0 : default:
634 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
635 : return -EINVAL;
636 : }
637 : }
638 :
639 : int
640 3 : hook_fchmodat(int dirfd, const char* cpath, mode_t mode) {
641 :
642 3 : LOG(DEBUG, "{}() called dirfd: {}, path: \"{}\", mode: {}", __func__, dirfd,
643 3 : cpath, mode);
644 :
645 6 : std::string resolved;
646 3 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
647 3 : switch(rstatus) {
648 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
649 0 : return syscall_no_intercept_wrapper(SYS_fchmodat, dirfd, cpath,
650 0 : mode);
651 :
652 1 : case gkfs::preload::RelativizeStatus::external:
653 1 : return syscall_no_intercept_wrapper(SYS_fchmodat, dirfd,
654 1 : resolved.c_str(), mode);
655 :
656 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
657 : return -ENOTDIR;
658 :
659 2 : case gkfs::preload::RelativizeStatus::internal:
660 2 : LOG(WARNING, "{}() operation not supported", __func__);
661 : return -ENOTSUP;
662 :
663 0 : default:
664 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
665 : return -EINVAL;
666 : }
667 : }
668 :
669 : int
670 2 : hook_fchmod(unsigned int fd, mode_t mode) {
671 :
672 2 : LOG(DEBUG, "{}() called with fd: {}, mode: {}", __func__, fd, mode);
673 :
674 2 : if(CTX->file_map()->exist(fd)) {
675 1 : LOG(WARNING, "{}() operation not supported", __func__);
676 1 : return -ENOTSUP;
677 : }
678 1 : return syscall_no_intercept_wrapper(SYS_fchmod, fd, mode);
679 : }
680 :
681 : int
682 9 : hook_chdir(const char* path) {
683 :
684 9 : LOG(DEBUG, "{}() called with path: \"{}\"", __func__, path);
685 :
686 18 : std::string rel_path;
687 9 : bool internal = CTX->relativize_path(path, rel_path);
688 9 : if(internal) {
689 : // path falls in our namespace
690 8 : auto md = gkfs::utils::get_metadata(rel_path);
691 5 : if(!md) {
692 1 : LOG(ERROR, "{}() path {} errno {}", __func__, path, errno);
693 3 : return -errno;
694 : }
695 :
696 4 : if(!S_ISDIR(md->mode())) {
697 1 : LOG(ERROR, "{}() path is not a directory", __func__);
698 1 : return -ENOTDIR;
699 : }
700 : // TODO get complete path from relativize_path instead of
701 : // removing mountdir and then adding again here
702 3 : rel_path.insert(0, CTX->mountdir());
703 3 : if(gkfs::path::has_trailing_slash(rel_path)) {
704 : // open_dir is '/'
705 0 : rel_path.pop_back();
706 : }
707 : }
708 7 : try {
709 7 : gkfs::path::set_cwd(rel_path, internal);
710 1 : } catch(const std::system_error& se) {
711 1 : return -(se.code().value());
712 : }
713 : return 0;
714 : }
715 :
716 : int
717 3 : hook_fchdir(unsigned int fd) {
718 :
719 3 : LOG(DEBUG, "{}() called with fd: {}", __func__, fd);
720 :
721 3 : if(CTX->file_map()->exist(fd)) {
722 3 : auto open_dir = CTX->file_map()->get_dir(fd);
723 2 : if(open_dir == nullptr) {
724 : // Cast did not succeeded: open_file is a regular file
725 1 : LOG(ERROR, "{}() file descriptor refers to a normal file",
726 1 : __func__);
727 2 : return -EBADF;
728 : }
729 :
730 2 : std::string new_path = CTX->mountdir() + open_dir->path();
731 1 : if(gkfs::path::has_trailing_slash(new_path)) {
732 : // open_dir is '/'
733 1 : new_path.pop_back();
734 : }
735 1 : try {
736 1 : gkfs::path::set_cwd(new_path, true);
737 0 : } catch(const std::system_error& se) {
738 0 : return -(se.code().value());
739 : }
740 : } else {
741 1 : long ret = syscall_no_intercept_wrapper(SYS_fchdir, fd);
742 1 : if(ret < 0) {
743 0 : throw std::system_error(
744 : syscall_error_code(ret), std::system_category(),
745 0 : "Failed to change directory (fchdir syscall)");
746 : }
747 1 : gkfs::path::unset_env_cwd();
748 2 : CTX->cwd(gkfs::path::get_sys_cwd());
749 : }
750 : return 0;
751 : }
752 :
753 : int
754 5 : hook_getcwd(char* buf, unsigned long size) {
755 :
756 5 : LOG(DEBUG, "{}() called with buf: {}, size: {}", __func__, fmt::ptr(buf),
757 5 : size);
758 :
759 5 : if(CTX->cwd().size() + 1 > size) {
760 0 : LOG(ERROR, "{}() buffer too small to host current working dir",
761 0 : __func__);
762 0 : return -ERANGE;
763 : }
764 :
765 5 : strcpy(buf, CTX->cwd().c_str());
766 5 : return (CTX->cwd().size() + 1);
767 : }
768 :
769 : int
770 1 : hook_readlinkat(int dirfd, const char* cpath, char* buf, int bufsiz) {
771 :
772 1 : LOG(DEBUG, "{}() called with dirfd: {}, path \"{}\", buf: {}, bufsize: {}",
773 1 : __func__, dirfd, cpath, fmt::ptr(buf), bufsiz);
774 :
775 2 : std::string resolved;
776 1 : auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved, false);
777 1 : switch(rstatus) {
778 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
779 0 : return syscall_no_intercept_wrapper(SYS_readlinkat, dirfd, cpath,
780 0 : buf, bufsiz);
781 :
782 0 : case gkfs::preload::RelativizeStatus::external:
783 0 : return syscall_no_intercept_wrapper(SYS_readlinkat, dirfd,
784 0 : resolved.c_str(), buf, bufsiz);
785 :
786 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
787 : return -ENOTDIR;
788 :
789 1 : case gkfs::preload::RelativizeStatus::internal:
790 1 : LOG(WARNING, "{}() not supported", __func__);
791 : return -ENOTSUP;
792 :
793 0 : default:
794 0 : LOG(ERROR, "{}() relativize status unknown: {}", __func__);
795 : return -EINVAL;
796 : }
797 : }
798 :
799 : int
800 15816 : hook_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) {
801 :
802 15816 : LOG(DEBUG, "{}() called with fd: {}, cmd: {}, arg: {}", __func__, fd, cmd,
803 15816 : arg);
804 :
805 15816 : if(!CTX->file_map()->exist(fd)) {
806 15810 : return syscall_no_intercept_wrapper(SYS_fcntl, fd, cmd, arg);
807 : }
808 6 : int ret;
809 6 : switch(cmd) {
810 :
811 1 : case F_DUPFD:
812 1 : LOG(DEBUG, "{}() F_DUPFD on fd {}", __func__, fd);
813 15816 : return with_errno(gkfs::syscall::gkfs_dup(fd));
814 :
815 1 : case F_DUPFD_CLOEXEC:
816 1 : LOG(DEBUG, "{}() F_DUPFD_CLOEXEC on fd {}", __func__, fd);
817 1 : ret = gkfs::syscall::gkfs_dup(fd);
818 1 : if(ret == -1) {
819 0 : return -errno;
820 : }
821 1 : CTX->file_map()->get(fd)->set_flag(
822 : gkfs::filemap::OpenFile_flags::cloexec, true);
823 1 : return ret;
824 :
825 1 : case F_GETFD:
826 1 : LOG(DEBUG, "{}() F_GETFD on fd {}", __func__, fd);
827 2 : if(CTX->file_map()->get(fd)->get_flag(
828 : gkfs::filemap::OpenFile_flags::cloexec)) {
829 0 : return FD_CLOEXEC;
830 : }
831 : return 0;
832 :
833 1 : case F_GETFL:
834 1 : LOG(DEBUG, "{}() F_GETFL on fd {}", __func__, fd);
835 1 : ret = 0;
836 1 : if(CTX->file_map()->get(fd)->get_flag(
837 : gkfs::filemap::OpenFile_flags::rdonly)) {
838 : ret |= O_RDONLY;
839 : }
840 2 : if(CTX->file_map()->get(fd)->get_flag(
841 : gkfs::filemap::OpenFile_flags::wronly)) {
842 0 : ret |= O_WRONLY;
843 : }
844 2 : if(CTX->file_map()->get(fd)->get_flag(
845 : gkfs::filemap::OpenFile_flags::rdwr)) {
846 1 : ret |= O_RDWR;
847 : }
848 : return ret;
849 :
850 1 : case F_SETFD:
851 1 : LOG(DEBUG, "{}() [fd: {}, cmd: F_SETFD, FD_CLOEXEC: {}]", __func__,
852 1 : fd, (arg & FD_CLOEXEC));
853 1 : CTX->file_map()->get(fd)->set_flag(
854 1 : gkfs::filemap::OpenFile_flags::cloexec, (arg & FD_CLOEXEC));
855 1 : return 0;
856 :
857 0 : case F_GETLK:
858 0 : LOG(ERROR, "{}() F_GETLK on fd (Not Supported) {}", __func__, fd);
859 : return 0;
860 :
861 0 : case F_SETLK:
862 0 : LOG(ERROR, "{}() F_SETLK on fd (Not Supported) {}", __func__, fd);
863 : return 0;
864 :
865 1 : default:
866 1 : LOG(ERROR, "{}() unrecognized command {} on fd {}", __func__, cmd,
867 : fd);
868 : return -ENOTSUP;
869 : }
870 : }
871 :
872 : int
873 14 : hook_renameat(int olddfd, const char* oldname, int newdfd, const char* newname,
874 : unsigned int flags) {
875 :
876 14 : LOG(DEBUG,
877 : "{}() called with olddfd: {}, oldname: \"{}\", newfd: {}, "
878 : "newname \"{}\", flags {}",
879 14 : __func__, olddfd, oldname, newdfd, newname, flags);
880 :
881 14 : const char* oldpath_pass;
882 28 : std::string oldpath_resolved;
883 14 : auto oldpath_status =
884 14 : CTX->relativize_fd_path(olddfd, oldname, oldpath_resolved);
885 14 : switch(oldpath_status) {
886 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
887 0 : oldpath_pass = oldname;
888 0 : break;
889 :
890 2 : case gkfs::preload::RelativizeStatus::external:
891 2 : oldpath_pass = oldpath_resolved.c_str();
892 2 : break;
893 :
894 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
895 : return -ENOTDIR;
896 :
897 : case gkfs::preload::RelativizeStatus::internal:
898 : break;
899 :
900 0 : default:
901 0 : LOG(ERROR, "{}() relativize status unknown", __func__);
902 : return -EINVAL;
903 : }
904 :
905 14 : const char* newpath_pass;
906 28 : std::string newpath_resolved;
907 14 : auto newpath_status =
908 14 : CTX->relativize_fd_path(newdfd, newname, newpath_resolved);
909 14 : switch(newpath_status) {
910 0 : case gkfs::preload::RelativizeStatus::fd_unknown:
911 0 : newpath_pass = newname;
912 0 : break;
913 :
914 1 : case gkfs::preload::RelativizeStatus::external:
915 1 : newpath_pass = newpath_resolved.c_str();
916 1 : break;
917 :
918 : case gkfs::preload::RelativizeStatus::fd_not_a_dir:
919 : return -ENOTDIR;
920 :
921 13 : case gkfs::preload::RelativizeStatus::internal:
922 : #ifdef HAS_RENAME
923 13 : if(oldpath_status == gkfs::preload::RelativizeStatus::internal) {
924 15 : return with_errno(gkfs::syscall::gkfs_rename(oldpath_resolved,
925 : newpath_resolved));
926 : } else {
927 : return -ENOTSUP;
928 : }
929 : #else
930 : return -ENOTSUP;
931 : #endif
932 0 : default:
933 0 : LOG(ERROR, "{}() relativize status unknown", __func__);
934 : return -EINVAL;
935 : }
936 :
937 1 : return syscall_no_intercept_wrapper(SYS_renameat2, olddfd, oldpath_pass,
938 1 : newdfd, newpath_pass, flags);
939 : }
940 :
941 : int
942 1 : hook_statfs(const char* path, struct statfs* buf) {
943 :
944 1 : LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path,
945 1 : fmt::ptr(buf));
946 :
947 2 : std::string rel_path;
948 1 : if(CTX->relativize_path(path, rel_path)) {
949 1 : return with_errno(gkfs::syscall::gkfs_statfs(buf));
950 : }
951 0 : return syscall_no_intercept_wrapper(SYS_statfs, rel_path.c_str(), buf);
952 : }
953 :
954 : int
955 2 : hook_fstatfs(unsigned int fd, struct statfs* buf) {
956 :
957 2 : LOG(DEBUG, "{}() called with fd: {}, buf: {}", __func__, fd, fmt::ptr(buf));
958 :
959 2 : if(CTX->file_map()->exist(fd)) {
960 1 : return with_errno(gkfs::syscall::gkfs_statfs(buf));
961 : }
962 1 : return syscall_no_intercept_wrapper(SYS_fstatfs, fd, buf);
963 : }
964 :
965 : /* The function should broadcast a flush message (pmem_persist i.e.) if the
966 : * application needs the capabilities*/
967 : int
968 1 : hook_fsync(unsigned int fd) {
969 :
970 1 : LOG(DEBUG, "{}() called with fd: {}", __func__, fd);
971 :
972 1 : if(CTX->file_map()->exist(fd)) {
973 1 : errno = 0;
974 1 : return 0;
975 : }
976 :
977 0 : return syscall_no_intercept_wrapper(SYS_fsync, fd);
978 : }
979 :
980 : int
981 1 : hook_getxattr(const char* path, const char* name, void* value, size_t size) {
982 :
983 1 : LOG(DEBUG, "{}() called with path '{}' name '{}' value '{}' size '{}'",
984 1 : __func__, path, name, fmt::ptr(value), size);
985 :
986 2 : std::string rel_path;
987 1 : if(CTX->relativize_path(path, rel_path)) {
988 : return -ENOTSUP;
989 : }
990 0 : return syscall_no_intercept_wrapper(SYS_getxattr, path, name, value, size);
991 : }
992 :
993 : } // namespace gkfs::hook
|