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 : }
|