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