LCOV - code coverage report
Current view: top level - tests/integration/harness/gkfs.io - syscall_coverage.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 135 238 56.7 %
Date: 2024-04-30 13:21:35 Functions: 3 4 75.0 %
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.
      12             : 
      13             :   GekkoFS is free software: you can redistribute it and/or modify
      14             :   it under the terms of the GNU General Public License as published by
      15             :   the Free Software Foundation, either version 3 of the License, or
      16             :   (at your option) any later version.
      17             : 
      18             :   GekkoFS 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 General Public License for more details.
      22             : 
      23             :   You should have received a copy of the GNU General Public License
      24             :   along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.
      25             : 
      26             :   SPDX-License-Identifier: GPL-3.0-or-later
      27             : */
      28             : 
      29             : /* C++ includes */
      30             : #include <CLI/CLI.hpp>
      31             : #include <nlohmann/json.hpp>
      32             : #include <memory>
      33             : #include <fmt/format.h>
      34             : #include <commands.hpp>
      35             : #include <reflection.hpp>
      36             : #include <serialize.hpp>
      37             : #include <binary_buffer.hpp>
      38             : 
      39             : 
      40             : /* C includes */
      41             : #include <sys/types.h>
      42             : #include <sys/stat.h>
      43             : #include <fcntl.h>
      44             : #include <unistd.h>
      45             : #include <sys/xattr.h>
      46             : 
      47             : // Syscall numbers
      48             : #include <sys/syscall.h>
      49             : 
      50             : using json = nlohmann::json;
      51             : 
      52         494 : struct syscall_coverage_options {
      53             :     bool verbose{};
      54             :     std::string pathname;
      55             : 
      56             : 
      57             :     REFL_DECL_STRUCT(syscall_coverage_options, REFL_DECL_MEMBER(bool, verbose),
      58             :                      REFL_DECL_MEMBER(std::string, pathname));
      59             : };
      60             : 
      61           1 : struct syscall_coverage_output {
      62             :     int retval;
      63             :     int errnum;
      64             :     std::string syscall;
      65             : 
      66             :     REFL_DECL_STRUCT(syscall_coverage_output, REFL_DECL_MEMBER(int, retval),
      67             :                      REFL_DECL_MEMBER(int, errnum),
      68             :                      REFL_DECL_MEMBER(std::string, syscall));
      69             : };
      70             : 
      71             : void
      72           1 : to_json(json& record, const syscall_coverage_output& out) {
      73           1 :     record = serialize(out);
      74           1 : }
      75             : 
      76             : void
      77           0 : output(const std::string syscall, const int ret,
      78             :        const syscall_coverage_options& opts) {
      79           0 :     if(opts.verbose) {
      80           0 :         fmt::print(
      81             :                 "syscall_coverage[{}](pathname=\"{}\") = {}, errno: {} [{}]\n",
      82           0 :                 syscall, opts.pathname, ret, errno, ::strerror(errno));
      83             :     }
      84             : 
      85           0 :     json out = syscall_coverage_output{ret, errno, syscall};
      86           0 :     fmt::print("{}\n", out.dump(2));
      87           0 : }
      88             : 
      89             : /*
      90             :  *  system calls
      91             :  *  that are being tested.
      92             :  */
      93             : void
      94           1 : syscall_coverage_exec(const syscall_coverage_options& opts) {
      95             : 
      96           1 :     int fd = ::open(opts.pathname.c_str(), O_RDWR);
      97             : 
      98           1 :     if(fd == -1) {
      99           0 :         output("open", fd, opts);
     100           0 :         return;
     101             :     }
     102             : 
     103             :     // create external
     104           1 :     int fdext = ::open("tmpfile", O_RDWR | O_CREAT, 0666);
     105             : 
     106             :     //  faccessat Internal
     107             : 
     108           1 :     auto rv = ::faccessat(AT_FDCWD, opts.pathname.c_str(), F_OK, 0);
     109             : 
     110           1 :     if(rv < 0) {
     111           0 :         output("faccessat", rv, opts);
     112           0 :         return;
     113             :     }
     114             : 
     115             :     // faccessat External
     116             : 
     117           1 :     rv = ::faccessat(AT_FDCWD, "/tmp", F_OK, 0);
     118             : 
     119           1 :     if(rv < 0) {
     120           0 :         output("faccessat, external", rv, opts);
     121           0 :         return;
     122             :     }
     123             : 
     124             :     // lstat
     125           1 :     struct stat st;
     126           1 :     rv = ::lstat(opts.pathname.c_str(), &st);
     127           1 :     if(rv < 0) {
     128           0 :         output("lstat", rv, opts);
     129           0 :         return;
     130             :     }
     131             : 
     132           1 :     rv = ::lstat("tmpfile", &st);
     133           1 :     if(rv < 0) {
     134           0 :         output("lstat", rv, opts);
     135           0 :         return;
     136             :     }
     137             : 
     138             :     // pwrite external
     139           1 :     rv = ::pwrite(fdext, "test", 4, 0);
     140           1 :     if(rv < 0) {
     141           0 :         output("pwrite", rv, opts);
     142           0 :         return;
     143             :     }
     144             : 
     145             :     // pread external
     146           1 :     char bufext[4];
     147           1 :     rv = ::pread(fdext, bufext, 4, 0);
     148           1 :     if(rv < 0) {
     149           0 :         output("pread", rv, opts);
     150           0 :         return;
     151             :     }
     152             : 
     153             :     // lseek external
     154           1 :     rv = ::lseek(fdext, 0, SEEK_SET);
     155           1 :     if(rv < 0) {
     156           0 :         output("lseek", rv, opts);
     157           0 :         return;
     158             :     }
     159             : 
     160             :     // ftruncate external
     161           1 :     rv = ::ftruncate(fdext, 0);
     162           1 :     if(rv < 0) {
     163           0 :         output("ftruncate", rv, opts);
     164           0 :         return;
     165             :     }
     166             : 
     167             :     // truncate exterlan
     168           1 :     rv = ::truncate("tmpfile", 0);
     169           1 :     if(rv < 0) {
     170           0 :         output("truncate", rv, opts);
     171           0 :         return;
     172             :     }
     173             : 
     174             : 
     175             :     // dup external
     176           1 :     int fdext2 = ::dup(fdext);
     177           1 :     if(fdext2 < 0) {
     178           0 :         output("dup", fdext2, opts);
     179           0 :         return;
     180             :     }
     181             : 
     182             :     // dup2 external
     183           1 :     int ffdext3 = 0;
     184           1 :     rv = ::dup2(fdext, ffdext3);
     185           1 :     if(rv < 0) {
     186           0 :         output("dup2", rv, opts);
     187           0 :         return;
     188             :     }
     189             : 
     190             : 
     191             :     // fchmod internal
     192           1 :     rv = ::fchmod(fd, 0777);
     193           1 :     if(errno != ENOTSUP) {
     194           0 :         output("fchmod", rv, opts);
     195           0 :         return;
     196             :     }
     197             : 
     198             :     // fchmod external
     199           1 :     rv = ::fchmod(fdext, 0777);
     200           1 :     if(rv < 0) {
     201           0 :         output("fchmod", rv, opts);
     202           0 :         return;
     203             :     }
     204             : 
     205             :     // fchmodat internal
     206           1 :     rv = ::fchmodat(AT_FDCWD, opts.pathname.c_str(), 0777, 0);
     207           1 :     if(errno != ENOTSUP) {
     208           0 :         output("fchmodat", rv, opts);
     209           0 :         return;
     210             :     }
     211             :     // fchmodat external
     212           1 :     rv = ::fchmodat(AT_FDCWD, "tmpfile", 0777, 0);
     213           1 :     if(rv < 0) {
     214           0 :         output("fchmodat, external", rv, opts);
     215           0 :         return;
     216             :     }
     217             : 
     218             :     // dup3 internal
     219           1 :     rv = ::dup3(fd, 0, 0);
     220           1 :     if(errno != ENOTSUP) {
     221           0 :         output("dup3", rv, opts);
     222           0 :         return;
     223             :     }
     224             : 
     225             :     // dup3 external
     226           1 :     int ffdext4 = 0;
     227           1 :     rv = ::dup3(fdext, ffdext4, 0);
     228           1 :     if(rv < 0) {
     229           0 :         output("dup3", rv, opts);
     230           0 :         return;
     231             :     }
     232             : 
     233             : 
     234             :     // fcntl
     235           1 :     rv = ::fcntl(fd, F_GETFD);
     236           1 :     if(rv < 0) {
     237           0 :         output("fcntl, F_GETFD", rv, opts);
     238           0 :         return;
     239             :     }
     240             : 
     241           1 :     rv = ::fcntl(fd, F_GETFL);
     242           1 :     if(rv < 0 || rv != O_RDWR) {
     243           0 :         output("fcntl, F_GETFL", rv, opts);
     244           0 :         return;
     245             :     }
     246             : 
     247             : 
     248           1 :     rv = ::fcntl(fd, F_SETFD, 0);
     249           1 :     if(rv < 0) {
     250           0 :         output("fcntl, F_SETFD", rv, opts);
     251           0 :         return;
     252             :     }
     253             : 
     254           1 :     rv = ::fcntl(fd, F_SETFL, 0);
     255           1 :     if(errno != ENOTSUP) {
     256           0 :         output("fcntl, F_SETFL", rv, opts);
     257           0 :         return;
     258             :     }
     259             : 
     260           1 :     rv = ::fcntl(fd, F_DUPFD, 0);
     261           1 :     if(rv < 0) {
     262           0 :         output("fcntl, F_DUPFD", rv, opts);
     263           0 :         return;
     264             :     }
     265             : 
     266           1 :     rv = ::fcntl(fd, F_DUPFD_CLOEXEC, 0);
     267           1 :     if(rv < 0) {
     268           0 :         output("fcntl, F_DUPFD_CLOEXEC", rv, opts);
     269           0 :         return;
     270             :     }
     271             : 
     272             :     // Fstatfs internal
     273             : 
     274           1 :     struct statfs stfs;
     275           1 :     rv = ::fstatfs(fd, &stfs);
     276           1 :     if(rv < 0) {
     277           0 :         output("fstatfs", rv, opts);
     278           0 :         return;
     279             :     }
     280             : 
     281             :     // Fstatfs external
     282           1 :     rv = ::fstatfs(fdext, &stfs);
     283           1 :     if(rv < 0) {
     284           0 :         output("fstatfs", rv, opts);
     285           0 :         return;
     286             :     }
     287             : 
     288             :     // fsync
     289             : 
     290           1 :     rv = ::fsync(fd);
     291           1 :     if(rv < 0) {
     292           0 :         output("fsync", rv, opts);
     293           0 :         return;
     294             :     }
     295             : 
     296             :     // getxattr
     297             : 
     298           1 :     char buf[1024];
     299           1 :     rv = ::getxattr(opts.pathname.c_str(), "user.test", buf, sizeof(buf));
     300           1 :     if(errno != ENOTSUP) {
     301           0 :         output("getxattr", rv, opts);
     302           0 :         return;
     303             :     }
     304             : 
     305             :     // readlinkat
     306           1 :     rv = ::readlinkat(AT_FDCWD, opts.pathname.c_str(), buf, sizeof(buf));
     307           1 :     if(errno != ENOTSUP) {
     308           0 :         output("readlinkat", rv, opts);
     309           0 :         return;
     310             :     }
     311             : 
     312             :     // chdir internal error
     313           1 :     rv = ::chdir(opts.pathname.c_str());
     314           1 :     if(errno != ENOTDIR) {
     315           0 :         output("chdir", rv, opts);
     316           0 :         return;
     317             :     }
     318             : 
     319             :     // chdir internal error
     320           2 :     std::string nonexist = opts.pathname+"x2";
     321           1 :     rv = ::chdir(nonexist.c_str());
     322           1 :     if(rv >= 0) {
     323           0 :         output("chdir", rv, opts);
     324           0 :         return;
     325             :     }
     326             : 
     327             :     // fchdir
     328           1 :     auto fddir = ::open(".", O_RDONLY);
     329           1 :     if(fddir < 0) {
     330           0 :         output("fchdir", fddir, opts);
     331           0 :         return;
     332             :     }
     333             : 
     334           1 :     rv = ::fchdir(fddir);
     335           1 :     if(rv < 0) {
     336           0 :         output("fchdir", rv, opts);
     337           0 :         return;
     338             :     }
     339             : 
     340             :     // ftruncate
     341           1 :     rv = ::ftruncate(fd, 0);
     342           1 :     if(rv < 0) {
     343           0 :         output("ftruncate", rv, opts);
     344           0 :         return;
     345             :     }
     346             : 
     347             :     // fchdir internal file
     348           1 :     rv = ::fchdir(fd);
     349           1 :     if(errno != EBADF) {
     350           0 :         output("fchdir", rv, opts);
     351           0 :         return;
     352             :     }
     353             : 
     354             : 
     355             :     // fchdir directory from opts.pathname
     356           1 :     auto fd2 = ::open(
     357           1 :             opts.pathname.substr(0, opts.pathname.find_last_of("/")).c_str(),
     358             :             O_RDONLY);
     359           1 :     if(fd2 < 0) {
     360           0 :         output("fchdir", fd2, opts);
     361           0 :         return;
     362             :     }
     363           1 :     rv = ::fchdir(fd2);
     364           1 :     if(rv < 0) {
     365           0 :         output("fchdir", rv, opts);
     366           0 :         return;
     367             :     }
     368             : 
     369           2 :     std::string pid = std::to_string(getpid());
     370           2 :     std::string path1 = "/tmp/"+pid+"test_rename";
     371           2 :     std::string path2 = "/tmp/"+pid+"test_rename2";
     372             : 
     373             :     // renameat external
     374           1 :     auto fdtmp = ::open(path1.c_str(), O_CREAT | O_WRONLY, 0644);
     375           1 :     ::close(fdtmp);
     376             : 
     377           1 :     rv = ::renameat(AT_FDCWD, path1.c_str(), AT_FDCWD,
     378             :                     opts.pathname.c_str());
     379           1 :     if(errno != ENOTSUP) {
     380           0 :         output("renameat_ext_to_int", rv, opts);
     381           0 :         return;
     382             :     }
     383             : 
     384           1 :     rv = ::renameat(AT_FDCWD, path1.c_str(), AT_FDCWD, path2.c_str());
     385           1 :     if(rv < 0) {
     386           0 :         output("renameat_ext_to_ext", rv, opts);
     387           0 :         return;
     388             :     }
     389             : 
     390             :     // open with O_APPEND
     391           2 :     std::string path_append = "/tmp/" + pid + "test_append";
     392           1 :     auto fd_append = ::open(path1.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0644);
     393           1 :     if(fd_append < 0) {
     394           0 :         output("open with O_APPEND", fd_append, opts);
     395           0 :         return;
     396             :     }
     397           1 :     rv = ::write(fd_append, "testappend", 10);
     398           1 :     if(rv < 0) {
     399           0 :         output("open with O_APPEND", rv, opts);
     400           0 :         return;
     401             :     }
     402           1 :     ::close(fd_append);
     403             : 
     404             :     // sys_open
     405           1 :     rv = ::syscall(SYS_open, opts.pathname.c_str(), O_RDONLY, 0);
     406           1 :     if(rv < 0) {
     407           0 :         output("sys_open", rv, opts);
     408           0 :         return;
     409             :     }
     410             : 
     411             :     // sys_creat
     412           1 :     rv = ::syscall(SYS_creat, opts.pathname.c_str(), 0777);
     413           1 :     if(rv < 0) {
     414           0 :         output("sys_creat", rv, opts);
     415           0 :         return;
     416             :     }
     417             : 
     418             :     // sys_unlinkat
     419           1 :     rv = ::syscall(SYS_unlinkat, AT_FDCWD, opts.pathname.c_str(), 0);
     420           1 :     if(rv < 0) {
     421           0 :         output("sys_unlinkat", rv, opts);
     422           0 :         return;
     423             :     }
     424             : 
     425             :     // sys_mkdirat
     426           2 :     std::string path = opts.pathname + "path";
     427           1 :     rv = ::syscall(SYS_mkdirat, AT_FDCWD, opts.pathname.c_str(), 0777);
     428           1 :     if(rv < 0) {
     429           0 :         output("sys_mkdirat", rv, opts);
     430           0 :         return;
     431             :     }
     432             : 
     433             :     // SYS_chmod
     434           1 :     rv = ::syscall(SYS_chmod, opts.pathname.c_str(), 0777);
     435           1 :     if(errno != ENOTSUP) {
     436           0 :         output("sys_chmod", rv, opts);
     437           0 :         return;
     438             :     }
     439             : 
     440             :     // hook_faccessat coverage
     441           1 :     rv = ::syscall(SYS_faccessat, AT_FDCWD, opts.pathname.c_str(), F_OK, 0);
     442           1 :     if(rv < 0) {
     443           0 :         output("sys_faccessat", rv, opts);
     444           0 :         return;
     445             :     }
     446             : 
     447           1 :     rv = ::syscall(SYS_faccessat, AT_FDCWD, "/tmp", F_OK, 0);
     448           1 :     if(rv < 0) {
     449           0 :         output("sys_faccessat", rv, opts);
     450           0 :         return;
     451             :     }
     452             : 
     453             : 
     454           1 :     rv = 0;
     455           1 :     errno = 0;
     456           1 :     auto syscall = "ALLOK";
     457           2 :     json out = syscall_coverage_output{(int) rv, errno, syscall};
     458           1 :     fmt::print("{}\n", out.dump(2));
     459           1 :     return;
     460             : }
     461             : 
     462             : void
     463         247 : syscall_coverage_init(CLI::App& app) {
     464             : 
     465             :     // Create the option and subcommand objects
     466         247 :     auto opts = std::make_shared<syscall_coverage_options>();
     467         247 :     auto* cmd =
     468         247 :             app.add_subcommand("syscall_coverage", "Execute severals syscalls");
     469             : 
     470             :     // Add options to cmd, binding them to opts
     471         247 :     cmd->add_flag("-v,--verbose", opts->verbose,
     472         494 :                   "Produce human writeable output");
     473             : 
     474         494 :     cmd->add_option("pathname", opts->pathname, "File name")
     475             :             ->required()
     476         494 :             ->type_name("");
     477             : 
     478         989 :     cmd->callback([opts]() { syscall_coverage_exec(*opts); });
     479         247 : }

Generated by: LCOV version 1.16