GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/swissknife_reflog.cc Lines: 0 124 0.0 %
Date: 2019-02-03 02:48:13 Branches: 0 72 0.0 %

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
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
  return r;
70
}
71
72
73
int CommandReconstructReflog::Main(const ArgumentList &args) {
74
  const std::string &repo_url           = *args.find('r')->second;
75
  const std::string &spooler            = *args.find('u')->second;
76
  const std::string &repo_name          = *args.find('n')->second;
77
  const std::string &tmp_dir            = *args.find('t')->second;
78
  const std::string &repo_keys          = *args.find('k')->second;
79
  const std::string &reflog_chksum_path = *args.find('R')->second;
80
81
  const bool follow_redirects = false;
82
  if (!this->InitDownloadManager(follow_redirects) ||
83
      !this->InitVerifyingSignatureManager(repo_keys)) {
84
    LogCvmfs(kLogCvmfs, kLogStderr, "failed to init repo connection");
85
    return 1;
86
  }
87
88
  ObjectFetcher object_fetcher(repo_name,
89
                               repo_url,
90
                               tmp_dir,
91
                               download_manager(),
92
                               signature_manager());
93
94
  UniquePtr<manifest::Manifest> manifest;
95
  ObjectFetcher::Failures retval = object_fetcher.FetchManifest(&manifest);
96
  if (retval != ObjectFetcher::kFailOk) {
97
    LogCvmfs(kLogCvmfs, kLogStderr, "failed to load repository manifest "
98
                                    "(%d - %s)",
99
                                    retval, Code2Ascii(retval));
100
    return 1;
101
  }
102
103
  const upload::SpoolerDefinition spooler_definition(spooler, shash::kAny);
104
  UniquePtr<upload::AbstractUploader> uploader(
105
                       upload::AbstractUploader::Construct(spooler_definition));
106
107
  if (!uploader.IsValid()) {
108
    LogCvmfs(kLogCvmfs, kLogStderr, "failed to initialize spooler for '%s'",
109
             spooler.c_str());
110
    return 1;
111
  }
112
113
  UniquePtr<manifest::Reflog> reflog(CreateEmptyReflog(tmp_dir, repo_name));
114
  reflog->TakeDatabaseFileOwnership();
115
116
  reflog->BeginTransaction();
117
  AddStaticManifestObjects(reflog.weak_ref(), manifest.weak_ref());
118
  RootChainWalker walker(manifest.weak_ref(),
119
                         &object_fetcher,
120
                         reflog.weak_ref());
121
  walker.FindObjectsAndPopulateReflog();
122
  reflog->CommitTransaction();
123
124
  LogCvmfs(kLogCvmfs, kLogStdout, "found %d entries", reflog->CountEntries());
125
126
  reflog->DropDatabaseFileOwnership();
127
  const std::string reflog_db = reflog->database_file();
128
  reflog.Destroy();
129
  uploader->Upload(reflog_db, ".cvmfsreflog");
130
  shash::Any reflog_hash(manifest->GetHashAlgorithm());
131
  manifest::Reflog::HashDatabase(reflog_db, &reflog_hash);
132
  uploader->WaitForUpload();
133
  unlink(reflog_db.c_str());
134
135
  const int errors = uploader->GetNumberOfErrors();
136
  if (errors > 0) {
137
    LogCvmfs(kLogCvmfs, kLogStderr, "failed to upload generated Reflog");
138
  }
139
140
  uploader->TearDown();
141
142
  manifest::Reflog::WriteChecksum(reflog_chksum_path, reflog_hash);
143
144
  return (errors == 0) ? 0 : 1;
145
}
146
147
148
void CommandReconstructReflog::AddStaticManifestObjects(
149
                                          manifest::Reflog    *reflog,
150
                                          manifest::Manifest  *manifest) const {
151
  const shash::Any certificate = manifest->certificate();
152
  const shash::Any meta_info   = manifest->meta_info();
153
  assert(!certificate.IsNull());
154
155
  bool success = reflog->AddCertificate(certificate);
156
  assert(success);
157
  LogCvmfs(kLogCvmfs, kLogStdout, "Certificate: %s",
158
           certificate.ToString().c_str());
159
160
  if (!meta_info.IsNull()) {
161
    success = reflog->AddMetainfo(meta_info);
162
    assert(success);
163
    LogCvmfs(kLogCvmfs, kLogStdout, "Metainfo: %s",
164
             meta_info.ToString().c_str());
165
  }
166
}
167
168
169
void RootChainWalker::FindObjectsAndPopulateReflog() {
170
  const shash::Any root_catalog = manifest_->catalog_hash();
171
  const shash::Any history      = manifest_->history();
172
173
  assert(!root_catalog.IsNull());
174
  WalkRootCatalogs(root_catalog);
175
176
  if (!history.IsNull()) {
177
    WalkHistories(history);
178
  }
179
}
180
181
182
void RootChainWalker::WalkRootCatalogs(const shash::Any &root_catalog_hash) {
183
  shash::Any           current_hash = root_catalog_hash;
184
  UniquePtr<CatalogTN> current_catalog;
185
186
  while (!current_hash.IsNull()                  &&
187
         !reflog_->ContainsCatalog(current_hash) &&
188
         (current_catalog = FetchCatalog(current_hash)).IsValid()) {
189
    LogCvmfs(kLogCvmfs, kLogStdout, "Catalog: %s Revision: %d",
190
             current_hash.ToString().c_str(), current_catalog->GetRevision());
191
192
    const bool success = reflog_->AddCatalog(current_hash);
193
    assert(success);
194
195
    current_hash = current_catalog->GetPreviousRevision();
196
  }
197
}
198
199
200
void RootChainWalker::WalkHistories(const shash::Any &history_hash) {
201
  shash::Any           current_hash = history_hash;
202
  UniquePtr<HistoryTN> current_history;
203
204
  while (!current_hash.IsNull()                  &&
205
         !reflog_->ContainsHistory(current_hash) &&
206
         (current_history = FetchHistory(current_hash)).IsValid()) {
207
    LogCvmfs(kLogCvmfs, kLogStdout, "History: %s",
208
             current_hash.ToString().c_str());
209
210
    bool cancel = WalkCatalogsInHistory(current_history);
211
    const bool success = reflog_->AddHistory(current_hash);
212
    assert(success);
213
214
    if (cancel) {
215
      current_hash = shash::Any(current_hash.algorithm);
216
    } else {
217
      current_hash = current_history->previous_revision();
218
    }
219
  }
220
}
221
222
223
/**
224
 * If false is returned, it indicates that the history database comes from
225
 * an early pre-release of the history functionality. The WalkHistory()
226
 * iteration will subsequently stop. This is necessary, for instance, to
227
 * handle the cernvm-prod.cern.ch repository.
228
 */
229
bool RootChainWalker::WalkCatalogsInHistory(const HistoryTN *history) {
230
  CatalogList tag_hashes;
231
  const bool list_success = history->GetHashes(&tag_hashes);
232
  assert(list_success);
233
234
  CatalogList bin_hashes;
235
  const bool bin_success = history->ListRecycleBin(&bin_hashes);
236
  if (!bin_success) {
237
    LogCvmfs(kLogCvmfs, kLogStderr, "  Warning: 'recycle bin' table missing");
238
  }
239
240
  WalkListedCatalogs(tag_hashes);
241
  WalkListedCatalogs(bin_hashes);
242
243
  return !bin_success;
244
}
245
246
247
void RootChainWalker::WalkListedCatalogs(
248
                             const RootChainWalker::CatalogList &catalog_list) {
249
  CatalogList::const_iterator i    = catalog_list.begin();
250
  CatalogList::const_iterator iend = catalog_list.end();
251
  for (; i != iend; ++i) {
252
    WalkRootCatalogs(*i);
253
  }
254
}
255
256
257
RootChainWalker::CatalogTN* RootChainWalker::FetchCatalog(
258
                                                const shash::Any catalog_hash) {
259
  CatalogTN *catalog = NULL;
260
  const char *root_path = "";
261
  ObjectFetcherFailures::Failures failure =
262
    object_fetcher_->FetchCatalog(catalog_hash, root_path, &catalog);
263
264
  return ReturnOrAbort(failure, catalog_hash, catalog);
265
}
266
267
268
RootChainWalker::HistoryTN* RootChainWalker::FetchHistory(
269
                                                const shash::Any history_hash) {
270
  HistoryTN *history = NULL;
271
  ObjectFetcherFailures::Failures failure =
272
    object_fetcher_->FetchHistory(&history, history_hash);
273
274
  return ReturnOrAbort(failure, history_hash, history);
275
}
276
277
278
template <class DatabaseT>
279
DatabaseT* RootChainWalker::ReturnOrAbort(
280
                            const ObjectFetcherFailures::Failures  failure,
281
                            const shash::Any                      &content_hash,
282
                            DatabaseT                             *database) {
283
  switch (failure) {
284
    case ObjectFetcherFailures::kFailOk:
285
      return database;
286
    case ObjectFetcherFailures::kFailNotFound:
287
      return NULL;
288
    default:
289
      LogCvmfs(kLogCvmfs, kLogStderr, "Failed to load object '%s' (%d - %s)",
290
                                      content_hash.ToStringWithSuffix().c_str(),
291
                                      failure, Code2Ascii(failure));
292
      abort();
293
  }
294
}
295
296
}  // namespace swissknife