| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/file_bundle.h |
| Date: | 2026-06-21 02:37:04 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 40 | 41 | 97.6% |
| Branches: | 27 | 52 | 51.9% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | * | ||
| 4 | * This class implements the format for .cvmfsbundle files | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef CVMFS_FILE_BUNDLE_H_ | ||
| 8 | #define CVMFS_FILE_BUNDLE_H_ | ||
| 9 | |||
| 10 | #include <cassert> | ||
| 11 | #include <fstream> | ||
| 12 | #include <sstream> | ||
| 13 | #include <string> | ||
| 14 | |||
| 15 | #include "json_document.h" | ||
| 16 | #include "shortstring.h" | ||
| 17 | #include "util/single_copy.h" | ||
| 18 | |||
| 19 | /* | ||
| 20 | |||
| 21 | The .cvmfsbundle file serves both as a file list and as a trigger for loading a | ||
| 22 | bundle. The convention is to call it .cvmfsbundle.<filename>, where <filename> | ||
| 23 | should trigger the bundle. | ||
| 24 | |||
| 25 | ? The content could be structured in json. | ||
| 26 | |||
| 27 | The file format should be versioned, with the header: | ||
| 28 | |||
| 29 | #%CVMFS_BUNDLE version=1 encoding=UTF-8 | ||
| 30 | |||
| 31 | ? end marker | ||
| 32 | |||
| 33 | The json file should contain an array of labeled objects as follows: | ||
| 34 | { | ||
| 35 | "name":"CVMFS_BUNDLE", | ||
| 36 | "version":"1.0.0", | ||
| 37 | "encoding":"UTF-8", | ||
| 38 | "dependencies":["/absolute/path/to/file/from/repositories/root"] | ||
| 39 | } | ||
| 40 | |||
| 41 | */ | ||
| 42 | |||
| 43 | class BundleFileMgr : SingleCopy { | ||
| 44 | public: | ||
| 45 | BundleFileMgr(const PathString &bundle_file_path) { | ||
| 46 | std::ifstream file(bundle_file_path.ToString()); | ||
| 47 | if (!file.is_open()) { | ||
| 48 | bundle_doc_ = nullptr; | ||
| 49 | } else { | ||
| 50 | std::stringstream rbuf; | ||
| 51 | rbuf << file.rdbuf(); | ||
| 52 | Manage(JsonDocument::Create(rbuf.str())); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | 142 | BundleFileMgr(JsonDocument *fc) { Manage(fc); } | |
| 57 | |||
| 58 | 568 | virtual ~BundleFileMgr() { ReleaseDocument(); } | |
| 59 | |||
| 60 | 16 | std::string GetVersion() const { | |
| 61 |
2/4✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
|
32 | const JSON *ptr = JsonDocument::SearchInObject( |
| 62 | 16 | bundle_doc_->root(), "version", JSON_STRING); | |
| 63 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
32 | return (ptr) ? ptr->get<std::string>() : std::string(); |
| 64 | } | ||
| 65 | |||
| 66 | 16 | std::string GetEncoding() const { | |
| 67 |
2/4✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
|
32 | const JSON *ptr = JsonDocument::SearchInObject( |
| 68 | 16 | bundle_doc_->root(), "encoding", JSON_STRING); | |
| 69 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
32 | return (ptr) ? ptr->get<std::string>() : std::string(); |
| 70 | } | ||
| 71 | |||
| 72 | 64 | virtual PathString GetNext() { | |
| 73 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | const JSON *deps = Dependencies(); |
| 74 |
5/6✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 16 times.
✓ Branch 6 taken 48 times.
|
64 | if (deps == nullptr || current_index_ >= deps->size()) { |
| 75 | // This should be handled as an error code. Path string should never be | ||
| 76 | // empty; | ||
| 77 | 16 | return PathString(); | |
| 78 | } | ||
| 79 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | const JSON &entry = (*deps)[current_index_++]; |
| 80 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
|
48 | assert(entry.is_string()); |
| 81 | const PathString result = static_cast<PathString>( | ||
| 82 |
2/4✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
|
48 | entry.get<std::string>()); |
| 83 |
3/8✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 9 taken 48 times.
✗ Branch 10 not taken.
|
48 | return (result.IsEmpty()) ? GetNext() : result; |
| 84 | 48 | } | |
| 85 | |||
| 86 | 16 | virtual size_t Size() const { return size_; } | |
| 87 | |||
| 88 | 64 | operator bool() const { | |
| 89 |
2/4✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
|
64 | return (bundle_doc_ != nullptr) ? bundle_doc_->IsValid() : false; |
| 90 | } | ||
| 91 | |||
| 92 | private: | ||
| 93 | 142 | void Manage(JsonDocument *fc) { | |
| 94 | 142 | bundle_doc_ = fc; | |
| 95 | 142 | current_index_ = 0; | |
| 96 | 142 | ResetSize(); | |
| 97 | 142 | } | |
| 98 | |||
| 99 | 206 | const JSON *Dependencies() const { | |
| 100 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
|
206 | if (bundle_doc_ == nullptr) |
| 101 | ✗ | return nullptr; | |
| 102 |
2/4✓ Branch 2 taken 206 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 206 times.
✗ Branch 6 not taken.
|
618 | return JsonDocument::SearchInObject( |
| 103 | 412 | bundle_doc_->root(), "dependencies", JSON_ARRAY); | |
| 104 | } | ||
| 105 | |||
| 106 | 142 | void ResetSize() { | |
| 107 | 142 | const JSON *deps = Dependencies(); | |
| 108 |
1/2✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
|
142 | size_ = (deps != nullptr) ? deps->size() : 0; |
| 109 | 142 | } | |
| 110 | |||
| 111 | 142 | void ReleaseDocument() { | |
| 112 |
1/2✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
|
142 | if (bundle_doc_ != nullptr) { |
| 113 |
1/2✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
|
142 | delete bundle_doc_; |
| 114 | 142 | bundle_doc_ = nullptr; | |
| 115 | } | ||
| 116 | 142 | } | |
| 117 | size_t size_ = 0; | ||
| 118 | size_t current_index_ = 0; | ||
| 119 | JsonDocument *bundle_doc_ = nullptr; | ||
| 120 | }; | ||
| 121 | |||
| 122 | #endif | ||
| 123 |