GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/manifest.cc Lines: 96 115 83.5 %
Date: 2019-02-03 02:48:13 Branches: 50 82 61.0 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 */
4
5
#include "manifest.h"
6
7
#include <cstdio>
8
#include <map>
9
10
#include "catalog.h"
11
#include "util/posix.h"
12
13
using namespace std;  // NOLINT
14
15
namespace manifest {
16
17
36
Manifest *Manifest::LoadMem(const unsigned char *buffer,
18
                            const unsigned length)
19
{
20
36
  map<char, string> content;
21
36
  ParseKeyvalMem(buffer, length, &content);
22
23
36
  return Load(content);
24
}
25
26
27
8
Manifest *Manifest::LoadFile(const std::string &from_path) {
28
8
  map<char, string> content;
29
8
  if (!ParseKeyvalPath(from_path, &content))
30
    return NULL;
31
32
8
  return Load(content);
33
}
34
35
36
44
Manifest *Manifest::Load(const map<char, string> &content) {
37
44
  map<char, string>::const_iterator iter;
38
39
  // Required keys
40
44
  shash::Any catalog_hash;
41
44
  shash::Md5 root_path;
42
  uint32_t ttl;
43
  uint64_t revision;
44
45
44
  iter = content.find('C');
46
44
  if ((iter = content.find('C')) == content.end())
47
    return NULL;
48
  catalog_hash = MkFromHexPtr(shash::HexPtr(iter->second),
49
44
                              shash::kSuffixCatalog);
50
44
  if ((iter = content.find('R')) == content.end())
51
    return NULL;
52
44
  root_path = shash::Md5(shash::HexPtr(iter->second));
53
44
  if ((iter = content.find('D')) == content.end())
54
    return NULL;
55
44
  ttl = String2Uint64(iter->second);
56
44
  if ((iter = content.find('S')) == content.end())
57
    return NULL;
58
44
  revision = String2Uint64(iter->second);
59
60
61
  // Optional keys
62
44
  uint64_t catalog_size = 0;
63
44
  shash::Any micro_catalog_hash;
64
44
  string repository_name;
65
44
  shash::Any certificate;
66
44
  shash::Any history;
67
44
  uint64_t publish_timestamp = 0;
68
44
  bool garbage_collectable = false;
69
44
  bool has_alt_catalog_path = false;
70
44
  shash::Any meta_info;
71
72
44
  if ((iter = content.find('B')) != content.end())
73
44
    catalog_size = String2Uint64(iter->second);
74
44
  if ((iter = content.find('L')) != content.end())
75
    micro_catalog_hash = MkFromHexPtr(shash::HexPtr(iter->second),
76
                                      shash::kSuffixMicroCatalog);
77
44
  if ((iter = content.find('N')) != content.end())
78
44
    repository_name = iter->second;
79
44
  if ((iter = content.find('X')) != content.end())
80
    certificate = MkFromHexPtr(shash::HexPtr(iter->second),
81
44
                               shash::kSuffixCertificate);
82
44
  if ((iter = content.find('H')) != content.end())
83
    history = MkFromHexPtr(shash::HexPtr(iter->second),
84
44
                           shash::kSuffixHistory);
85
44
  if ((iter = content.find('T')) != content.end())
86
30
    publish_timestamp = String2Uint64(iter->second);
87
44
  if ((iter = content.find('G')) != content.end())
88
44
    garbage_collectable = (iter->second == "yes");
89
44
  if ((iter = content.find('A')) != content.end())
90
44
    has_alt_catalog_path = (iter->second == "yes");
91
44
  if ((iter = content.find('M')) != content.end())
92
    meta_info = MkFromHexPtr(shash::HexPtr(iter->second),
93
                             shash::kSuffixMetainfo);
94
95
  return new Manifest(catalog_hash, catalog_size, root_path, ttl, revision,
96
                      micro_catalog_hash, repository_name, certificate,
97
                      history, publish_timestamp, garbage_collectable,
98
44
                      has_alt_catalog_path, meta_info);
99
}
100
101
102
195
Manifest::Manifest(const shash::Any &catalog_hash,
103
                   const uint64_t catalog_size,
104
                   const string &root_path)
105
  : catalog_hash_(catalog_hash)
106
  , catalog_size_(catalog_size)
107
  , root_path_(shash::Md5(shash::AsciiPtr(root_path)))
108
  , ttl_(catalog::Catalog::kDefaultTTL)
109
  , revision_(0)
110
  , publish_timestamp_(0)
111
  , garbage_collectable_(false)
112
195
  , has_alt_catalog_path_(false)
113
{ }
114
115
116
/**
117
 * Creates the manifest string
118
 */
119
41
string Manifest::ExportString() const {
120
  string manifest =
121
    "C" + catalog_hash_.ToString() + "\n" +
122
    "B" + StringifyInt(catalog_size_) + "\n" +
123
    "R" + root_path_.ToString() + "\n" +
124
    "D" + StringifyInt(ttl_) + "\n" +
125
    "S" + StringifyInt(revision_) + "\n" +
126
    "G" + StringifyBool(garbage_collectable_) + "\n" +
127
41
    "A" + StringifyBool(has_alt_catalog_path_) + "\n";
128
129
41
  if (!micro_catalog_hash_.IsNull())
130
    manifest += "L" + micro_catalog_hash_.ToString() + "\n";
131
41
  if (repository_name_ != "")
132
41
    manifest += "N" + repository_name_ + "\n";
133
41
  if (!certificate_.IsNull())
134
41
    manifest += "X" + certificate_.ToString() + "\n";
135
41
  if (!history_.IsNull())
136
41
    manifest += "H" + history_.ToString() + "\n";
137
41
  if (publish_timestamp_ > 0)
138
21
    manifest += "T" + StringifyInt(publish_timestamp_) + "\n";
139
41
  if (!meta_info_.IsNull())
140
    manifest += "M" + meta_info_.ToString() + "\n";
141
  // Reserved: Z -> for identification of channel tips
142
143
41
  return manifest;
144
}
145
146
147
148
/**
149
 * Writes the .cvmfspublished file (unsigned).
150
 */
151
1
bool Manifest::Export(const std::string &path) const {
152
1
  FILE *fmanifest = fopen(path.c_str(), "w");
153
1
  if (!fmanifest)
154
    return false;
155
156
1
  string manifest = ExportString();
157
158
1
  if (fwrite(manifest.data(), 1, manifest.length(), fmanifest) !=
159
      manifest.length())
160
  {
161
    fclose(fmanifest);
162
    unlink(path.c_str());
163
    return false;
164
  }
165
1
  fclose(fmanifest);
166
167
1
  return true;
168
}
169
170
171
/**
172
 * Writes the cvmfschecksum.$repository file.  Atomic store.
173
 */
174
11
bool Manifest::ExportChecksum(const string &directory, const int mode) const {
175
  string checksum_path = MakeCanonicalPath(directory) + "/cvmfschecksum." +
176
11
                         repository_name_;
177
11
  string checksum_tmp_path;
178
11
  FILE *fchksum = CreateTempFile(checksum_path, mode, "w", &checksum_tmp_path);
179
11
  if (fchksum == NULL)
180
    return false;
181
  string cache_checksum = catalog_hash_.ToString() + "T" +
182
11
                          StringifyInt(publish_timestamp_);
183
  int written = fwrite(&(cache_checksum[0]), 1, cache_checksum.length(),
184
11
                       fchksum);
185
11
  fclose(fchksum);
186
11
  if (static_cast<unsigned>(written) != cache_checksum.length()) {
187
    unlink(checksum_tmp_path.c_str());
188
    return false;
189
  }
190
11
  int retval = rename(checksum_tmp_path.c_str(), checksum_path.c_str());
191
11
  if (retval != 0) {
192
    unlink(checksum_tmp_path.c_str());
193
    return false;
194
  }
195
11
  return true;
196
}
197
198
199
/**
200
 * Read the hash and the last-modified time stamp from the
201
 * cvmfschecksum.$repository file in the given directory.
202
 */
203
29
bool Manifest::ReadChecksum(
204
  const std::string &repo_name,
205
  const std::string &directory,
206
  shash::Any *hash,
207
  uint64_t *last_modified)
208
{
209
29
  bool result = false;
210
29
  const string checksum_path = directory + "/cvmfschecksum." + repo_name;
211
29
  FILE *file_checksum = fopen(checksum_path.c_str(), "r");
212
  char tmp[128];
213
  int read_bytes;
214

29
  if (file_checksum && (read_bytes = fread(tmp, 1, 128, file_checksum)) > 0) {
215
    // Separate hash from timestamp
216
5
    int separator_pos = 0;
217

5
    for (; (separator_pos < read_bytes) && (tmp[separator_pos] != 'T');
218
         ++separator_pos) { }
219
    *hash = shash::MkFromHexPtr(shash::HexPtr(string(tmp, separator_pos)),
220
5
                                shash::kSuffixCatalog);
221
222
    // Get local last modified time
223
5
    string str_modified;
224

5
    if ((tmp[separator_pos] == 'T') && (read_bytes > (separator_pos+1))) {
225
      str_modified = string(tmp+separator_pos+1,
226
3
                            read_bytes-(separator_pos+1));
227
3
      *last_modified = String2Uint64(str_modified);
228
3
      result = true;
229
    }
230
  }
231
29
  if (file_checksum) fclose(file_checksum);
232
233
29
  return result;
234
}
235
236
}  // namespace manifest