19
19
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
20
20
*/
21
21
22
+ #include < algorithm>
22
23
#include < cstring>
23
24
#include < iostream>
25
+ #include < mutex>
24
26
#include < string_view>
25
27
#include < vector>
26
28
27
29
#include < boost/program_options.hpp>
28
30
31
+ #include < fmt/chrono.h>
32
+ #include < fmt/format.h>
33
+
29
34
#include < folly/String.h>
35
+ #include < folly/gen/String.h>
30
36
#include < folly/json.h>
31
37
#include < folly/portability/Unistd.h>
32
38
#include < folly/system/HardwareConcurrency.h>
33
39
40
+ #include " dwarfs/checksum.h"
34
41
#include " dwarfs/error.h"
35
42
#include " dwarfs/file_access.h"
36
43
#include " dwarfs/filesystem_v2.h"
41
48
#include " dwarfs/os_access.h"
42
49
#include " dwarfs/tool.h"
43
50
#include " dwarfs/util.h"
51
+ #include " dwarfs/worker_group.h"
44
52
#include " dwarfs_tool_main.h"
45
53
46
54
namespace dwarfs {
47
55
48
56
namespace po = boost::program_options;
49
57
58
+ namespace {
59
+
60
+ void do_list_files (filesystem_v2& fs, iolayer const & iol, bool verbose) {
61
+ auto max_width = [](auto const & vec) {
62
+ auto max = std::max_element (vec.begin (), vec.end ());
63
+ return std::to_string (*max).size ();
64
+ };
65
+
66
+ auto const uid_width = max_width (fs.get_all_uids ());
67
+ auto const gid_width = max_width (fs.get_all_gids ());
68
+
69
+ file_stat::off_type max_inode_size{0 };
70
+ fs.walk ([&](auto const & de) {
71
+ file_stat st;
72
+ fs.getattr (de.inode (), &st);
73
+ max_inode_size = std::max (max_inode_size, st.size );
74
+ });
75
+
76
+ auto const inode_size_width = fmt::format (" {:L}" , max_inode_size).size ();
77
+
78
+ fs.walk ([&](auto const & de) {
79
+ auto iv = de.inode ();
80
+ file_stat st;
81
+ fs.getattr (iv, &st);
82
+ auto name = de.unix_path ();
83
+ utf8_sanitize (name);
84
+
85
+ if (verbose) {
86
+ if (iv.is_symlink ()) {
87
+ auto target = fs.readlink (iv).value ();
88
+ utf8_sanitize (target);
89
+ name += " -> " + target;
90
+ }
91
+
92
+ iol.out << fmt::format (
93
+ " {3} {4:{0}}/{5:{1}} {6:{2}L} {7:%Y-%m-%d %H:%M} {8}\n " , uid_width,
94
+ gid_width, inode_size_width, iv.mode_string (), iv.getuid (),
95
+ iv.getgid (), st.size , fmt::localtime (st.mtime ), name);
96
+ } else if (!name.empty ()) {
97
+ iol.out << name << " \n " ;
98
+ }
99
+ });
100
+ }
101
+
102
+ void do_checksum (logger& lgr, filesystem_v2& fs, iolayer const & iol,
103
+ std::string const & algo, size_t num_workers) {
104
+ LOG_PROXY (debug_logger_policy, lgr);
105
+
106
+ worker_group wg{lgr, *iol.os , " checksum" , num_workers};
107
+ std::mutex mx;
108
+
109
+ fs.walk_data_order ([&](auto const & de) {
110
+ auto iv = de.inode ();
111
+ if (iv.is_regular_file ()) {
112
+ wg.add_job ([&, de, iv] {
113
+ file_stat st;
114
+
115
+ if (fs.getattr (de.inode (), &st) != 0 ) {
116
+ LOG_ERROR << " failed to get attributes for inode " << iv.inode_num ();
117
+ return ;
118
+ }
119
+
120
+ auto ranges = fs.readv (iv.inode_num (), st.size );
121
+
122
+ if (!ranges) {
123
+ LOG_ERROR << " failed to read inode " << iv.inode_num () << " : "
124
+ << std::strerror (-ranges.error ());
125
+ return ;
126
+ }
127
+
128
+ checksum cs (algo);
129
+
130
+ for (auto & fut : ranges.value ()) {
131
+ try {
132
+ auto range = fut.get ();
133
+ cs.update (range.data (), range.size ());
134
+ } catch (std::exception const & e) {
135
+ LOG_ERROR << " error reading data from inode " << iv.inode_num ()
136
+ << " : " << e.what ();
137
+ return ;
138
+ }
139
+ }
140
+
141
+ auto output = fmt::format (" {} {}\n " , cs.hexdigest (), de.unix_path ());
142
+
143
+ {
144
+ std::lock_guard lock (mx);
145
+ iol.out << output;
146
+ }
147
+ });
148
+ }
149
+ });
150
+
151
+ wg.wait ();
152
+ }
153
+
154
+ } // namespace
155
+
50
156
int dwarfsck_main (int argc, sys_char** argv, iolayer const & iol) {
157
+ using namespace folly ::gen;
158
+
51
159
const size_t num_cpu = std::max (folly::hardware_concurrency (), 1u );
52
160
53
- std::string input, export_metadata, image_offset;
161
+ auto algo_list = checksum::available_algorithms ();
162
+ auto checksum_desc = " print checksums for all files (" +
163
+ (from (algo_list) | unsplit (" , " )) + " )" ;
164
+
165
+ std::string input, export_metadata, image_offset, checksum_algo;
54
166
logger_options logopts;
55
167
size_t num_workers;
56
168
int detail;
57
169
bool quiet{false };
170
+ bool verbose{false };
58
171
bool output_json{false };
59
172
bool check_integrity{false };
60
173
bool no_check{false };
61
174
bool print_header{false };
175
+ bool list_files{false };
62
176
63
177
// clang-format off
64
178
po::options_description opts (" Command line options" );
@@ -72,12 +186,21 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) {
72
186
(" quiet,q" ,
73
187
po::value<bool >(&quiet)->zero_tokens (),
74
188
" don't print anything unless an error occurs" )
189
+ (" verbose,v" ,
190
+ po::value<bool >(&verbose)->zero_tokens (),
191
+ " produce verbose output" )
75
192
(" image-offset,O" ,
76
193
po::value<std::string>(&image_offset)->default_value (" auto" ),
77
194
" filesystem image offset in bytes" )
78
195
(" print-header,H" ,
79
196
po::value<bool >(&print_header)->zero_tokens (),
80
197
" print filesystem header to stdout and exit" )
198
+ (" list,l" ,
199
+ po::value<bool >(&list_files)->zero_tokens (),
200
+ " list all files and exit" )
201
+ (" checksum" ,
202
+ po::value<std::string>(&checksum_algo),
203
+ checksum_desc.c_str ())
81
204
(" num-workers,n" ,
82
205
po::value<size_t >(&num_workers)->default_value (num_cpu),
83
206
" number of reader worker threads" )
@@ -138,10 +261,16 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) {
138
261
return 1 ;
139
262
}
140
263
264
+ if (vm.count (" checksum" ) && !checksum::is_available (checksum_algo)) {
265
+ LOG_WARN << " checksum algorithm not available: " << checksum_algo;
266
+ return 1 ;
267
+ }
268
+
141
269
if (print_header &&
142
- (output_json || !export_metadata.empty () || check_integrity)) {
270
+ (output_json || !export_metadata.empty () || check_integrity ||
271
+ list_files || !checksum_algo.empty ())) {
143
272
LOG_WARN << " --print-header is mutually exclusive with --json, "
144
- " --export-metadata and --check-integrity" ;
273
+ " --export-metadata, --check-integrity, --list and --checksum " ;
145
274
return 1 ;
146
275
}
147
276
@@ -191,14 +320,22 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) {
191
320
: filesystem_check_level::CHECKSUM;
192
321
auto errors = no_check ? 0 : fs.check (level, num_workers);
193
322
194
- if (!quiet) {
323
+ if (!quiet && !list_files && checksum_algo. empty () ) {
195
324
if (output_json) {
196
325
iol.out << folly::toPrettyJson (fs.info_as_dynamic (detail)) << " \n " ;
197
326
} else {
198
327
fs.dump (iol.out , detail);
199
328
}
200
329
}
201
330
331
+ if (list_files) {
332
+ do_list_files (fs, iol, verbose);
333
+ }
334
+
335
+ if (!checksum_algo.empty ()) {
336
+ do_checksum (lgr, fs, iol, checksum_algo, num_workers);
337
+ }
338
+
202
339
if (errors > 0 ) {
203
340
return 1 ;
204
341
}
0 commit comments