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 : /* C includes */
40 : #include <sys/types.h>
41 : #include <sys/stat.h>
42 : #include <fcntl.h>
43 : #include <unistd.h>
44 : #include <libgen.h>
45 :
46 : using json = nlohmann::json;
47 :
48 : // Creates a file in the path, and does a readdir comparing the count size (may be accumulated with previous operations)
49 494 : struct directory_validate_options {
50 : bool verbose{};
51 : std::string pathname;
52 : ::size_t count;
53 :
54 : REFL_DECL_STRUCT(directory_validate_options, REFL_DECL_MEMBER(bool, verbose),
55 : REFL_DECL_MEMBER(std::string, pathname),
56 : REFL_DECL_MEMBER(::size_t, count));
57 : };
58 :
59 : struct directory_validate_output {
60 : int retval;
61 : int errnum;
62 :
63 : REFL_DECL_STRUCT(directory_validate_output, REFL_DECL_MEMBER(int, retval),
64 : REFL_DECL_MEMBER(int, errnum));
65 : };
66 :
67 : void
68 4 : to_json(json& record, const directory_validate_output& out) {
69 4 : record = serialize(out);
70 4 : }
71 :
72 :
73 : /**
74 : * returns the number of elements existing in the path
75 : * @param opts
76 : * @param dir Path checked
77 : * @returns The number of elements in the directory
78 : */
79 : int
80 8 : number_of_elements(const directory_validate_options& opts, const std::string dir) {
81 8 : int num_elements = 0;
82 :
83 8 : ::DIR* dirp = ::opendir(dir.c_str());
84 :
85 8 : if(dirp == NULL) {
86 0 : if(opts.verbose) {
87 0 : fmt::print("readdir(pathname=\"{}\") = {}, errno: {} [{}]\n",
88 0 : dir, "NULL", errno, ::strerror(errno));
89 0 : return -3;
90 : }
91 :
92 0 : std::cout << "Error create directory" << std::endl;
93 0 : json out = directory_validate_output{-3, errno};
94 0 : fmt::print("{}\n", out.dump(2));
95 :
96 0 : return -3;
97 : }
98 :
99 1023 : struct ::dirent* entry;
100 :
101 1023 : while((entry = ::readdir(dirp)) != NULL) {
102 1015 : num_elements++;
103 : }
104 :
105 : return num_elements;
106 : }
107 :
108 :
109 : /**
110 : * Creates `count` files, with a suffix starting at the number of elements existing in the path
111 : * @param opts
112 : * @param path Path where the file is created
113 : * @param count Number of files to create
114 : * @returns The number of elements created plus the number of elements already in the directory (calculated, not checked)
115 : */
116 : int
117 4 : create_n_files (const directory_validate_options& opts, const std::string path, int count) {
118 :
119 : // Read Directory and get number of entries
120 4 : int num_elements = number_of_elements(opts, path);
121 :
122 1007 : for (int i = num_elements; i < count+num_elements; i++) {
123 3009 : std::string filename = path+"/file_auto_"+std::to_string(i);
124 1003 : int fd = ::creat(filename.c_str(), S_IRWXU);
125 1003 : if(fd == -1) {
126 0 : if(opts.verbose) {
127 0 : fmt::print(
128 : "directory_validate(pathname=\"{}\", count={}) = {}, errno: {} [{}]\n",
129 0 : filename, i, fd, errno, ::strerror(errno));
130 0 : return -2;
131 : }
132 0 : json out = directory_validate_output{-2, errno};
133 0 : fmt::print("{}\n", out.dump(2));
134 :
135 0 : return -2;
136 : }
137 :
138 1003 : ::close(fd);
139 : }
140 4 : return num_elements+count;
141 : }
142 :
143 : /**
144 : * Creates `count` files, and returns the number of elements in the path
145 : * If count == 0, the path is the filename to be created. Parent directory is checked
146 : * @param opts
147 : */
148 : void
149 4 : directory_validate_exec(const directory_validate_options& opts) {
150 :
151 4 : if (opts.count == 0) {
152 0 : int fd = ::creat(opts.pathname.c_str(), S_IRWXU);
153 :
154 0 : if(fd == -1) {
155 0 : if(opts.verbose) {
156 0 : fmt::print(
157 : "directory_validate(pathname=\"{}\", count={}) = {}, errno: {} [{}]\n",
158 0 : opts.pathname, opts.count, fd, errno, ::strerror(errno));
159 0 : return;
160 : }
161 0 : json out = directory_validate_output{-2, errno};
162 0 : fmt::print("{}\n", out.dump(2));
163 :
164 0 : return;
165 : }
166 :
167 0 : ::close(fd);
168 : }
169 : else {
170 4 : int created = create_n_files(opts, opts.pathname, opts.count);
171 4 : if (created <= 0) return;
172 : }
173 :
174 : // Do a readdir
175 8 : std::string dir = opts.pathname;
176 :
177 4 : if (opts.count == 0)
178 0 : dir = ::dirname((char*)opts.pathname.c_str());
179 :
180 4 : auto num_elements = number_of_elements(opts, dir);
181 :
182 4 : if(opts.verbose) {
183 0 : fmt::print("readdir(pathname=\"{}\") = [\n{}],\nerrno: {} [{}]\n",
184 : opts.pathname, num_elements, errno,
185 0 : ::strerror(errno));
186 0 : return;
187 : }
188 :
189 4 : errno = 0;
190 4 : json out = directory_validate_output{num_elements, errno};
191 4 : fmt::print("{}\n", out.dump(2));
192 4 : return;
193 :
194 : }
195 :
196 : void
197 247 : directory_validate_init(CLI::App& app) {
198 :
199 : // Create the option and subcommand objects
200 247 : auto opts = std::make_shared<directory_validate_options>();
201 494 : auto* cmd = app.add_subcommand(
202 : "directory_validate",
203 494 : "Create count files in the directory and execute a direntry system call and returns the number of elements");
204 :
205 : // Add options to cmd, binding them to opts
206 247 : cmd->add_flag("-v,--verbose", opts->verbose,
207 494 : "Produce human writeable output");
208 :
209 494 : cmd->add_option("pathname", opts->pathname, "directory to check or filename to create (if count is 0), elements will be checked in the parent dir")
210 : ->required()
211 494 : ->type_name("");
212 :
213 494 : cmd->add_option("count", opts->count, "Number of files to create. If 0, it creates only the entry in the pathname.")
214 : ->required()
215 494 : ->type_name("");
216 :
217 992 : cmd->callback([opts]() { directory_validate_exec(*opts); });
218 247 : }
|