Line |
Branch |
Exec |
Source |
1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
#include "swissknife_reflog.h" |
6 |
|
|
|
7 |
|
|
#include <cassert> |
8 |
|
|
|
9 |
|
|
#include <string> |
10 |
|
|
#include <vector> |
11 |
|
|
|
12 |
|
|
#include "manifest.h" |
13 |
|
|
#include "object_fetcher.h" |
14 |
|
|
#include "upload_facility.h" |
15 |
|
|
#include "util/exception.h" |
16 |
|
|
|
17 |
|
|
namespace swissknife { |
18 |
|
|
|
19 |
|
|
typedef HttpObjectFetcher<> ObjectFetcher; |
20 |
|
|
|
21 |
|
|
class RootChainWalker { |
22 |
|
|
public: |
23 |
|
|
typedef ObjectFetcher::CatalogTN CatalogTN; |
24 |
|
|
typedef ObjectFetcher::HistoryTN HistoryTN; |
25 |
|
|
|
26 |
|
|
public: |
27 |
|
✗ |
RootChainWalker(const manifest::Manifest *manifest, |
28 |
|
|
ObjectFetcher *object_fetcher, |
29 |
|
|
manifest::Reflog *reflog) |
30 |
|
✗ |
: object_fetcher_(object_fetcher) |
31 |
|
✗ |
, reflog_(reflog) |
32 |
|
✗ |
, manifest_(manifest) {} |
33 |
|
|
|
34 |
|
|
void FindObjectsAndPopulateReflog(); |
35 |
|
|
|
36 |
|
|
protected: |
37 |
|
|
typedef std::vector<shash::Any> CatalogList; |
38 |
|
|
|
39 |
|
|
protected: |
40 |
|
|
CatalogTN* FetchCatalog(const shash::Any catalog_hash); |
41 |
|
|
HistoryTN* FetchHistory(const shash::Any history_hash); |
42 |
|
|
|
43 |
|
|
void WalkRootCatalogs(const shash::Any &root_catalog_hash); |
44 |
|
|
void WalkHistories(const shash::Any &history_hash); |
45 |
|
|
|
46 |
|
|
bool WalkCatalogsInHistory(const HistoryTN *history); |
47 |
|
|
void WalkListedCatalogs(const CatalogList &catalog_list); |
48 |
|
|
|
49 |
|
|
template <class DatabaseT> |
50 |
|
|
DatabaseT* ReturnOrAbort(const ObjectFetcherFailures::Failures failure, |
51 |
|
|
const shash::Any &content_hash, |
52 |
|
|
DatabaseT *database); |
53 |
|
|
|
54 |
|
|
private: |
55 |
|
|
ObjectFetcher *object_fetcher_; |
56 |
|
|
manifest::Reflog *reflog_; |
57 |
|
|
const manifest::Manifest *manifest_; |
58 |
|
|
}; |
59 |
|
|
|
60 |
|
|
|
61 |
|
✗ |
ParameterList CommandReconstructReflog::GetParams() const { |
62 |
|
✗ |
ParameterList r; |
63 |
|
✗ |
r.push_back(Parameter::Mandatory('r', "repository url")); |
64 |
|
✗ |
r.push_back(Parameter::Mandatory('u', "spooler definition string")); |
65 |
|
✗ |
r.push_back(Parameter::Mandatory('n', "fully qualified repository name")); |
66 |
|
✗ |
r.push_back(Parameter::Mandatory('t', "temporary directory")); |
67 |
|
✗ |
r.push_back(Parameter::Mandatory('k', "repository keychain")); |
68 |
|
✗ |
r.push_back(Parameter::Mandatory('R', "path to reflog.chksum file")); |
69 |
|
✗ |
r.push_back(Parameter::Optional('@', "proxy url")); |
70 |
|
✗ |
return r; |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
|
74 |
|
✗ |
int CommandReconstructReflog::Main(const ArgumentList &args) { |
75 |
|
✗ |
const std::string &repo_url = *args.find('r')->second; |
76 |
|
✗ |
const std::string &spooler = *args.find('u')->second; |
77 |
|
✗ |
const std::string &repo_name = *args.find('n')->second; |
78 |
|
✗ |
const std::string &tmp_dir = *args.find('t')->second; |
79 |
|
✗ |
const std::string &repo_keys = *args.find('k')->second; |
80 |
|
✗ |
const std::string &reflog_chksum_path = *args.find('R')->second; |
81 |
|
|
|
82 |
|
✗ |
const bool follow_redirects = false; |
83 |
|
✗ |
const std::string proxy = ((args.count('@') > 0) ? |
84 |
|
✗ |
*args.find('@')->second : ""); |
85 |
|
✗ |
if (!this->InitDownloadManager(follow_redirects, proxy) || |
86 |
|
✗ |
!this->InitSignatureManager(repo_keys)) { |
87 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "failed to init repo connection"); |
88 |
|
✗ |
return 1; |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
ObjectFetcher object_fetcher(repo_name, |
92 |
|
|
repo_url, |
93 |
|
|
tmp_dir, |
94 |
|
|
download_manager(), |
95 |
|
✗ |
signature_manager()); |
96 |
|
|
|
97 |
|
✗ |
UniquePtr<manifest::Manifest> manifest; |
98 |
|
✗ |
ObjectFetcher::Failures retval = object_fetcher.FetchManifest(&manifest); |
99 |
|
✗ |
if (retval != ObjectFetcher::kFailOk) { |
100 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "failed to load repository manifest " |
101 |
|
|
"(%d - %s)", |
102 |
|
|
retval, Code2Ascii(retval)); |
103 |
|
✗ |
return 1; |
104 |
|
|
} |
105 |
|
|
|
106 |
|
✗ |
const upload::SpoolerDefinition spooler_definition(spooler, shash::kAny); |
107 |
|
|
UniquePtr<upload::AbstractUploader> uploader( |
108 |
|
✗ |
upload::AbstractUploader::Construct(spooler_definition)); |
109 |
|
|
|
110 |
|
✗ |
if (!uploader.IsValid()) { |
111 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "failed to initialize spooler for '%s'", |
112 |
|
|
spooler.c_str()); |
113 |
|
✗ |
return 1; |
114 |
|
|
} |
115 |
|
|
|
116 |
|
✗ |
UniquePtr<manifest::Reflog> reflog(CreateEmptyReflog(tmp_dir, repo_name)); |
117 |
|
✗ |
reflog->TakeDatabaseFileOwnership(); |
118 |
|
|
|
119 |
|
✗ |
reflog->BeginTransaction(); |
120 |
|
✗ |
AddStaticManifestObjects(reflog.weak_ref(), manifest.weak_ref()); |
121 |
|
✗ |
RootChainWalker walker(manifest.weak_ref(), |
122 |
|
|
&object_fetcher, |
123 |
|
✗ |
reflog.weak_ref()); |
124 |
|
✗ |
walker.FindObjectsAndPopulateReflog(); |
125 |
|
✗ |
reflog->CommitTransaction(); |
126 |
|
|
|
127 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "found %lu entries", reflog->CountEntries()); |
128 |
|
|
|
129 |
|
✗ |
reflog->DropDatabaseFileOwnership(); |
130 |
|
✗ |
const std::string reflog_db = reflog->database_file(); |
131 |
|
✗ |
reflog.Destroy(); |
132 |
|
✗ |
uploader->UploadFile(reflog_db, ".cvmfsreflog"); |
133 |
|
✗ |
shash::Any reflog_hash(manifest->GetHashAlgorithm()); |
134 |
|
✗ |
manifest::Reflog::HashDatabase(reflog_db, &reflog_hash); |
135 |
|
✗ |
uploader->WaitForUpload(); |
136 |
|
✗ |
unlink(reflog_db.c_str()); |
137 |
|
|
|
138 |
|
✗ |
const int errors = uploader->GetNumberOfErrors(); |
139 |
|
✗ |
if (errors > 0) { |
140 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "failed to upload generated Reflog"); |
141 |
|
|
} |
142 |
|
|
|
143 |
|
✗ |
uploader->TearDown(); |
144 |
|
|
|
145 |
|
✗ |
manifest::Reflog::WriteChecksum(reflog_chksum_path, reflog_hash); |
146 |
|
|
|
147 |
|
✗ |
return (errors == 0) ? 0 : 1; |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
|
151 |
|
✗ |
void CommandReconstructReflog::AddStaticManifestObjects( |
152 |
|
|
manifest::Reflog *reflog, |
153 |
|
|
manifest::Manifest *manifest) const { |
154 |
|
✗ |
const shash::Any certificate = manifest->certificate(); |
155 |
|
✗ |
const shash::Any meta_info = manifest->meta_info(); |
156 |
|
✗ |
assert(!certificate.IsNull()); |
157 |
|
|
|
158 |
|
✗ |
bool success = reflog->AddCertificate(certificate); |
159 |
|
✗ |
assert(success); |
160 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "Certificate: %s", |
161 |
|
|
certificate.ToString().c_str()); |
162 |
|
|
|
163 |
|
✗ |
if (!meta_info.IsNull()) { |
164 |
|
✗ |
success = reflog->AddMetainfo(meta_info); |
165 |
|
✗ |
assert(success); |
166 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "Metainfo: %s", |
167 |
|
|
meta_info.ToString().c_str()); |
168 |
|
|
} |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
|
172 |
|
✗ |
void RootChainWalker::FindObjectsAndPopulateReflog() { |
173 |
|
✗ |
const shash::Any root_catalog = manifest_->catalog_hash(); |
174 |
|
✗ |
const shash::Any history = manifest_->history(); |
175 |
|
|
|
176 |
|
✗ |
assert(!root_catalog.IsNull()); |
177 |
|
✗ |
WalkRootCatalogs(root_catalog); |
178 |
|
|
|
179 |
|
✗ |
if (!history.IsNull()) { |
180 |
|
✗ |
WalkHistories(history); |
181 |
|
|
} |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
|
185 |
|
✗ |
void RootChainWalker::WalkRootCatalogs(const shash::Any &root_catalog_hash) { |
186 |
|
✗ |
shash::Any current_hash = root_catalog_hash; |
187 |
|
✗ |
UniquePtr<CatalogTN> current_catalog; |
188 |
|
|
|
189 |
|
✗ |
while (!current_hash.IsNull() && |
190 |
|
✗ |
!reflog_->ContainsCatalog(current_hash) && |
191 |
|
✗ |
(current_catalog = FetchCatalog(current_hash)).IsValid()) { |
192 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "Catalog: %s Revision: %" PRIu64, |
193 |
|
|
current_hash.ToString().c_str(), current_catalog->GetRevision()); |
194 |
|
|
|
195 |
|
✗ |
const bool success = reflog_->AddCatalog(current_hash); |
196 |
|
✗ |
assert(success); |
197 |
|
|
|
198 |
|
✗ |
current_hash = current_catalog->GetPreviousRevision(); |
199 |
|
|
} |
200 |
|
|
} |
201 |
|
|
|
202 |
|
|
|
203 |
|
✗ |
void RootChainWalker::WalkHistories(const shash::Any &history_hash) { |
204 |
|
✗ |
shash::Any current_hash = history_hash; |
205 |
|
✗ |
UniquePtr<HistoryTN> current_history; |
206 |
|
|
|
207 |
|
✗ |
while (!current_hash.IsNull() && |
208 |
|
✗ |
!reflog_->ContainsHistory(current_hash) && |
209 |
|
✗ |
(current_history = FetchHistory(current_hash)).IsValid()) { |
210 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "History: %s", |
211 |
|
|
current_hash.ToString().c_str()); |
212 |
|
|
|
213 |
|
✗ |
bool cancel = WalkCatalogsInHistory(current_history.weak_ref()); |
214 |
|
✗ |
const bool success = reflog_->AddHistory(current_hash); |
215 |
|
✗ |
assert(success); |
216 |
|
|
|
217 |
|
✗ |
if (cancel) { |
218 |
|
✗ |
current_hash = shash::Any(current_hash.algorithm); |
219 |
|
|
} else { |
220 |
|
✗ |
current_hash = current_history->previous_revision(); |
221 |
|
|
} |
222 |
|
|
} |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
|
226 |
|
|
/** |
227 |
|
|
* If false is returned, it indicates that the history database comes from |
228 |
|
|
* an early pre-release of the history functionality. The WalkHistory() |
229 |
|
|
* iteration will subsequently stop. This is necessary, for instance, to |
230 |
|
|
* handle the cernvm-prod.cern.ch repository. |
231 |
|
|
*/ |
232 |
|
✗ |
bool RootChainWalker::WalkCatalogsInHistory(const HistoryTN *history) { |
233 |
|
✗ |
CatalogList tag_hashes; |
234 |
|
✗ |
const bool list_success = history->GetHashes(&tag_hashes); |
235 |
|
✗ |
assert(list_success); |
236 |
|
|
|
237 |
|
✗ |
CatalogList bin_hashes; |
238 |
|
✗ |
const bool bin_success = history->ListRecycleBin(&bin_hashes); |
239 |
|
✗ |
if (!bin_success) { |
240 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, " Warning: 'recycle bin' table missing"); |
241 |
|
|
} |
242 |
|
|
|
243 |
|
✗ |
WalkListedCatalogs(tag_hashes); |
244 |
|
✗ |
WalkListedCatalogs(bin_hashes); |
245 |
|
|
|
246 |
|
✗ |
return !bin_success; |
247 |
|
|
} |
248 |
|
|
|
249 |
|
|
|
250 |
|
✗ |
void RootChainWalker::WalkListedCatalogs( |
251 |
|
|
const RootChainWalker::CatalogList &catalog_list) { |
252 |
|
✗ |
CatalogList::const_iterator i = catalog_list.begin(); |
253 |
|
✗ |
CatalogList::const_iterator iend = catalog_list.end(); |
254 |
|
✗ |
for (; i != iend; ++i) { |
255 |
|
✗ |
WalkRootCatalogs(*i); |
256 |
|
|
} |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
|
260 |
|
✗ |
RootChainWalker::CatalogTN* RootChainWalker::FetchCatalog( |
261 |
|
|
const shash::Any catalog_hash) { |
262 |
|
✗ |
CatalogTN *catalog = NULL; |
263 |
|
✗ |
const char *root_path = ""; |
264 |
|
|
ObjectFetcherFailures::Failures failure = |
265 |
|
✗ |
object_fetcher_->FetchCatalog(catalog_hash, root_path, &catalog); |
266 |
|
|
|
267 |
|
✗ |
return ReturnOrAbort(failure, catalog_hash, catalog); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
|
271 |
|
✗ |
RootChainWalker::HistoryTN* RootChainWalker::FetchHistory( |
272 |
|
|
const shash::Any history_hash) { |
273 |
|
✗ |
HistoryTN *history = NULL; |
274 |
|
|
ObjectFetcherFailures::Failures failure = |
275 |
|
✗ |
object_fetcher_->FetchHistory(&history, history_hash); |
276 |
|
|
|
277 |
|
✗ |
return ReturnOrAbort(failure, history_hash, history); |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
|
281 |
|
|
template <class DatabaseT> |
282 |
|
✗ |
DatabaseT* RootChainWalker::ReturnOrAbort( |
283 |
|
|
const ObjectFetcherFailures::Failures failure, |
284 |
|
|
const shash::Any &content_hash, |
285 |
|
|
DatabaseT *database) { |
286 |
|
✗ |
switch (failure) { |
287 |
|
✗ |
case ObjectFetcherFailures::kFailOk: |
288 |
|
✗ |
return database; |
289 |
|
✗ |
case ObjectFetcherFailures::kFailNotFound: |
290 |
|
✗ |
return NULL; |
291 |
|
✗ |
default: |
292 |
|
✗ |
PANIC(kLogStderr, "Failed to load object '%s' (%d - %s)", |
293 |
|
|
content_hash.ToStringWithSuffix().c_str(), failure, |
294 |
|
|
Code2Ascii(failure)); |
295 |
|
|
} |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
} // namespace swissknife |
299 |
|
|
|