GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/object_fetcher.h
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 207 236 87.7%
Branches: 121 237 51.1%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #ifndef CVMFS_OBJECT_FETCHER_H_
6 #define CVMFS_OBJECT_FETCHER_H_
7
8 #include <unistd.h>
9
10 #include <string>
11
12 #include "catalog.h"
13 #include "crypto/signature.h"
14 #include "history_sqlite.h"
15 #include "manifest.h"
16 #include "manifest_fetch.h"
17 #include "network/download.h"
18 #include "reflog.h"
19 #include "util/posix.h"
20
21 /**
22 * Trait class to define the concrete object types produced by the methods of
23 * concrete instantiations of AbstractObjectFetcher<>. For each implementation
24 * of AbstractObjectFetcher<> one needs to provide a specialisation of this
25 * trait. Note that this specialisation can be templated with the actual para-
26 * meters, hence the parameter space does not explode.
27 *
28 * See: http://stackoverflow.com/questions/6006614/
29 * c-static-polymorphism-crtp-and-using-typedefs-from-derived-classes
30 */
31 template <class ConcreteObjectFetcherT>
32 struct object_fetcher_traits;
33
34 struct ObjectFetcherFailures {
35 enum Failures {
36 kFailOk,
37 kFailNotFound,
38 kFailLocalIO,
39 kFailNetwork,
40 kFailDecompression,
41 kFailManifestNameMismatch,
42 kFailManifestSignatureMismatch,
43 kFailBadData,
44 kFailUnknown,
45
46 kFailNumEntries
47 };
48 };
49
50 2 inline const char* Code2Ascii(const ObjectFetcherFailures::Failures error) {
51 const char *texts[ObjectFetcherFailures::kFailNumEntries + 1];
52 2 texts[0] = "OK";
53 2 texts[1] = "object not found";
54 2 texts[2] = "local I/O failure";
55 2 texts[3] = "network failure";
56 2 texts[4] = "decompression failed";
57 2 texts[5] = "manifest name doesn't match";
58 2 texts[6] = "manifest signature is invalid";
59 2 texts[7] = "bad data received";
60 2 texts[8] = "no text";
61 2 return texts[error];
62 }
63
64 /**
65 * This is the default class implementing the data object fetching strategy. It
66 * is meant to be used when CVMFS specific data structures need to be downloaded
67 * from a backend storage of a repository.
68 *
69 * ObjectFetchers are supposed to be configured for one specific repository. How
70 * this is done depends on the concrete implementation of this base class. When
71 * a concrete implementation of ObjectFetcher<> needs to deal with files on the
72 * local file system it is obliged to take measures for proper cleanup of those
73 * files after usage.
74 *
75 * It abstracts all accesses to external file or HTTP resources and gathers this
76 * access logic in one central point. This also comes in handy when unit testing
77 * components that depend on downloading CVMFS data structures from a repository
78 * backend storage like CatalogTraversal<> or GarbageCollector<>.
79 */
80 template <class DerivedT>
81 class AbstractObjectFetcher : public ObjectFetcherFailures {
82 public:
83 typedef typename object_fetcher_traits<DerivedT>::CatalogTN CatalogTN;
84 typedef typename object_fetcher_traits<DerivedT>::HistoryTN HistoryTN;
85 typedef typename object_fetcher_traits<DerivedT>::ReflogTN ReflogTN;
86
87 typedef ObjectFetcherFailures::Failures Failures;
88
89 static const std::string kManifestFilename;
90 static const std::string kReflogFilename;
91
92 public:
93 /**
94 * Fetches and opens the manifest of the repository this object fetcher is
95 * configured for. Note that the user is responsible to clean up this object.
96 *
97 * @param manifest pointer to a manifest object pointer
98 * @return failure code, specifying the action's result
99 */
100 166 Failures FetchManifest(manifest::Manifest** manifest) {
101 166 return static_cast<DerivedT*>(this)->FetchManifest(manifest);
102 }
103
104 /**
105 * Downloads and opens (read-only) a history database. Note that the user is
106 * responsible to remove the history object after usage. The fetched SQLite
107 * database file will be unlinked automatically during the destruction of the
108 * HistoryTN object.
109 *
110 * @param history pointer to a history database object pointer
111 * @param history_hash (optional) the content hash of the history database
112 * if left blank, the latest one is downloaded
113 * @return failure code, specifying the action's result
114 */
115 92 Failures FetchHistory(HistoryTN **history,
116 const shash::Any &history_hash = shash::Any()) {
117 // retrieve the current HEAD history hash (if nothing else given)
118
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 66 times.
92 shash::Any effective_history_hash = (!history_hash.IsNull())
119 ? history_hash
120
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
72 : GetHistoryHash();
121
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
92 if (effective_history_hash.IsNull()) {
122 return kFailNotFound;
123 }
124
3/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 66 times.
92 assert(history_hash.suffix == shash::kSuffixHistory ||
125 history_hash.IsNull());
126
127 // download the history hash
128 92 std::string path;
129
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
92 const Failures retval = Fetch(effective_history_hash, &path);
130
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 71 times.
92 if (retval != kFailOk) {
131 11 return retval;
132 }
133
134 // open the history file
135
1/2
✓ Branch 1 taken 71 times.
✗ Branch 2 not taken.
81 *history = HistoryTN::Open(path);
136
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 70 times.
81 if (NULL == *history) {
137 1 return kFailLocalIO;
138 }
139
140
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
80 (*history)->TakeDatabaseFileOwnership();
141 80 return kFailOk;
142 92 }
143
144 /**
145 * Downloads and opens a catalog. Note that the user is responsible to remove
146 * the catalog object after usage.
147 *
148 * @param catalog_hash the content hash of the catalog object
149 * @param catalog_path the root_path the catalog is mounted on
150 * @param catalog pointer to the fetched catalog object pointer
151 * @param is_nested a hint if the catalog to be loaded is a nested one
152 * @param parent (optional) parent catalog of the requested catalog
153 * @return failure code, specifying the action's result
154 */
155 370133 Failures FetchCatalog(const shash::Any &catalog_hash,
156 const std::string &catalog_path,
157 CatalogTN **catalog,
158 const bool is_nested = false,
159 CatalogTN *parent = NULL) {
160
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 370120 times.
370133 assert(!catalog_hash.IsNull());
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 370120 times.
370132 assert(catalog_hash.suffix == shash::kSuffixCatalog);
162
163 370132 std::string path;
164
1/2
✓ Branch 1 taken 368333 times.
✗ Branch 2 not taken.
370134 const Failures retval = Fetch(catalog_hash, &path);
165
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 368310 times.
368345 if (retval != kFailOk) {
166 29 return retval;
167 }
168
169
1/2
✓ Branch 1 taken 369380 times.
✗ Branch 2 not taken.
368316 *catalog = CatalogTN::AttachFreely(catalog_path,
170 path,
171 catalog_hash,
172 parent,
173 is_nested);
174
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 369379 times.
369386 if (NULL == *catalog) {
175 1 return kFailLocalIO;
176 }
177
178
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
369385 (*catalog)->TakeDatabaseFileOwnership();
179 369383 return kFailOk;
180 369413 }
181
182 18 Failures FetchReflog(const shash::Any &reflog_hash, ReflogTN **reflog) {
183
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
18 assert(!reflog_hash.IsNull());
184
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
18 assert(reflog_hash.suffix == shash::kSuffixNone);
185
186 18 std::string tmp_path;
187 18 const bool decompress = false;
188 18 const bool nocache = true;
189
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 Failures failure = Fetch(kReflogFilename, decompress, nocache, &tmp_path);
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
18 if (failure != kFailOk) {
191 return failure;
192 }
193
194 // Ensure data integrity
195
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 shash::Any computed_hash(reflog_hash.algorithm);
196
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 ReflogTN::HashDatabase(tmp_path, &computed_hash);
197
2/4
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
18 if (computed_hash != reflog_hash) {
198 6 unlink(tmp_path.c_str());
199 6 return kFailBadData;
200 }
201
202
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 *reflog = ReflogTN::Open(tmp_path);
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 if (NULL == *reflog) {
204 return kFailLocalIO;
205 }
206
207
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
12 (*reflog)->TakeDatabaseFileOwnership();
208 12 return kFailOk;
209 18 }
210
211 166 Failures FetchManifest(UniquePtr<manifest::Manifest> *manifest) {
212 166 manifest::Manifest *raw_manifest_ptr = NULL;
213
1/2
✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
166 Failures failure = FetchManifest(&raw_manifest_ptr);
214
2/4
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 154 times.
✗ Branch 4 not taken.
166 if (failure == kFailOk) *manifest = raw_manifest_ptr;
215 166 return failure;
216 }
217
218 82 Failures FetchHistory(UniquePtr<HistoryTN> *history,
219 const shash::Any &history_hash = shash::Any()) {
220 82 HistoryTN *raw_history_ptr = NULL;
221
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
82 Failures failure = FetchHistory(&raw_history_ptr, history_hash);
222
3/4
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
82 if (failure == kFailOk) *history = raw_history_ptr;
223 82 return failure;
224 }
225
226 22 Failures FetchCatalog(const shash::Any &catalog_hash,
227 const std::string &catalog_path,
228 UniquePtr<CatalogTN> *catalog,
229 const bool is_nested = false,
230 CatalogTN *parent = NULL) {
231 22 CatalogTN *raw_catalog_ptr = NULL;
232
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
22 Failures failure = FetchCatalog(catalog_hash,
233 catalog_path,
234 &raw_catalog_ptr,
235 is_nested,
236 parent);
237
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
22 if (failure == kFailOk) *catalog = raw_catalog_ptr;
238 22 return failure;
239 }
240
241 6 Failures FetchReflog(const shash::Any &reflog_hash,
242 UniquePtr<ReflogTN> *reflog)
243 {
244 6 ReflogTN *raw_reflog_ptr = NULL;
245
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 Failures failure = FetchReflog(reflog_hash, &raw_reflog_ptr);
246
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
6 if (failure == kFailOk) *reflog = raw_reflog_ptr;
247 6 return failure;
248 }
249
250 std::string GetUrl(const shash::Any &hash) const {
251 return static_cast<DerivedT*>(this)->GetUrl(hash);
252 }
253
254 6 bool HasHistory() {
255
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 shash::Any history_hash = GetHistoryHash();
256 6 return !history_hash.IsNull();
257 }
258
259 58 const std::string& temporary_directory() const {
260 58 return temporary_directory_;
261 }
262
263 protected:
264 147 explicit AbstractObjectFetcher(const std::string &temp_dir)
265 147 : temporary_directory_(temp_dir) {}
266
267 /**
268 * Internal function used to download objects defined by the given content
269 * hash. This needs to be implemented depending on the concrete implementation
270 * of this base class.
271 *
272 * @param object_hash the content hash of the object to be downloaded
273 * @param file_path temporary file path to store the download result
274 * @return failure code (if not kFailOk, file_path is invalid)
275 */
276 370224 Failures Fetch(const shash::Any &object_hash, std::string *file_path) {
277 370224 return static_cast<DerivedT*>(this)->Fetch(object_hash, file_path);
278 }
279
280 18 Failures Fetch(const std::string &relative_path,
281 const bool decompress,
282 const bool nocache,
283 std::string *file_path) {
284 18 return static_cast<DerivedT*>(this)->Fetch(relative_path,
285 decompress,
286 nocache,
287 18 file_path);
288 }
289
290 /**
291 * Retrieves the history content hash of the HEAD history database from the
292 * repository's manifest
293 *
294 * @return the content hash of the HEAD history db or a null-hash on error
295 */
296 77 shash::Any GetHistoryHash() {
297
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
77 UniquePtr<manifest::Manifest> manifest;
298
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
77 const Failures retval = FetchManifest(&manifest);
299
300 154 if (retval != kFailOk ||
301
3/6
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 69 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 69 times.
154 !manifest.IsValid() ||
302
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 69 times.
154 manifest->history().IsNull()) {
303 return shash::Any();
304 }
305
306 77 return manifest->history();
307 77 }
308
309 private:
310 const std::string temporary_directory_;
311 };
312
313 template <class DerivedT>
314 const std::string AbstractObjectFetcher<DerivedT>::kManifestFilename =
315 ".cvmfspublished";
316 template <class DerivedT>
317 const std::string AbstractObjectFetcher<DerivedT>::kReflogFilename =
318 ".cvmfsreflog";
319
320
321 /**
322 * This is an AbstractObjectFetcher<> accessing locally stored repository files.
323 * Note that this implementation does not take care of any repository signature
324 * verification.
325 */
326 template <class CatalogT = catalog::Catalog,
327 class HistoryT = history::SqliteHistory,
328 class ReflogT = manifest::Reflog>
329 class LocalObjectFetcher :
330 public AbstractObjectFetcher<LocalObjectFetcher<CatalogT, HistoryT, ReflogT> >
331 {
332 protected:
333 typedef LocalObjectFetcher<CatalogT, HistoryT, ReflogT> ThisTN;
334 typedef AbstractObjectFetcher<ThisTN> BaseTN;
335
336 public:
337 typedef typename BaseTN::Failures Failures;
338
339 public:
340 /**
341 * LocalObjectFetcher can reside on the stack or the heap.
342 *
343 * @param base_path the path to the repository's backend storage
344 * @param temp_dir location to store decompressed tmp data
345 */
346 10 LocalObjectFetcher(const std::string &base_path,
347 const std::string &temp_dir)
348 : BaseTN(temp_dir)
349
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 , base_path_(base_path) {}
350
351 using BaseTN::FetchManifest; // un-hiding convenience overload
352 7 Failures FetchManifest(manifest::Manifest** manifest) {
353
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 const std::string path = BuildPath(BaseTN::kManifestFilename);
354
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 if (!FileExists(path)) {
355 return BaseTN::kFailNotFound;
356 }
357
358
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 *manifest = manifest::Manifest::LoadFile(path);
359
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 return (*manifest != NULL) ? BaseTN::kFailOk
360 7 : BaseTN::kFailUnknown;
361 7 }
362
363 std::string GetUrl(const shash::Any &hash) const {
364 return "file://" + BuildPath(BuildRelativePath(hash));
365 }
366
367 13 Failures Fetch(const shash::Any &object_hash, std::string *file_path) {
368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 assert(file_path != NULL);
369 13 file_path->clear();
370
371
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 const std::string relative_path = BuildRelativePath(object_hash);
372 13 const bool decompress = true;
373 13 const bool nocache = false;
374
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
26 return Fetch(relative_path, decompress, nocache, file_path);
375 13 }
376
377
378 16 Failures Fetch(const std::string &relative_path,
379 const bool decompress,
380 const bool /* nocache */,
381 std::string *file_path) {
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 assert(file_path != NULL);
383 16 file_path->clear();
384
385 // check if the requested file object is available locally
386
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const std::string source = BuildPath(relative_path);
387
3/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 13 times.
16 if (!FileExists(source)) {
388
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 LogCvmfs(kLogDownload, kLogDebug, "failed to locate file '%s'",
389 relative_path.c_str());
390 3 return BaseTN::kFailNotFound;
391 }
392
393 // create a temporary file to store the (decompressed) object file
394
3/6
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 13 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 13 times.
✗ Branch 9 not taken.
26 const std::string tmp_path = BaseTN::temporary_directory() + "/" +
395 GetFileName(relative_path);
396
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 FILE *f = CreateTempFile(tmp_path, 0600, "w", file_path);
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (NULL == f) {
398 LogCvmfs(kLogDownload, kLogStderr,
399 "failed to create temp file '%s' (errno: %d)",
400 tmp_path.c_str(), errno);
401 return BaseTN::kFailLocalIO;
402 }
403
404 // decompress or copy the requested object file
405 13 const bool success = (decompress)
406
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
13 ? zlib::DecompressPath2File(source, f)
407
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 : CopyPath2File(source, f);
408
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 fclose(f);
409
410 // check the decompression success and remove the temporary file otherwise
411
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
13 if (!success) {
412
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 LogCvmfs(kLogDownload, kLogDebug, "failed to fetch file from '%s' "
413 "to '%s' (errno: %d)",
414 2 source.c_str(), file_path->c_str(), errno);
415 2 unlink(file_path->c_str());
416 2 file_path->clear();
417 2 return BaseTN::kFailDecompression;
418 }
419
420 11 return BaseTN::kFailOk;
421 16 }
422
423 protected:
424 23 std::string BuildPath(const std::string &relative_path) const {
425
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 return base_path_ + "/" + relative_path;
426 }
427
428 13 std::string BuildRelativePath(const shash::Any &hash) const {
429
1/2
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 return "data/" + hash.MakePath();
430 }
431
432 private:
433 const std::string base_path_;
434 };
435
436 template <class CatalogT, class HistoryT, class ReflogT>
437 struct object_fetcher_traits<LocalObjectFetcher<CatalogT, HistoryT, ReflogT> > {
438 typedef CatalogT CatalogTN;
439 typedef HistoryT HistoryTN;
440 typedef ReflogT ReflogTN;
441 };
442
443
444 /**
445 * This implements the AbstractObjectFetcher<> to retrieve repository objects
446 * from a remote location through HTTP. It verifies the repository's signature
447 * and the downloaded data integrity.
448 */
449 template <class CatalogT = catalog::Catalog,
450 class HistoryT = history::SqliteHistory,
451 class ReflogT = manifest::Reflog>
452 class HttpObjectFetcher :
453 public AbstractObjectFetcher<HttpObjectFetcher<CatalogT, HistoryT, ReflogT> >
454 {
455 protected:
456 typedef HttpObjectFetcher<CatalogT, HistoryT, ReflogT> ThisTN;
457 typedef AbstractObjectFetcher<ThisTN> BaseTN;
458
459 public:
460 typedef typename BaseTN::Failures Failures;
461
462 public:
463 /**
464 * HttpObjectFetcher<> contains external DownloadManager and SignatureManager
465 * hence it essentially is a wrapper object and can be copied.
466 *
467 * @param repo_name the name of the repository to download objects from
468 * @param repo_url the URL to the repository's backend storage
469 * @param temp_dir location to store decompressed tmp data
470 * @param download_mgr pointer to the download manager to be used
471 * @param signature_mgr pointer to the signature manager to be used
472 *
473 * @return a HttpObjectFetcher<> object or NULL on error
474 */
475 10 HttpObjectFetcher(const std::string &repo_name,
476 const std::string &repo_url,
477 const std::string &temp_dir,
478 download::DownloadManager *download_mgr,
479 signature::SignatureManager *signature_mgr)
480 : BaseTN(temp_dir)
481
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 , repo_url_(repo_url)
482
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 , repo_name_(repo_name)
483 10 , download_manager_(download_mgr)
484 20 , signature_manager_(signature_mgr) {}
485
486 public:
487 using BaseTN::FetchManifest; // un-hiding convenience overload
488 7 Failures FetchManifest(manifest::Manifest** manifest) {
489
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 const std::string url = BuildUrl(BaseTN::kManifestFilename);
490
491 // Download manifest file
492 7 struct manifest::ManifestEnsemble manifest_ensemble;
493 14 manifest::Failures retval = manifest::Fetch(
494 7 repo_url_,
495
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 repo_name_,
496 0,
497 NULL,
498 signature_manager_,
499 download_manager_,
500 &manifest_ensemble);
501
502 // Check if manifest was loaded correctly
503
1/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7 switch (retval) {
504 7 case manifest::kFailOk:
505 7 break;
506
507 case manifest::kFailNameMismatch:
508 LogCvmfs(kLogDownload, kLogDebug,
509 "repository name mismatch. No name provided?");
510 return BaseTN::kFailManifestNameMismatch;
511
512 case manifest::kFailBadSignature:
513 case manifest::kFailBadCertificate:
514 case manifest::kFailBadWhitelist:
515 LogCvmfs(kLogDownload, kLogDebug,
516 "repository signature mismatch. No key(s) provided?");
517 return BaseTN::kFailManifestSignatureMismatch;
518
519 default:
520 LogCvmfs(kLogDownload, kLogDebug,
521 "failed to load manifest (%d - %s)",
522 retval, Code2Ascii(retval));
523 return BaseTN::kFailUnknown;
524 }
525
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 assert(retval == manifest::kFailOk);
527
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
7 *manifest = new manifest::Manifest(*manifest_ensemble.manifest);
528
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 return (*manifest != NULL) ? BaseTN::kFailOk
529 7 : BaseTN::kFailUnknown;
530 7 }
531
532 std::string GetUrl(const shash::Any &hash) const {
533 return BuildUrl(BuildRelativeUrl(hash));
534 }
535
536 13 Failures Fetch(const shash::Any &object_hash, std::string *object_file) {
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 assert(object_file != NULL);
538
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 assert(!object_hash.IsNull());
539
540 13 const bool decompress = true;
541 13 const bool nocache = false;
542
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 const std::string url = BuildRelativeUrl(object_hash);
543
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
26 return Download(url, decompress, nocache, &object_hash, object_file);
544 13 }
545
546 3 Failures Fetch(const std::string &relative_path,
547 const bool decompress,
548 const bool nocache,
549 std::string *file_path) {
550 3 const shash::Any *expected_hash = NULL;
551 3 return Download(relative_path, decompress, nocache, expected_hash,
552 3 file_path);
553 }
554
555 protected:
556 23 std::string BuildUrl(const std::string &relative_path) const {
557
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 return repo_url_ + "/" + relative_path;
558 }
559
560 13 std::string BuildRelativeUrl(const shash::Any &hash) const {
561
1/2
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 return "data/" + hash.MakePath();
562 }
563
564 16 Failures Download(const std::string &relative_path,
565 const bool decompress,
566 const bool nocache,
567 const shash::Any *expected_hash,
568 std::string *file_path) {
569 16 file_path->clear();
570
571 // create temporary file to host the fetching result
572
3/6
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 16 times.
✗ Branch 9 not taken.
32 const std::string tmp_path = BaseTN::temporary_directory() + "/" +
573 GetFileName(relative_path);
574
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 FILE *f = CreateTempFile(tmp_path, 0600, "w", file_path);
575
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (NULL == f) {
576 LogCvmfs(kLogDownload, kLogStderr,
577 "failed to create temp file '%s' (errno: %d)",
578 tmp_path.c_str(), errno);
579 return BaseTN::kFailLocalIO;
580 }
581
582 // fetch and decompress the requested object
583
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const std::string url = BuildUrl(relative_path);
584 16 const bool probe_hosts = false;
585 16 cvmfs::FileSink filesink(f);
586
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 download::JobInfo download_job(&url, decompress, probe_hosts, expected_hash,
587 &filesink);
588 16 download_job.SetForceNocache(nocache);
589
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 download::Failures retval = download_manager_->Fetch(&download_job);
590 16 const bool success = (retval == download::kFailOk);
591
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 fclose(f);
592
593 // check if download worked and remove temporary file if not
594
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 11 times.
16 if (!success) {
595
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 LogCvmfs(kLogDownload, kLogDebug, "failed to download file "
596 "%s to '%s' (%d - %s)",
597 relative_path.c_str(), file_path->c_str(),
598 retval, Code2Ascii(retval));
599 5 unlink(file_path->c_str());
600 5 file_path->clear();
601
602 // hand out the error status
603
2/5
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
5 switch (retval) {
604 case download::kFailLocalIO:
605 return BaseTN::kFailLocalIO;
606
607 case download::kFailBadUrl:
608 case download::kFailProxyResolve:
609 case download::kFailHostResolve:
610 case download::kFailUnsupportedProtocol:
611 LogCvmfs(kLogDownload, kLogDebug | kLogStderr,
612 "HTTP connection error %d: %s", retval, url.c_str());
613 return BaseTN::kFailNetwork;
614
615 2 case download::kFailProxyHttp:
616 case download::kFailHostHttp:
617
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (download_job.http_code() == 404)
618 return BaseTN::kFailNotFound;
619
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 LogCvmfs(kLogDownload, kLogDebug | kLogStderr,
620 "HTTP protocol error %d: %s (%d)",
621 download_job.http_code(), url.c_str(), retval);
622 2 return BaseTN::kFailNetwork;
623
624 case download::kFailBadData:
625 case download::kFailTooBig:
626 return BaseTN::kFailBadData;
627
628 3 default:
629
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
6 if (download::IsProxyTransferError(retval) ||
630 3 download::IsHostTransferError(retval))
631 {
632
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 LogCvmfs(kLogDownload, kLogDebug | kLogStderr,
633 "HTTP transfer error %d (HTTP code %d): %s",
634 retval, download_job.http_code(), url.c_str());
635 3 return BaseTN::kFailNetwork;
636 }
637 return BaseTN::kFailUnknown;
638 }
639 }
640
641
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 assert(success);
642 11 return BaseTN::kFailOk;
643 16 }
644
645 private:
646 const std::string repo_url_;
647 const std::string repo_name_;
648 download::DownloadManager *download_manager_;
649 signature::SignatureManager *signature_manager_;
650 };
651
652 template <class CatalogT, class HistoryT, class ReflogT>
653 struct object_fetcher_traits<HttpObjectFetcher<CatalogT, HistoryT, ReflogT> > {
654 typedef CatalogT CatalogTN;
655 typedef HistoryT HistoryTN;
656 typedef ReflogT ReflogTN;
657 };
658
659 #endif // CVMFS_OBJECT_FETCHER_H_
660