GCC Code Coverage Report


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