GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 "download.h" |
||
14 |
#include "history_sqlite.h" |
||
15 |
#include "manifest.h" |
||
16 |
#include "manifest_fetch.h" |
||
17 |
#include "reflog.h" |
||
18 |
#include "signature.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 |
175 |
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 |
4 |
inline const char* Code2Ascii(const ObjectFetcherFailures::Failures error) { |
|
51 |
const char *texts[ObjectFetcherFailures::kFailNumEntries + 1]; |
||
52 |
4 |
texts[0] = "OK"; |
|
53 |
4 |
texts[1] = "object not found"; |
|
54 |
4 |
texts[2] = "local I/O failure"; |
|
55 |
4 |
texts[3] = "network failure"; |
|
56 |
4 |
texts[4] = "decompression failed"; |
|
57 |
4 |
texts[5] = "manifest name doesn't match"; |
|
58 |
4 |
texts[6] = "manifest signature is invalid"; |
|
59 |
4 |
texts[7] = "bad data received"; |
|
60 |
4 |
texts[8] = "no text"; |
|
61 |
4 |
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 |
175 |
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 |
73 |
Failures FetchHistory(HistoryTN **history, |
|
116 |
const shash::Any &history_hash = shash::Any()) { |
||
117 |
// retrieve the current HEAD history hash (if nothing else given) |
||
118 |
shash::Any effective_history_hash = (!history_hash.IsNull()) |
||
119 |
? history_hash |
||
120 |
✓✓✓✓ ✓✓ |
73 |
: GetHistoryHash(); |
121 |
✗✓✗✓ ✗✓ |
73 |
if (effective_history_hash.IsNull()) { |
122 |
return kFailNotFound; |
||
123 |
} |
||
124 |
✓✓✗✓ ✓✓✗✓ ✓✓✗✓ |
73 |
assert(history_hash.suffix == shash::kSuffixHistory || |
125 |
history_hash.IsNull()); |
||
126 |
|||
127 |
// download the history hash |
||
128 |
73 |
std::string path; |
|
129 |
73 |
const Failures retval = Fetch(effective_history_hash, &path); |
|
130 |
✓✓✓✓ ✓✓ |
73 |
if (retval != kFailOk) { |
131 |
9 |
return retval; |
|
132 |
} |
||
133 |
|||
134 |
// open the history file |
||
135 |
64 |
*history = HistoryTN::Open(path); |
|
136 |
✓✓✗✓ ✗✓ |
64 |
if (NULL == *history) { |
137 |
1 |
return kFailLocalIO; |
|
138 |
} |
||
139 |
|||
140 |
63 |
(*history)->TakeDatabaseFileOwnership(); |
|
141 |
63 |
return kFailOk; |
|
142 |
} |
||
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 |
6228 |
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 |
✗✓✗✓ ✗✓ |
6228 |
assert(!catalog_hash.IsNull()); |
161 |
✗✓✗✓ ✗✓ |
6228 |
assert(catalog_hash.suffix == shash::kSuffixCatalog); |
162 |
|||
163 |
6228 |
std::string path; |
|
164 |
6228 |
const Failures retval = Fetch(catalog_hash, &path); |
|
165 |
✓✓✓✓ ✓✓ |
6228 |
if (retval != kFailOk) { |
166 |
28 |
return retval; |
|
167 |
} |
||
168 |
|||
169 |
6200 |
*catalog = CatalogTN::AttachFreely(catalog_path, |
|
170 |
path, |
||
171 |
catalog_hash, |
||
172 |
parent, |
||
173 |
is_nested); |
||
174 |
✓✓✗✓ ✗✓ |
6200 |
if (NULL == *catalog) { |
175 |
1 |
return kFailLocalIO; |
|
176 |
} |
||
177 |
|||
178 |
6199 |
(*catalog)->TakeDatabaseFileOwnership(); |
|
179 |
6199 |
return kFailOk; |
|
180 |
} |
||
181 |
|||
182 |
9 |
Failures FetchReflog(const shash::Any &reflog_hash, ReflogTN **reflog) { |
|
183 |
✗✓✗✓ ✗✓ |
9 |
assert(!reflog_hash.IsNull()); |
184 |
✗✓✗✓ ✗✓ |
9 |
assert(reflog_hash.suffix == shash::kSuffixNone); |
185 |
|||
186 |
9 |
std::string tmp_path; |
|
187 |
9 |
const bool decompress = false; |
|
188 |
9 |
const bool nocache = true; |
|
189 |
9 |
Failures failure = Fetch(kReflogFilename, decompress, nocache, &tmp_path); |
|
190 |
✗✓✗✓ ✗✓ |
9 |
if (failure != kFailOk) { |
191 |
return failure; |
||
192 |
} |
||
193 |
|||
194 |
// Ensure data integrity |
||
195 |
9 |
shash::Any computed_hash(reflog_hash.algorithm); |
|
196 |
9 |
ReflogTN::HashDatabase(tmp_path, &computed_hash); |
|
197 |
✓✓✓✓ ✓✓ |
9 |
if (computed_hash != reflog_hash) { |
198 |
3 |
unlink(tmp_path.c_str()); |
|
199 |
3 |
return kFailBadData; |
|
200 |
} |
||
201 |
|||
202 |
6 |
*reflog = ReflogTN::Open(tmp_path); |
|
203 |
✗✓✗✓ ✗✓ |
6 |
if (NULL == *reflog) { |
204 |
return kFailLocalIO; |
||
205 |
} |
||
206 |
|||
207 |
6 |
(*reflog)->TakeDatabaseFileOwnership(); |
|
208 |
6 |
return kFailOk; |
|
209 |
} |
||
210 |
|||
211 |
166 |
Failures FetchManifest(UniquePtr<manifest::Manifest> *manifest) { |
|
212 |
166 |
manifest::Manifest *raw_manifest_ptr = NULL; |
|
213 |
166 |
Failures failure = FetchManifest(&raw_manifest_ptr); |
|
214 |
✓✗✓✗ ✓✗ |
166 |
if (failure == kFailOk) *manifest = raw_manifest_ptr; |
215 |
166 |
return failure; |
|
216 |
} |
||
217 |
|||
218 |
67 |
Failures FetchHistory(UniquePtr<HistoryTN> *history, |
|
219 |
const shash::Any &history_hash = shash::Any()) { |
||
220 |
67 |
HistoryTN *raw_history_ptr = NULL; |
|
221 |
67 |
Failures failure = FetchHistory(&raw_history_ptr, history_hash); |
|
222 |
✓✓✓✓ ✓✓ |
67 |
if (failure == kFailOk) *history = raw_history_ptr; |
223 |
67 |
return failure; |
|
224 |
} |
||
225 |
|||
226 |
11 |
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 |
11 |
CatalogTN *raw_catalog_ptr = NULL; |
|
232 |
Failures failure = FetchCatalog(catalog_hash, |
||
233 |
catalog_path, |
||
234 |
&raw_catalog_ptr, |
||
235 |
is_nested, |
||
236 |
11 |
parent); |
|
237 |
✓✓✓✓ ✓✓ |
11 |
if (failure == kFailOk) *catalog = raw_catalog_ptr; |
238 |
11 |
return failure; |
|
239 |
} |
||
240 |
|||
241 |
3 |
Failures FetchReflog(const shash::Any &reflog_hash, |
|
242 |
UniquePtr<ReflogTN> *reflog) |
||
243 |
{ |
||
244 |
3 |
ReflogTN *raw_reflog_ptr = NULL; |
|
245 |
3 |
Failures failure = FetchReflog(reflog_hash, &raw_reflog_ptr); |
|
246 |
✓✗✓✗ ✓✗ |
3 |
if (failure == kFailOk) *reflog = raw_reflog_ptr; |
247 |
3 |
return failure; |
|
248 |
} |
||
249 |
|||
250 |
std::string GetUrl(const shash::Any &hash) const { |
||
251 |
return static_cast<DerivedT*>(this)->GetUrl(hash); |
||
252 |
} |
||
253 |
|||
254 |
3 |
bool HasHistory() { |
|
255 |
3 |
shash::Any history_hash = GetHistoryHash(); |
|
256 |
3 |
return !history_hash.IsNull(); |
|
257 |
} |
||
258 |
|||
259 |
29 |
const std::string& temporary_directory() const { |
|
260 |
29 |
return temporary_directory_; |
|
261 |
} |
||
262 |
|||
263 |
protected: |
||
264 |
175 |
explicit AbstractObjectFetcher(const std::string &temp_dir) |
|
265 |
175 |
: 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 |
6301 |
Failures Fetch(const shash::Any &object_hash, std::string *file_path) { |
|
277 |
6301 |
return static_cast<DerivedT*>(this)->Fetch(object_hash, file_path); |
|
278 |
} |
||
279 |
|||
280 |
9 |
Failures Fetch(const std::string &relative_path, |
|
281 |
const bool decompress, |
||
282 |
const bool nocache, |
||
283 |
std::string *file_path) { |
||
284 |
return static_cast<DerivedT*>(this)->Fetch(relative_path, |
||
285 |
decompress, |
||
286 |
nocache, |
||
287 |
9 |
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 |
64 |
shash::Any GetHistoryHash() { |
|
297 |
64 |
UniquePtr<manifest::Manifest> manifest; |
|
298 |
64 |
const Failures retval = FetchManifest(&manifest); |
|
299 |
|||
300 |
✓✗✓✗ ✗✓✗✓ ✓✗✓✗ ✗✓✗✓ ✓✗✓✗ ✗✓✗✓ |
64 |
if (retval != kFailOk || |
301 |
!manifest.IsValid() || |
||
302 |
manifest->history().IsNull()) { |
||
303 |
return shash::Any(); |
||
304 |
} |
||
305 |
|||
306 |
64 |
return manifest->history(); |
|
307 |
} |
||
308 |
|||
309 |
private: |
||
310 |
const std::string temporary_directory_; |
||
311 |
}; |
||
312 |
|||
313 |
template <class DerivedT> |
||
314 |
✓✗✓✗ ✗ |
30 |
const std::string AbstractObjectFetcher<DerivedT>::kManifestFilename = |
315 |
".cvmfspublished"; |
||
316 |
template <class DerivedT> |
||
317 |
✓✓✓✗ ✓✗ |
60 |
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 |
10 |
{ |
|
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 |
10 |
, base_path_(base_path) {} |
|
350 |
|||
351 |
using BaseTN::FetchManifest; // un-hiding convenience overload |
||
352 |
7 |
Failures FetchManifest(manifest::Manifest** manifest) { |
|
353 |
7 |
const std::string path = BuildPath(BaseTN::kManifestFilename); |
|
354 |
✗✓ | 7 |
if (!FileExists(path)) { |
355 |
return BaseTN::kFailNotFound; |
||
356 |
} |
||
357 |
|||
358 |
7 |
*manifest = manifest::Manifest::LoadFile(path); |
|
359 |
return (*manifest != NULL) ? BaseTN::kFailOk |
||
360 |
✓✗ | 7 |
: BaseTN::kFailUnknown; |
361 |
} |
||
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 |
✗✓ | 13 |
assert(file_path != NULL); |
369 |
13 |
file_path->clear(); |
|
370 |
|||
371 |
13 |
const std::string relative_path = BuildRelativePath(object_hash); |
|
372 |
13 |
const bool decompress = true; |
|
373 |
13 |
const bool nocache = false; |
|
374 |
13 |
return Fetch(relative_path, decompress, nocache, file_path); |
|
375 |
} |
||
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 |
✗✓ | 16 |
assert(file_path != NULL); |
383 |
16 |
file_path->clear(); |
|
384 |
|||
385 |
// check if the requested file object is available locally |
||
386 |
16 |
const std::string source = BuildPath(relative_path); |
|
387 |
✓✓ | 16 |
if (!FileExists(source)) { |
388 |
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 |
const std::string tmp_path = BaseTN::temporary_directory() + "/" + |
||
395 |
13 |
GetFileName(relative_path); |
|
396 |
13 |
FILE *f = CreateTempFile(tmp_path, 0600, "w", file_path); |
|
397 |
✗✓ | 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 |
const bool success = (decompress) |
||
406 |
? zlib::DecompressPath2File(source, f) |
||
407 |
✓✓ | 13 |
: CopyPath2File(source, f); |
408 |
13 |
fclose(f); |
|
409 |
|||
410 |
// check the decompression success and remove the temporary file otherwise |
||
411 |
✓✓ | 13 |
if (!success) { |
412 |
2 |
LogCvmfs(kLogDownload, kLogDebug, "failed to fetch file from '%s' " |
|
413 |
"to '%s' (errno: %d)", |
||
414 |
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 |
} |
||
422 |
|||
423 |
protected: |
||
424 |
23 |
std::string BuildPath(const std::string &relative_path) const { |
|
425 |
23 |
return base_path_ + "/" + relative_path; |
|
426 |
} |
||
427 |
|||
428 |
13 |
std::string BuildRelativePath(const shash::Any &hash) const { |
|
429 |
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 |
10 |
{ |
|
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 |
, repo_url_(repo_url) |
||
482 |
, repo_name_(repo_name) |
||
483 |
, download_manager_(download_mgr) |
||
484 |
10 |
, signature_manager_(signature_mgr) {} |
|
485 |
|||
486 |
public: |
||
487 |
using BaseTN::FetchManifest; // un-hiding convenience overload |
||
488 |
7 |
Failures FetchManifest(manifest::Manifest** manifest) { |
|
489 |
7 |
const std::string url = BuildUrl(BaseTN::kManifestFilename); |
|
490 |
|||
491 |
// Download manifest file |
||
492 |
7 |
struct manifest::ManifestEnsemble manifest_ensemble; |
|
493 |
manifest::Failures retval = manifest::Fetch( |
||
494 |
repo_url_, |
||
495 |
repo_name_, |
||
496 |
0, |
||
497 |
NULL, |
||
498 |
signature_manager_, |
||
499 |
download_manager_, |
||
500 |
7 |
&manifest_ensemble); |
|
501 |
|||
502 |
// Check if manifest was loaded correctly |
||
503 |
✓✗✗✗ |
7 |
switch (retval) { |
504 |
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 |
✗✓ | 7 |
assert(retval == manifest::kFailOk); |
527 |
7 |
*manifest = new manifest::Manifest(*manifest_ensemble.manifest); |
|
528 |
return (*manifest != NULL) ? BaseTN::kFailOk |
||
529 |
✓✗ | 7 |
: BaseTN::kFailUnknown; |
530 |
} |
||
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 |
✗✓ | 13 |
assert(object_file != NULL); |
538 |
✗✓ | 13 |
assert(!object_hash.IsNull()); |
539 |
|||
540 |
13 |
const bool decompress = true; |
|
541 |
13 |
const bool nocache = false; |
|
542 |
13 |
const std::string url = BuildRelativeUrl(object_hash); |
|
543 |
13 |
return Download(url, decompress, nocache, &object_hash, object_file); |
|
544 |
} |
||
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 |
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 |
23 |
return repo_url_ + "/" + relative_path; |
|
558 |
} |
||
559 |
|||
560 |
13 |
std::string BuildRelativeUrl(const shash::Any &hash) const { |
|
561 |
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 |
const std::string tmp_path = BaseTN::temporary_directory() + "/" + |
||
573 |
16 |
GetFileName(relative_path); |
|
574 |
16 |
FILE *f = CreateTempFile(tmp_path, 0600, "w", file_path); |
|
575 |
✗✓ | 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 |
16 |
const std::string url = BuildUrl(relative_path); |
|
584 |
16 |
const bool probe_hosts = false; |
|
585 |
download::JobInfo download_job(&url, |
||
586 |
decompress, |
||
587 |
probe_hosts, |
||
588 |
f, |
||
589 |
16 |
expected_hash); |
|
590 |
16 |
download_job.force_nocache = nocache; |
|
591 |
16 |
download::Failures retval = download_manager_->Fetch(&download_job); |
|
592 |
16 |
const bool success = (retval == download::kFailOk); |
|
593 |
16 |
fclose(f); |
|
594 |
|||
595 |
// check if download worked and remove temporary file if not |
||
596 |
✓✓ | 16 |
if (!success) { |
597 |
5 |
LogCvmfs(kLogDownload, kLogDebug, "failed to download file " |
|
598 |
"%s to '%s' (%d - %s)", |
||
599 |
relative_path.c_str(), file_path->c_str(), |
||
600 |
retval, Code2Ascii(retval)); |
||
601 |
5 |
unlink(file_path->c_str()); |
|
602 |
5 |
file_path->clear(); |
|
603 |
|||
604 |
// hand out the error status |
||
605 |
✗✗✓✗ ✓ |
5 |
switch (retval) { |
606 |
case download::kFailLocalIO: |
||
607 |
return BaseTN::kFailLocalIO; |
||
608 |
|||
609 |
case download::kFailBadUrl: |
||
610 |
case download::kFailProxyResolve: |
||
611 |
case download::kFailHostResolve: |
||
612 |
case download::kFailUnsupportedProtocol: |
||
613 |
return BaseTN::kFailNetwork; |
||
614 |
|||
615 |
case download::kFailProxyHttp: |
||
616 |
case download::kFailHostHttp: |
||
617 |
return (download_job.http_code == 404) |
||
618 |
? BaseTN::kFailNotFound |
||
619 |
✗✓ | 2 |
: BaseTN::kFailNetwork; |
620 |
|||
621 |
case download::kFailBadData: |
||
622 |
case download::kFailTooBig: |
||
623 |
return BaseTN::kFailBadData; |
||
624 |
|||
625 |
default: |
||
626 |
✓✗✓✗ ✓✗ |
3 |
if (download::IsProxyTransferError(retval) || |
627 |
download::IsHostTransferError(retval)) { |
||
628 |
3 |
return BaseTN::kFailNetwork; |
|
629 |
} |
||
630 |
return BaseTN::kFailUnknown; |
||
631 |
} |
||
632 |
} |
||
633 |
|||
634 |
✗✓ | 11 |
assert(success); |
635 |
11 |
return BaseTN::kFailOk; |
|
636 |
} |
||
637 |
|||
638 |
private: |
||
639 |
const std::string repo_url_; |
||
640 |
const std::string repo_name_; |
||
641 |
download::DownloadManager *download_manager_; |
||
642 |
signature::SignatureManager *signature_manager_; |
||
643 |
}; |
||
644 |
|||
645 |
template <class CatalogT, class HistoryT, class ReflogT> |
||
646 |
struct object_fetcher_traits<HttpObjectFetcher<CatalogT, HistoryT, ReflogT> > { |
||
647 |
typedef CatalogT CatalogTN; |
||
648 |
typedef HistoryT HistoryTN; |
||
649 |
typedef ReflogT ReflogTN; |
||
650 |
}; |
||
651 |
|||
652 |
#endif // CVMFS_OBJECT_FETCHER_H_ |
Generated by: GCOVR (Version 4.1) |