GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/object_fetcher.h
Date: 2026-10-11 02:40:02
Exec Total Coverage
Lines: 206 235 87.7%
Branches: 116 227 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 52 inline const char *Code2Ascii(const ObjectFetcherFailures::Failures error) {
52 const char *texts[ObjectFetcherFailures::kFailNumEntries + 1];
53 52 texts[0] = "OK";
54 52 texts[1] = "object not found";
55 52 texts[2] = "local I/O failure";
56 52 texts[3] = "network failure";
57 52 texts[4] = "decompression failed";
58 52 texts[5] = "manifest name doesn't match";
59 52 texts[6] = "manifest signature is invalid";
60 52 texts[7] = "bad data received";
61 52 texts[8] = "no text";
62 52 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 2286 Failures FetchManifest(manifest::Manifest **manifest) {
102 2286 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 1297 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 292 times.
✓ Branch 2 taken 746 times.
1297 shash::Any const effective_history_hash = (!history_hash.IsNull())
120 ? history_hash
121
1/2
✓ Branch 1 taken 746 times.
✗ Branch 2 not taken.
857 : GetHistoryHash();
122
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1038 times.
1297 if (effective_history_hash.IsNull()) {
123 ✗ return kFailNotFound;
124 }
125
3/4
✓ Branch 0 taken 746 times.
✓ Branch 1 taken 292 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 746 times.
1297 assert(history_hash.suffix == shash::kSuffixHistory
126 || history_hash.IsNull());
127
128 // download the history hash
129 1297 std::string path;
130
1/2
✓ Branch 1 taken 1038 times.
✗ Branch 2 not taken.
1297 const Failures retval = Fetch(effective_history_hash, &path);
131
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 876 times.
1297 if (retval != kFailOk) {
132 236 return retval;
133 }
134
135 // open the history file
136
1/2
✓ Branch 1 taken 876 times.
✗ Branch 2 not taken.
1061 *history = HistoryTN::Open(path);
137
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 840 times.
1061 if (NULL == *history) {
138 36 return kFailLocalIO;
139 }
140
141
1/2
✓ Branch 1 taken 840 times.
✗ Branch 2 not taken.
1025 (*history)->TakeDatabaseFileOwnership();
142 1025 return kFailOk;
143 1297 }
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 16207968 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 16207702 times.
16207968 assert(!catalog_hash.IsNull());
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16207702 times.
16207924 assert(catalog_hash.suffix == shash::kSuffixCatalog);
163
164 16207924 std::string path;
165
1/2
✓ Branch 1 taken 16140690 times.
✗ Branch 2 not taken.
16207924 const Failures retval = Fetch(catalog_hash, &path);
166
2/2
✓ Branch 0 taken 418 times.
✓ Branch 1 taken 16140272 times.
16140912 if (retval != kFailOk) {
167 529 return retval;
168 }
169
170
1/2
✓ Branch 1 taken 16150744 times.
✗ Branch 2 not taken.
16140383 *catalog = CatalogTN::AttachFreely(catalog_path, path, catalog_hash, parent,
171 is_nested);
172
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 16150708 times.
16150855 if (NULL == *catalog) {
173 36 return kFailLocalIO;
174 }
175
176
1/2
✓ Branch 1 taken 111 times.
✗ Branch 2 not taken.
16150819 (*catalog)->TakeDatabaseFileOwnership();
177 16149367 return kFailOk;
178 16149932 }
179
180 438 Failures FetchReflog(const shash::Any &reflog_hash, ReflogTN **reflog) {
181
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 219 times.
438 assert(!reflog_hash.IsNull());
182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 219 times.
438 assert(reflog_hash.suffix == shash::kSuffixNone);
183
184 438 std::string tmp_path;
185 438 const bool decompress = false;
186 438 const bool nocache = true;
187
1/2
✓ Branch 1 taken 219 times.
✗ Branch 2 not taken.
438 Failures const failure = Fetch(kReflogFilename, decompress, nocache,
188 &tmp_path);
189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 219 times.
438 if (failure != kFailOk) {
190 ✗ return failure;
191 }
192
193 // Ensure data integrity
194
1/2
✓ Branch 1 taken 219 times.
✗ Branch 2 not taken.
438 shash::Any computed_hash(reflog_hash.algorithm);
195
1/2
✓ Branch 1 taken 219 times.
✗ Branch 2 not taken.
438 ReflogTN::HashDatabase(tmp_path, &computed_hash);
196
2/4
✓ Branch 1 taken 73 times.
✓ Branch 2 taken 146 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
438 if (computed_hash != reflog_hash) {
197 146 unlink(tmp_path.c_str());
198 146 return kFailBadData;
199 }
200
201
1/2
✓ Branch 1 taken 146 times.
✗ Branch 2 not taken.
292 *reflog = ReflogTN::Open(tmp_path);
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 146 times.
292 if (NULL == *reflog) {
203 ✗ return kFailLocalIO;
204 }
205
206
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
292 (*reflog)->TakeDatabaseFileOwnership();
207 292 return kFailOk;
208 438 }
209
210 2286 Failures FetchManifest(std::unique_ptr<manifest::Manifest> *manifest) {
211 2286 manifest::Manifest *raw_manifest_ptr = NULL;
212
1/2
✓ Branch 1 taken 2064 times.
✗ Branch 2 not taken.
2286 Failures const failure = FetchManifest(&raw_manifest_ptr);
213
1/2
✓ Branch 0 taken 2064 times.
✗ Branch 1 not taken.
2286 if (failure == kFailOk)
214 2286 *manifest = std::unique_ptr<manifest::Manifest>(raw_manifest_ptr);
215 2286 return failure;
216 }
217
218 1077 Failures FetchHistory(std::unique_ptr<HistoryTN> *history,
219 const shash::Any &history_hash = shash::Any()) {
220 1077 HistoryTN *raw_history_ptr = NULL;
221
1/2
✓ Branch 1 taken 892 times.
✗ Branch 2 not taken.
1077 Failures const failure = FetchHistory(&raw_history_ptr, history_hash);
222
2/2
✓ Branch 0 taken 694 times.
✓ Branch 1 taken 198 times.
1077 if (failure == kFailOk)
223 805 *history = std::unique_ptr<HistoryTN>(raw_history_ptr);
224 1077 return failure;
225 }
226
227 512 Failures FetchCatalog(const shash::Any &catalog_hash,
228 const std::string &catalog_path,
229 std::unique_ptr<CatalogTN> *catalog,
230 const bool is_nested = false,
231 CatalogTN *parent = NULL) {
232 512 CatalogTN *raw_catalog_ptr = NULL;
233
1/2
✓ Branch 1 taken 256 times.
✗ Branch 2 not taken.
512 Failures failure = FetchCatalog(catalog_hash, catalog_path,
234 &raw_catalog_ptr, is_nested, parent);
235
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 146 times.
512 if (failure == kFailOk)
236 220 catalog->reset(raw_catalog_ptr);
237 512 return failure;
238 }
239
240 146 Failures FetchReflog(const shash::Any &reflog_hash,
241 std::unique_ptr<ReflogTN> *reflog) {
242 146 ReflogTN *raw_reflog_ptr = NULL;
243
1/2
✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
146 Failures failure = FetchReflog(reflog_hash, &raw_reflog_ptr);
244
1/2
✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
146 if (failure == kFailOk)
245 146 reflog->reset(raw_reflog_ptr);
246 146 return failure;
247 }
248
249 std::string GetUrl(const shash::Any &hash) const {
250 return static_cast<DerivedT *>(this)->GetUrl(hash);
251 }
252
253 146 bool HasHistory() {
254
1/2
✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
146 shash::Any history_hash = GetHistoryHash();
255 146 return !history_hash.IsNull();
256 }
257
258 1178 const std::string &temporary_directory() const {
259 1178 return temporary_directory_;
260 }
261
262 protected:
263 2982 explicit AbstractObjectFetcher(const std::string &temp_dir)
264 2982 : temporary_directory_(temp_dir) { }
265
266 /**
267 * Internal function used to download objects defined by the given content
268 * hash. This needs to be implemented depending on the concrete implementation
269 * of this base class.
270 *
271 * @param object_hash the content hash of the object to be downloaded
272 * @param file_path temporary file path to store the download result
273 * @return failure code (if not kFailOk, file_path is invalid)
274 */
275 16209089 Failures Fetch(const shash::Any &object_hash, std::string *file_path) {
276 16209089 return static_cast<DerivedT *>(this)->Fetch(object_hash, file_path);
277 }
278
279 438 Failures Fetch(const std::string &relative_path,
280 const bool decompress,
281 const bool nocache,
282 std::string *file_path) {
283 438 return static_cast<DerivedT *>(this)->Fetch(relative_path, decompress,
284 438 nocache, file_path);
285 }
286
287 /**
288 * Retrieves the history content hash of the HEAD history database from the
289 * repository's manifest
290 *
291 * @return the content hash of the HEAD history db or a null-hash on error
292 */
293 967 shash::Any GetHistoryHash() {
294 967 std::unique_ptr<manifest::Manifest> manifest;
295
1/2
✓ Branch 1 taken 819 times.
✗ Branch 2 not taken.
967 const Failures retval = FetchManifest(&manifest);
296
297
1/2
✓ Branch 1 taken 819 times.
✗ Branch 2 not taken.
967 if (retval != kFailOk || manifest.get() == nullptr
298
3/6
✓ Branch 0 taken 819 times.
✗ Branch 1 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 819 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 819 times.
1934 || manifest->history().IsNull()) {
299 ✗ return shash::Any();
300 }
301
302 967 return manifest->history();
303 967 }
304
305 private:
306 const std::string temporary_directory_;
307 };
308
309 template<class DerivedT>
310 const std::string
311 AbstractObjectFetcher<DerivedT>::kManifestFilename = ".cvmfspublished";
312 template<class DerivedT>
313 const std::string
314 AbstractObjectFetcher<DerivedT>::kReflogFilename = ".cvmfsreflog";
315
316
317 /**
318 * This is an AbstractObjectFetcher<> accessing locally stored repository files.
319 * Note that this implementation does not take care of any repository signature
320 * verification.
321 */
322 template<class CatalogT = catalog::Catalog,
323 class HistoryT = history::SqliteHistory,
324 class ReflogT = manifest::Reflog>
325 class LocalObjectFetcher
326 : public AbstractObjectFetcher<
327 LocalObjectFetcher<CatalogT, HistoryT, ReflogT> > {
328 protected:
329 typedef LocalObjectFetcher<CatalogT, HistoryT, ReflogT> ThisTN;
330 typedef AbstractObjectFetcher<ThisTN> BaseTN;
331
332 public:
333 typedef typename BaseTN::Failures Failures;
334
335 public:
336 /**
337 * LocalObjectFetcher can reside on the stack or the heap.
338 *
339 * @param base_path the path to the repository's backend storage
340 * @param temp_dir location to store decompressed tmp data
341 */
342 10 LocalObjectFetcher(const std::string &base_path, const std::string &temp_dir)
343
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 : BaseTN(temp_dir), base_path_(base_path) { }
344
345 using BaseTN::FetchManifest; // un-hiding convenience overload
346 7 Failures FetchManifest(manifest::Manifest **manifest) {
347
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 const std::string path = BuildPath(BaseTN::kManifestFilename);
348
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 if (!FileExists(path)) {
349 ✗ return BaseTN::kFailNotFound;
350 }
351
352
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 *manifest = manifest::Manifest::LoadFile(path);
353
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 return (*manifest != NULL) ? BaseTN::kFailOk : BaseTN::kFailUnknown;
354 7 }
355
356 std::string GetUrl(const shash::Any &hash) const {
357 return "file://" + BuildPath(BuildRelativePath(hash));
358 }
359
360 13 Failures Fetch(const shash::Any &object_hash, std::string *file_path) {
361
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 assert(file_path != NULL);
362 13 file_path->clear();
363
364
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 const std::string relative_path = BuildRelativePath(object_hash);
365 13 const bool decompress = true;
366 13 const bool nocache = false;
367
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
26 return Fetch(relative_path, decompress, nocache, file_path);
368 13 }
369
370
371 16 Failures Fetch(const std::string &relative_path,
372 const bool decompress,
373 const bool /* nocache */,
374 std::string *file_path) {
375
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 assert(file_path != NULL);
376 16 file_path->clear();
377
378 // check if the requested file object is available locally
379
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const std::string source = BuildPath(relative_path);
380
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)) {
381
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 LogCvmfs(kLogDownload, kLogDebug, "failed to locate file '%s'",
382 relative_path.c_str());
383 3 return BaseTN::kFailNotFound;
384 }
385
386 // create a temporary file to store the (decompressed) object file
387
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() + "/"
388 + GetFileName(relative_path);
389
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 FILE *f = CreateTempFile(tmp_path, 0600, "w", file_path);
390
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (NULL == f) {
391 ✗ LogCvmfs(kLogDownload, kLogStderr,
392 "failed to create temp file '%s' (errno: %d)", tmp_path.c_str(),
393 ✗ errno);
394 ✗ return BaseTN::kFailLocalIO;
395 }
396
397 // decompress or copy the requested object file
398
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
13 const bool success = (decompress) ? zlib::DecompressPath2File(source, f)
399
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 : CopyPath2File(source, f);
400
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 fclose(f);
401
402 // check the decompression success and remove the temporary file otherwise
403
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
13 if (!success) {
404
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 LogCvmfs(kLogDownload, kLogDebug,
405 "failed to fetch file from '%s' "
406 "to '%s' (errno: %d)",
407 2 source.c_str(), file_path->c_str(), errno);
408 2 unlink(file_path->c_str());
409 2 file_path->clear();
410 2 return BaseTN::kFailDecompression;
411 }
412
413 11 return BaseTN::kFailOk;
414 16 }
415
416 protected:
417 23 std::string BuildPath(const std::string &relative_path) const {
418
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 return base_path_ + "/" + relative_path;
419 }
420
421 13 std::string BuildRelativePath(const shash::Any &hash) const {
422
1/2
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 return "data/" + hash.MakePath();
423 }
424
425 private:
426 const std::string base_path_;
427 };
428
429 template<class CatalogT, class HistoryT, class ReflogT>
430 struct object_fetcher_traits<LocalObjectFetcher<CatalogT, HistoryT, ReflogT> > {
431 typedef CatalogT CatalogTN;
432 typedef HistoryT HistoryTN;
433 typedef ReflogT ReflogTN;
434 };
435
436
437 /**
438 * This implements the AbstractObjectFetcher<> to retrieve repository objects
439 * from a remote location through HTTP. It verifies the repository's signature
440 * and the downloaded data integrity.
441 */
442 template<class CatalogT = catalog::Catalog,
443 class HistoryT = history::SqliteHistory,
444 class ReflogT = manifest::Reflog>
445 class HttpObjectFetcher : public AbstractObjectFetcher<
446 HttpObjectFetcher<CatalogT, HistoryT, ReflogT> > {
447 protected:
448 typedef HttpObjectFetcher<CatalogT, HistoryT, ReflogT> ThisTN;
449 typedef AbstractObjectFetcher<ThisTN> BaseTN;
450
451 public:
452 typedef typename BaseTN::Failures Failures;
453
454 public:
455 /**
456 * HttpObjectFetcher<> contains external DownloadManager and SignatureManager
457 * hence it essentially is a wrapper object and can be copied.
458 *
459 * @param repo_name the name of the repository to download objects from
460 * @param repo_url the URL to the repository's backend storage
461 * @param temp_dir location to store decompressed tmp data
462 * @param download_mgr pointer to the download manager to be used
463 * @param signature_mgr pointer to the signature manager to be used
464 *
465 * @return a HttpObjectFetcher<> object or NULL on error
466 */
467 360 HttpObjectFetcher(const std::string &repo_name,
468 const std::string &repo_url,
469 const std::string &temp_dir,
470 download::DownloadManager *download_mgr,
471 signature::SignatureManager *signature_mgr)
472 : BaseTN(temp_dir)
473
1/2
✓ Branch 1 taken 360 times.
✗ Branch 2 not taken.
360 , repo_url_(repo_url)
474
1/2
✓ Branch 1 taken 360 times.
✗ Branch 2 not taken.
360 , repo_name_(repo_name)
475 360 , download_manager_(download_mgr)
476 720 , signature_manager_(signature_mgr) { }
477
478 public:
479 using BaseTN::FetchManifest; // un-hiding convenience overload
480 252 Failures FetchManifest(manifest::Manifest **manifest) {
481
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 const std::string url = BuildUrl(BaseTN::kManifestFilename);
482
483 // Download manifest file
484 252 struct manifest::ManifestEnsemble manifest_ensemble;
485 504 manifest::Failures const retval = manifest::Fetch(repo_url_,
486
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 repo_name_,
487 0,
488 NULL,
489 signature_manager_,
490 download_manager_,
491 &manifest_ensemble);
492
493 // Check if manifest was loaded correctly
494
1/4
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
252 switch (retval) {
495 252 case manifest::kFailOk:
496 252 break;
497
498 ✗ case manifest::kFailNameMismatch:
499 ✗ LogCvmfs(kLogDownload, kLogDebug,
500 "repository name mismatch. No name provided?");
501 ✗ return BaseTN::kFailManifestNameMismatch;
502
503 ✗ case manifest::kFailBadSignature:
504 case manifest::kFailBadCertificate:
505 case manifest::kFailBadWhitelist:
506 ✗ LogCvmfs(kLogDownload, kLogDebug,
507 "repository signature mismatch. No key(s) provided?");
508 ✗ return BaseTN::kFailManifestSignatureMismatch;
509
510 ✗ default:
511 ✗ LogCvmfs(kLogDownload, kLogDebug, "failed to load manifest (%d - %s)",
512 retval, Code2Ascii(retval));
513 ✗ return BaseTN::kFailUnknown;
514 }
515
516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252 assert(retval == manifest::kFailOk);
517
2/4
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 252 times.
✗ Branch 5 not taken.
252 *manifest = new manifest::Manifest(*manifest_ensemble.manifest);
518
1/2
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
252 return (*manifest != NULL) ? BaseTN::kFailOk : BaseTN::kFailUnknown;
519 252 }
520
521 std::string GetUrl(const shash::Any &hash) const {
522 return BuildUrl(BuildRelativeUrl(hash));
523 }
524
525 468 Failures Fetch(const shash::Any &object_hash, std::string *object_file) {
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 468 times.
468 assert(object_file != NULL);
527
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 468 times.
468 assert(!object_hash.IsNull());
528
529 468 const bool decompress = true;
530 468 const bool nocache = false;
531
1/2
✓ Branch 1 taken 468 times.
✗ Branch 2 not taken.
468 const std::string url = BuildRelativeUrl(object_hash);
532
1/2
✓ Branch 1 taken 468 times.
✗ Branch 2 not taken.
936 return Download(url, decompress, nocache, &object_hash, object_file);
533 468 }
534
535 108 Failures Fetch(const std::string &relative_path,
536 const bool decompress,
537 const bool nocache,
538 std::string *file_path) {
539 108 const shash::Any *expected_hash = NULL;
540 108 return Download(relative_path, decompress, nocache, expected_hash,
541 108 file_path);
542 }
543
544 protected:
545 828 std::string BuildUrl(const std::string &relative_path) const {
546
1/2
✓ Branch 2 taken 828 times.
✗ Branch 3 not taken.
828 return repo_url_ + "/" + relative_path;
547 }
548
549 468 std::string BuildRelativeUrl(const shash::Any &hash) const {
550
1/2
✓ Branch 2 taken 468 times.
✗ Branch 3 not taken.
468 return "data/" + hash.MakePath();
551 }
552
553 576 Failures Download(const std::string &relative_path,
554 const bool decompress,
555 const bool nocache,
556 const shash::Any *expected_hash,
557 std::string *file_path) {
558 576 file_path->clear();
559
560 // create temporary file to host the fetching result
561
3/6
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 576 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 576 times.
✗ Branch 9 not taken.
1152 const std::string tmp_path = BaseTN::temporary_directory() + "/"
562 + GetFileName(relative_path);
563
1/2
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
576 FILE *f = CreateTempFile(tmp_path, 0600, "w", file_path);
564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 576 times.
576 if (NULL == f) {
565 ✗ LogCvmfs(kLogDownload, kLogStderr,
566 "failed to create temp file '%s' (errno: %d)", tmp_path.c_str(),
567 ✗ errno);
568 ✗ return BaseTN::kFailLocalIO;
569 }
570
571 // fetch and decompress the requested object
572
1/2
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
576 const std::string url = BuildUrl(relative_path);
573 576 const bool probe_hosts = false;
574 576 cvmfs::FileSink filesink(f);
575
1/2
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
576 download::JobInfo download_job(&url, decompress, probe_hosts, expected_hash,
576 &filesink);
577 576 download_job.SetForceNocache(nocache);
578
1/2
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
576 download::Failures const retval = download_manager_->Fetch(&download_job);
579 576 const bool success = (retval == download::kFailOk);
580
1/2
✓ Branch 1 taken 576 times.
✗ Branch 2 not taken.
576 fclose(f);
581
582 // check if download worked and remove temporary file if not
583
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 396 times.
576 if (!success) {
584
1/2
✓ Branch 4 taken 180 times.
✗ Branch 5 not taken.
180 LogCvmfs(kLogDownload, kLogDebug,
585 "failed to download file "
586 "%s to '%s' (%d - %s)",
587 relative_path.c_str(), file_path->c_str(), retval,
588 Code2Ascii(retval));
589 180 unlink(file_path->c_str());
590 180 file_path->clear();
591
592 // hand out the error status
593
2/5
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 108 times.
180 switch (retval) {
594 ✗ case download::kFailLocalIO:
595 ✗ return BaseTN::kFailLocalIO;
596
597 ✗ case download::kFailBadUrl:
598 case download::kFailProxyResolve:
599 case download::kFailHostResolve:
600 case download::kFailUnsupportedProtocol:
601 ✗ LogCvmfs(kLogDownload, kLogDebug | kLogStderr,
602 "HTTP connection error %d: %s", retval, url.c_str());
603 ✗ return BaseTN::kFailNetwork;
604
605 72 case download::kFailProxyHttp:
606 case download::kFailHostHttp:
607
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
72 if (download_job.http_code() == 404)
608 ✗ return BaseTN::kFailNotFound;
609
1/2
✓ Branch 3 taken 72 times.
✗ Branch 4 not taken.
72 LogCvmfs(kLogDownload, kLogDebug | kLogStderr,
610 "HTTP protocol error %d: %s (%d)", download_job.http_code(),
611 url.c_str(), retval);
612 72 return BaseTN::kFailNetwork;
613
614 ✗ case download::kFailBadData:
615 case download::kFailTooBig:
616 ✗ return BaseTN::kFailBadData;
617
618 108 default:
619 108 if (download::IsProxyTransferError(retval)
620
3/6
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 108 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 108 times.
✗ Branch 6 not taken.
108 || download::IsHostTransferError(retval)) {
621
1/2
✓ Branch 3 taken 108 times.
✗ Branch 4 not taken.
108 LogCvmfs(kLogDownload, kLogDebug | kLogStderr,
622 "HTTP transfer error %d (HTTP code %d): %s", retval,
623 download_job.http_code(), url.c_str());
624 108 return BaseTN::kFailNetwork;
625 }
626 ✗ return BaseTN::kFailUnknown;
627 }
628 }
629
630
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 396 times.
396 assert(success);
631 396 return BaseTN::kFailOk;
632 576 }
633
634 private:
635 const std::string repo_url_;
636 const std::string repo_name_;
637 download::DownloadManager *download_manager_;
638 signature::SignatureManager *signature_manager_;
639 };
640
641 template<class CatalogT, class HistoryT, class ReflogT>
642 struct object_fetcher_traits<HttpObjectFetcher<CatalogT, HistoryT, ReflogT> > {
643 typedef CatalogT CatalogTN;
644 typedef HistoryT HistoryTN;
645 typedef ReflogT ReflogTN;
646 };
647
648 #endif // CVMFS_OBJECT_FETCHER_H_
649