1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
#define __STDC_FORMAT_MACROS |
6 |
|
|
|
7 |
|
|
#include "swissknife_diff_tool.h" |
8 |
|
|
|
9 |
|
|
#include <inttypes.h> |
10 |
|
|
#include <stdint.h> |
11 |
|
|
#include "cvmfs_config.h" |
12 |
|
|
|
13 |
|
|
#include <vector> |
14 |
|
|
|
15 |
|
|
#include "directory_entry.h" |
16 |
|
|
#include "download.h" |
17 |
|
|
#include "statistics.h" |
18 |
|
|
|
19 |
|
|
namespace swissknife { |
20 |
|
|
|
21 |
|
|
DiffTool::DiffTool(const std::string &repo_path, |
22 |
|
|
const history::History::Tag &old_tag, |
23 |
|
|
const history::History::Tag &new_tag, |
24 |
|
|
const std::string &temp_dir, |
25 |
|
|
download::DownloadManager *download_manager, |
26 |
|
|
perf::Statistics *statistics, |
27 |
|
|
bool machine_readable, |
28 |
|
|
bool ignore_timediff) |
29 |
|
|
: CatalogDiffTool<catalog::SimpleCatalogManager>( |
30 |
|
|
repo_path, old_tag.root_hash, new_tag.root_hash, temp_dir, |
31 |
|
|
download_manager), |
32 |
|
|
old_tag_(old_tag), |
33 |
|
|
new_tag_(new_tag), |
34 |
|
|
machine_readable_(machine_readable), |
35 |
|
|
ignore_timediff_(ignore_timediff) |
36 |
|
|
{ |
37 |
|
|
// Created here but ownership goes to the Statistics class |
38 |
|
|
// owned by the server tool. |
39 |
|
|
counter_total_added_ = statistics->Register("difftool.added_bytes", |
40 |
|
|
"Total number of bytes added"); |
41 |
|
|
counter_total_removed_ = statistics->Register("difftool.removed_bytes", |
42 |
|
|
"Total number of bytes " |
43 |
|
|
"removed"); |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
DiffTool::~DiffTool() {} |
47 |
|
|
|
48 |
|
|
std::string DiffTool::PrintEntryType(const catalog::DirectoryEntry &entry) { |
49 |
|
|
if (entry.IsRegular()) |
50 |
|
|
return machine_readable_ ? "F" : "file"; |
51 |
|
|
else if (entry.IsLink()) |
52 |
|
|
return machine_readable_ ? "S" : "symlink"; |
53 |
|
|
else if (entry.IsDirectory()) |
54 |
|
|
return machine_readable_ ? "D" : "directory"; |
55 |
|
|
else |
56 |
|
|
return machine_readable_ ? "U" : "unknown"; |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
std::string DiffTool::PrintDifferences( |
60 |
|
|
catalog::DirectoryEntryBase::Differences diff) { |
61 |
|
|
vector<string> result_list; |
62 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kName) |
63 |
|
|
result_list.push_back(machine_readable_ ? "N" : "name"); |
64 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kLinkcount) |
65 |
|
|
result_list.push_back(machine_readable_ ? "I" : "link-count"); |
66 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kSize) |
67 |
|
|
result_list.push_back(machine_readable_ ? "S" : "size"); |
68 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kMode) |
69 |
|
|
result_list.push_back(machine_readable_ ? "M" : "mode"); |
70 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kMtime) |
71 |
|
|
result_list.push_back(machine_readable_ ? "T" : "timestamp"); |
72 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kSymlink) |
73 |
|
|
result_list.push_back(machine_readable_ ? "L" : "symlink-target"); |
74 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kChecksum) |
75 |
|
|
result_list.push_back(machine_readable_ ? "C" : "content"); |
76 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kHardlinkGroup) |
77 |
|
|
result_list.push_back(machine_readable_ ? "G" : "hardlink-group"); |
78 |
|
|
if (diff & |
79 |
|
|
catalog::DirectoryEntryBase::Difference::kNestedCatalogTransitionFlags) { |
80 |
|
|
result_list.push_back(machine_readable_ ? "N" : "nested-catalog"); |
81 |
|
|
} |
82 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kChunkedFileFlag) |
83 |
|
|
result_list.push_back(machine_readable_ ? "P" : "chunked-file"); |
84 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kHasXattrsFlag) |
85 |
|
|
result_list.push_back(machine_readable_ ? "X" : "xattrs"); |
86 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kExternalFileFlag) |
87 |
|
|
result_list.push_back(machine_readable_ ? "E" : "external-file"); |
88 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kBindMountpointFlag) |
89 |
|
|
result_list.push_back(machine_readable_ ? "B" : "bind-mountpoint"); |
90 |
|
|
if (diff & catalog::DirectoryEntryBase::Difference::kHiddenFlag) |
91 |
|
|
result_list.push_back(machine_readable_ ? "H" : "hidden"); |
92 |
|
|
|
93 |
|
|
return machine_readable_ ? ("[" + JoinStrings(result_list, "") + "]") |
94 |
|
|
: (" [" + JoinStrings(result_list, ", ") + "]"); |
95 |
|
|
} |
96 |
|
|
|
97 |
|
|
void DiffTool::ReportHeader() { |
98 |
|
|
if (machine_readable_) { |
99 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, |
100 |
|
|
"# line descriptor: A - add, R - remove, M - modify, " |
101 |
|
|
"S - statistics; modify flags: S - size, M - mode, T - timestamp, " |
102 |
|
|
"C - content, L - symlink target; entry types: F - regular file, " |
103 |
|
|
"S - symbolic link, D - directory, N - nested catalog"); |
104 |
|
|
} else { |
105 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "DELTA: %s/r%u (%s) --> %s/r%u (%s)", |
106 |
|
|
old_tag_.name.c_str(), old_tag_.revision, |
107 |
|
|
StringifyTime(old_tag_.timestamp, true).c_str(), |
108 |
|
|
new_tag_.name.c_str(), new_tag_.revision, |
109 |
|
|
StringifyTime(new_tag_.timestamp, true).c_str()); |
110 |
|
|
} |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
void DiffTool::ReportAddition(const PathString &path, |
114 |
|
|
const catalog::DirectoryEntry &entry, |
115 |
|
|
const XattrList & /*xattrs*/, |
116 |
|
|
const FileChunkList& /*chunks*/) { |
117 |
|
|
// XXX careful we're casting silently from usint64 to int64 there... |
118 |
|
|
counter_total_added_->Xadd(entry.size()); |
119 |
|
|
string operation = machine_readable_ ? "A" : "add"; |
120 |
|
|
if (machine_readable_) { |
121 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s +%" PRIu64, operation.c_str(), |
122 |
|
|
PrintEntryType(entry).c_str(), path.c_str(), entry.size()); |
123 |
|
|
} else { |
124 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s +%" PRIu64 " bytes", |
125 |
|
|
path.c_str(), operation.c_str(), |
126 |
|
|
PrintEntryType(entry).c_str(), entry.size()); |
127 |
|
|
} |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
void DiffTool::ReportRemoval(const PathString &path, |
131 |
|
|
const catalog::DirectoryEntry &entry) { |
132 |
|
|
// XXX careful we're casting silently from usint64 to int64 there... |
133 |
|
|
counter_total_removed_->Xadd(entry.size()); |
134 |
|
|
string operation = machine_readable_ ? "R" : "remove"; |
135 |
|
|
if (machine_readable_) { |
136 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s -%" PRIu64, operation.c_str(), |
137 |
|
|
PrintEntryType(entry).c_str(), path.c_str(), entry.size()); |
138 |
|
|
} else { |
139 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s -%" PRIu64 " bytes", |
140 |
|
|
path.c_str(), operation.c_str(), |
141 |
|
|
PrintEntryType(entry).c_str(), entry.size()); |
142 |
|
|
} |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
void DiffTool::ReportModification(const PathString &path, |
146 |
|
|
const catalog::DirectoryEntry &entry_from, |
147 |
|
|
const catalog::DirectoryEntry &entry_to, |
148 |
|
|
const XattrList & /*xattrs*/, |
149 |
|
|
const FileChunkList& /*chunks*/) { |
150 |
|
|
catalog::DirectoryEntryBase::Differences diff = |
151 |
|
|
entry_from.CompareTo(entry_to); |
152 |
|
|
if (ignore_timediff_) { |
153 |
|
|
diff = diff & ~catalog::DirectoryEntryBase::Difference::kMtime; |
154 |
|
|
if (diff == 0) |
155 |
|
|
return; |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
string type_from = PrintEntryType(entry_from); |
159 |
|
|
string type_to = PrintEntryType(entry_to); |
160 |
|
|
string type = type_from; |
161 |
|
|
if (type_from != type_to) { |
162 |
|
|
type += machine_readable_ ? type_to : ("->" + type_to); |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
string operation = machine_readable_ ? "M" : "modify"; |
166 |
|
|
if (machine_readable_) { |
167 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %s %s", operation.c_str(), |
168 |
|
|
PrintDifferences(diff).c_str(), type.c_str(), path.c_str()); |
169 |
|
|
} else { |
170 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s%s", path.c_str(), |
171 |
|
|
operation.c_str(), type.c_str(), PrintDifferences(diff).c_str()); |
172 |
|
|
} |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
void DiffTool::ReportStats() { |
176 |
|
|
const catalog::Catalog *catalog_from = GetOldCatalog(); |
177 |
|
|
const catalog::Catalog *catalog_to = GetNewCatalog(); |
178 |
|
|
const catalog::Counters counters_from = catalog_from->GetCounters(); |
179 |
|
|
const catalog::Counters counters_to = catalog_to->GetCounters(); |
180 |
|
|
catalog::DeltaCounters counters_diff = |
181 |
|
|
catalog::Counters::Diff(counters_from, counters_to); |
182 |
|
|
string operation = machine_readable_ ? "S " : "d("; |
183 |
|
|
string type_file = machine_readable_ ? "F" : "# regular files):"; |
184 |
|
|
string type_symlink = machine_readable_ ? "S" : "# symlinks):"; |
185 |
|
|
string type_directory = machine_readable_ ? "D" : "# directories):"; |
186 |
|
|
string type_catalog = machine_readable_ ? "N" : "# catalogs):"; |
187 |
|
|
int64_t diff_file = |
188 |
|
|
counters_diff.self.regular_files + counters_diff.subtree.regular_files; |
189 |
|
|
int64_t diff_symlink = |
190 |
|
|
counters_diff.self.symlinks + counters_diff.subtree.symlinks; |
191 |
|
|
int64_t diff_catalog = counters_diff.self.nested_catalogs + |
192 |
|
|
counters_diff.subtree.nested_catalogs; |
193 |
|
|
// Nested catalogs make internally two directory entries |
194 |
|
|
int64_t diff_directory = counters_diff.self.directories + |
195 |
|
|
counters_diff.subtree.directories - diff_catalog; |
196 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(), |
197 |
|
|
type_file.c_str(), diff_file); |
198 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(), |
199 |
|
|
type_symlink.c_str(), diff_symlink); |
200 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(), |
201 |
|
|
type_directory.c_str(), diff_directory); |
202 |
|
|
LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(), |
203 |
|
|
type_catalog.c_str(), diff_catalog); |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
} // namespace swissknife |