LCOV - code coverage report
Current view: top level - tests/integration/harness/gkfs.io/util - file_compare.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 45 61 73.8 %
Date: 2024-04-30 13:21:35 Functions: 5 5 100.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 <binary_buffer.hpp>
      33             : #include <memory>
      34             : #include <fmt/format.h>
      35             : #include <reflection.hpp>
      36             : #include <serialize.hpp>
      37             : 
      38             : /* C includes */
      39             : #include <sys/types.h>
      40             : #include <unistd.h>
      41             : #include <fcntl.h>
      42             : 
      43             : using json = nlohmann::json;
      44             : 
      45         494 : struct file_compare_options {
      46             :     bool verbose{};
      47             :     std::string path_1{};
      48             :     std::string path_2{};
      49             :     size_t count{};
      50             : 
      51             :     REFL_DECL_STRUCT(file_compare_options, REFL_DECL_MEMBER(bool, verbose),
      52             :                      REFL_DECL_MEMBER(std::string, path_1),
      53             :                      REFL_DECL_MEMBER(std::string, path_2),
      54             :                      REFL_DECL_MEMBER(size_t, count));
      55             : };
      56             : 
      57             : struct file_compare_output {
      58             :     int retval;
      59             :     int errnum;
      60             : 
      61             :     REFL_DECL_STRUCT(file_compare_output, REFL_DECL_MEMBER(int, retval),
      62             :                      REFL_DECL_MEMBER(int, errnum));
      63             : };
      64             : 
      65             : void
      66           2 : to_json(json& record, const file_compare_output& out) {
      67           2 :     record = serialize(out);
      68           2 : }
      69             : 
      70             : int
      71           4 : open_file(const std::string& path, bool verbose) {
      72           4 :     auto fd = ::open(path.c_str(), O_RDONLY);
      73           4 :     if(fd == -1) {
      74           0 :         if(verbose) {
      75           0 :             fmt::print("open(pathname=\"{}\") = {}, errno: {} [{}]\n", path, fd,
      76           0 :                        errno, ::strerror(errno));
      77           0 :             return -1;
      78             :         }
      79           0 :         json out = file_compare_output{fd, errno};
      80           0 :         fmt::print("{}\n", out.dump(2));
      81           0 :         return -1;
      82             :     }
      83             :     return fd;
      84             : }
      85             : 
      86             : size_t
      87           4 : read_file(io::buffer& buf, int fd, size_t count) {
      88           4 :     ssize_t rv{};
      89           4 :     size_t total{};
      90           4 :     do {
      91           4 :         rv = ::read(fd, buf.data(), count - total);
      92           4 :         total += rv;
      93           4 :     } while(rv > 0 && total < count);
      94             : 
      95           4 :     if(rv < 0 && total != count) {
      96           0 :         json out = file_compare_output{(int) rv, errno};
      97           0 :         fmt::print("{}\n", out.dump(2));
      98           0 :         return 0;
      99             :     }
     100             :     return total;
     101             : }
     102             : 
     103             : void
     104           2 : file_compare_exec(const file_compare_options& opts) {
     105             : 
     106             :     // Open both files
     107           2 :     auto fd_1 = open_file(opts.path_1, opts.verbose);
     108           2 :     if(fd_1 == -1) {
     109           0 :         return;
     110             :     }
     111           2 :     auto fd_2 = open_file(opts.path_2, opts.verbose);
     112           2 :     if(fd_2 == -1) {
     113             :         return;
     114             :     }
     115             : 
     116             :     // read both files
     117           4 :     io::buffer buf_1(opts.count);
     118           2 :     auto rv = read_file(buf_1, fd_1, opts.count);
     119           2 :     if(rv == 0)
     120           0 :         return;
     121             : 
     122           4 :     io::buffer buf_2(opts.count);
     123           2 :     rv = read_file(buf_2, fd_2, opts.count);
     124           2 :     if(rv == 0)
     125           0 :         return;
     126             : 
     127             :     // memcmp both files to check if they're equal
     128           2 :     auto comp_rv = memcmp(buf_1.data(), buf_2.data(), opts.count);
     129           2 :     if(comp_rv != 0) {
     130           0 :         if(opts.verbose) {
     131           0 :             fmt::print("memcmp(path_1='{}', path_2='{}', count='{}') = '{}'\n",
     132           0 :                        opts.path_1, opts.path_2, opts.count, comp_rv);
     133             :             return;
     134             :         }
     135             :     }
     136             : 
     137           4 :     json out = file_compare_output{comp_rv, errno};
     138           4 :     fmt::print("{}\n", out.dump(2));
     139             : }
     140             : 
     141             : void
     142         247 : file_compare_init(CLI::App& app) {
     143             : 
     144             :     // Create the option and subcommand objects
     145         247 :     auto opts = std::make_shared<file_compare_options>();
     146         494 :     auto* cmd = app.add_subcommand("file_compare",
     147         494 :                                    "Execute the truncate() system call");
     148             : 
     149             :     // Add options to cmd, binding them to opts
     150         247 :     cmd->add_flag("-v,--verbose", opts->verbose,
     151         494 :                   "Produce human writeable output");
     152             : 
     153         494 :     cmd->add_option("path_1", opts->path_1, "Path to first file")
     154             :             ->required()
     155         494 :             ->type_name("");
     156             : 
     157         494 :     cmd->add_option("path_2", opts->path_2, "Path to second file")
     158             :             ->required()
     159         494 :             ->type_name("");
     160             : 
     161         247 :     cmd->add_option("count", opts->count,
     162         494 :                     "How many bytes to compare of each file")
     163             :             ->required()
     164         494 :             ->type_name("");
     165             : 
     166         990 :     cmd->callback([opts]() { file_compare_exec(*opts); });
     167         247 : }

Generated by: LCOV version 1.16