Line |
Branch |
Exec |
Source |
1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
* |
4 |
|
|
* This command processes a repository's catalog structure to detect and remove |
5 |
|
|
* outdated and/or unneeded data objects. |
6 |
|
|
*/ |
7 |
|
|
|
8 |
|
|
|
9 |
|
|
#include "swissknife_list_reflog.h" |
10 |
|
|
|
11 |
|
|
#include "manifest.h" |
12 |
|
|
#include "object_fetcher.h" |
13 |
|
|
#include "reflog.h" |
14 |
|
|
#include "util/posix.h" |
15 |
|
|
#include "util/string.h" |
16 |
|
|
|
17 |
|
|
using namespace std; // NOLINT |
18 |
|
|
|
19 |
|
|
namespace swissknife { |
20 |
|
|
|
21 |
|
✗ |
ParameterList CommandListReflog::GetParams() const { |
22 |
|
✗ |
ParameterList r; |
23 |
|
✗ |
r.push_back(Parameter::Mandatory('r', "repository url / local storage path")); |
24 |
|
✗ |
r.push_back(Parameter::Mandatory('n', "fully qualified repository name")); |
25 |
|
✗ |
r.push_back(Parameter::Optional('R', "path to reflog.chksum file")); |
26 |
|
✗ |
r.push_back(Parameter::Optional('k', "repository master key(s) / dir")); |
27 |
|
✗ |
r.push_back(Parameter::Optional('t', "temporary directory")); |
28 |
|
✗ |
r.push_back(Parameter::Optional('o', "output file")); |
29 |
|
✗ |
r.push_back(Parameter::Optional('@', "proxy url")); |
30 |
|
✗ |
return r; |
31 |
|
|
} |
32 |
|
|
|
33 |
|
✗ |
int CommandListReflog::Main(const ArgumentList &args) { |
34 |
|
✗ |
const string &repo_url = *args.find('r')->second; |
35 |
|
✗ |
const string &repo_name = *args.find('n')->second; |
36 |
|
✗ |
const std::string &reflog_chksum_path = (args.count('R') > 0) ? |
37 |
|
✗ |
*args.find('R')->second : ""; |
38 |
|
✗ |
string repo_keys = (args.count('k') > 0) ? |
39 |
|
✗ |
*args.find('k')->second : ""; |
40 |
|
✗ |
if (DirectoryExists(repo_keys)) |
41 |
|
✗ |
repo_keys = JoinStrings(FindFilesBySuffix(repo_keys, ".pub"), ":"); |
42 |
|
✗ |
const string temp_directory = (args.count('t') > 0) ? |
43 |
|
✗ |
*args.find('t')->second : "/tmp"; |
44 |
|
✗ |
const string output_path = (args.count('o') > 0) ? |
45 |
|
✗ |
*args.find('o')->second : ""; |
46 |
|
|
|
47 |
|
✗ |
shash::Any reflog_hash; |
48 |
|
✗ |
if (reflog_chksum_path != "") { |
49 |
|
✗ |
if (!manifest::Reflog::ReadChecksum(reflog_chksum_path, &reflog_hash)) { |
50 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Could not read reflog checksum"); |
51 |
|
✗ |
return 1; |
52 |
|
|
} |
53 |
|
|
} |
54 |
|
|
|
55 |
|
✗ |
const bool follow_redirects = false; |
56 |
|
✗ |
const string proxy = (args.count('@') > 0) ? *args.find('@')->second : ""; |
57 |
|
✗ |
if (!this->InitDownloadManager(follow_redirects, proxy) || |
58 |
|
✗ |
!this->InitSignatureManager(repo_keys)) { |
59 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "failed to init repo connection"); |
60 |
|
✗ |
return 1; |
61 |
|
|
} |
62 |
|
|
|
63 |
|
|
bool success; |
64 |
|
✗ |
if (IsHttpUrl(repo_url)) { |
65 |
|
|
HttpObjectFetcher<> object_fetcher(repo_name, |
66 |
|
|
repo_url, |
67 |
|
|
temp_directory, |
68 |
|
|
download_manager(), |
69 |
|
✗ |
signature_manager()); |
70 |
|
✗ |
if (reflog_hash.IsNull()) { |
71 |
|
✗ |
manifest::Manifest *manifest = NULL; |
72 |
|
|
ObjectFetcherFailures::Failures failure; |
73 |
|
✗ |
switch (failure = object_fetcher.FetchManifest(&manifest)) { |
74 |
|
✗ |
case ObjectFetcherFailures::kFailOk: |
75 |
|
✗ |
reflog_hash = manifest->reflog_hash(); |
76 |
|
✗ |
break; |
77 |
|
✗ |
default: |
78 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Failed to fetch manifest: %s", |
79 |
|
|
Code2Ascii(failure)); |
80 |
|
✗ |
return 1; |
81 |
|
|
} |
82 |
|
✗ |
delete manifest; |
83 |
|
|
} |
84 |
|
✗ |
success = Run(&object_fetcher, repo_name, output_path, reflog_hash); |
85 |
|
✗ |
} else { |
86 |
|
✗ |
LocalObjectFetcher<> object_fetcher(repo_url, temp_directory); |
87 |
|
✗ |
success = Run(&object_fetcher, repo_name, output_path, reflog_hash); |
88 |
|
|
} |
89 |
|
|
|
90 |
|
✗ |
return (success) ? 0 : 1; |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
template <class ObjectFetcherT> |
94 |
|
✗ |
bool CommandListReflog::Run(ObjectFetcherT *object_fetcher, string repo_name, |
95 |
|
|
string output_path, shash::Any reflog_hash) |
96 |
|
|
{ |
97 |
|
|
typename ObjectFetcherT::ReflogTN *reflog; |
98 |
|
✗ |
reflog = FetchReflog(object_fetcher, repo_name, reflog_hash); |
99 |
|
|
|
100 |
|
✗ |
shash::Any null_hash = shash::Any(reflog_hash.algorithm); |
101 |
|
✗ |
objects_ = new SmallHashDynamic<shash::Any, bool>; |
102 |
|
✗ |
objects_->Init(1024, null_hash, hasher); |
103 |
|
|
|
104 |
|
|
// Traverse through catalogs and regular objects |
105 |
|
✗ |
vector<shash::Any> catalogs; |
106 |
|
✗ |
if (NULL == reflog || !reflog->List(SqlReflog::kRefCatalog, &catalogs)) { |
107 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Failed to list catalog reference log"); |
108 |
|
✗ |
return false; |
109 |
|
|
} |
110 |
|
✗ |
typename CatalogTraversal<ObjectFetcherT>::Parameters traversal_params; |
111 |
|
✗ |
traversal_params.object_fetcher = object_fetcher; |
112 |
|
✗ |
CatalogTraversal<ObjectFetcherT> traversal(traversal_params); |
113 |
|
✗ |
traversal.RegisterListener( |
114 |
|
|
&swissknife::CommandListReflog::CatalogCallback, |
115 |
|
|
this); |
116 |
|
✗ |
bool success = true; |
117 |
|
✗ |
vector<shash::Any>::iterator i = catalogs.begin(); |
118 |
|
✗ |
const vector<shash::Any>::const_iterator iend = catalogs.end(); |
119 |
|
✗ |
for (; i != iend && success; i++) { |
120 |
|
✗ |
success &= traversal.TraverseRevision(*i, |
121 |
|
|
CatalogTraversal<ObjectFetcherT>::kBreadthFirst); |
122 |
|
|
} |
123 |
|
|
|
124 |
|
✗ |
if (!success) { |
125 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
126 |
|
|
"Catalog traversal aborted due to an error"); |
127 |
|
✗ |
return false; |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
// Add history, certificate, metainfo objects from reflog |
131 |
|
✗ |
vector<shash::Any> histories, certificates, metainfos; |
132 |
|
✗ |
if (!reflog->List(SqlReflog::kRefHistory, &histories)) { |
133 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
134 |
|
|
"Failed to fetch history objects from reflog"); |
135 |
|
✗ |
return false; |
136 |
|
|
} |
137 |
|
✗ |
if (!reflog->List(SqlReflog::kRefCertificate, &certificates)) { |
138 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
139 |
|
|
"Failed to fetch certificate objects from reflog"); |
140 |
|
✗ |
return false; |
141 |
|
|
} |
142 |
|
✗ |
if (!reflog->List(SqlReflog::kRefMetainfo, &metainfos)) { |
143 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
144 |
|
|
"Failed to fetch metainfo objects from reflog"); |
145 |
|
✗ |
return false; |
146 |
|
|
} |
147 |
|
✗ |
InsertObjects(histories); |
148 |
|
✗ |
InsertObjects(certificates); |
149 |
|
✗ |
InsertObjects(metainfos); |
150 |
|
|
|
151 |
|
|
// Clean up reflog file |
152 |
|
✗ |
delete reflog; |
153 |
|
|
|
154 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Number of objects: %u", objects_->size()); |
155 |
|
|
|
156 |
|
✗ |
if (output_path == "") { |
157 |
|
✗ |
DumpObjects(stdout); |
158 |
|
|
} else { |
159 |
|
✗ |
int fd = open(output_path.c_str(), O_WRONLY | O_CREAT, 0644); |
160 |
|
✗ |
assert(fd); |
161 |
|
✗ |
FILE *stream = fdopen(fd, "w"); |
162 |
|
✗ |
DumpObjects(stream); |
163 |
|
✗ |
fclose(stream); // no need to call close after fclose |
164 |
|
|
} |
165 |
|
|
|
166 |
|
✗ |
return success; |
167 |
|
|
} |
168 |
|
|
|
169 |
|
✗ |
void CommandListReflog::CatalogCallback( |
170 |
|
|
const CatalogTraversalData<catalog::Catalog> &data) |
171 |
|
|
{ |
172 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Processing catalog \"%s\"", |
173 |
|
|
data.catalog->mountpoint().c_str()); |
174 |
|
|
const catalog::Catalog::HashVector &referenced_hashes = |
175 |
|
✗ |
data.catalog->GetReferencedObjects(); |
176 |
|
✗ |
InsertObjects(referenced_hashes); |
177 |
|
✗ |
if (data.catalog->hash() != objects_->empty_key()) |
178 |
|
✗ |
objects_->Insert(data.catalog->hash(), true); |
179 |
|
|
} |
180 |
|
|
|
181 |
|
✗ |
void CommandListReflog::InsertObjects(const vector<shash::Any> &list) { |
182 |
|
✗ |
vector<shash::Any>::const_iterator i = list.begin(); |
183 |
|
✗ |
const vector<shash::Any>::const_iterator iend = list.end(); |
184 |
|
✗ |
for (; i != iend; ++i) { |
185 |
|
✗ |
if ((*i) != objects_->empty_key()) |
186 |
|
✗ |
objects_->Insert(*i, true); |
187 |
|
|
} |
188 |
|
|
} |
189 |
|
|
|
190 |
|
✗ |
void CommandListReflog::DumpObjects(FILE *stream) |
191 |
|
|
{ |
192 |
|
✗ |
shash::Any empty_key = objects_->empty_key(); |
193 |
|
✗ |
shash::Any *hashes = objects_->keys(); |
194 |
|
✗ |
for (uint32_t i = 0; i < objects_->capacity(); ++i) { |
195 |
|
✗ |
if (hashes[i] != empty_key) { |
196 |
|
✗ |
fprintf(stream, "%s\n", hashes[i].ToString().c_str()); |
197 |
|
|
} |
198 |
|
|
} |
199 |
|
|
} |
200 |
|
|
|
201 |
|
|
} // namespace swissknife |
202 |
|
|
|