GCC Code Coverage Report


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