Line |
Branch |
Exec |
Source |
1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
|
6 |
|
|
#include "publish/repository.h" |
7 |
|
|
|
8 |
|
|
#include <cassert> |
9 |
|
|
#include <cstddef> |
10 |
|
|
#include <cstdlib> |
11 |
|
|
|
12 |
|
|
#include "catalog_mgr_ro.h" |
13 |
|
|
#include "catalog_mgr_rw.h" |
14 |
|
|
#include "crypto/hash.h" |
15 |
|
|
#include "crypto/signature.h" |
16 |
|
|
#include "gateway_util.h" |
17 |
|
|
#include "history_sqlite.h" |
18 |
|
|
#include "ingestion/ingestion_source.h" |
19 |
|
|
#include "manifest.h" |
20 |
|
|
#include "manifest_fetch.h" |
21 |
|
|
#include "network/download.h" |
22 |
|
|
#include "publish/except.h" |
23 |
|
|
#include "publish/repository_util.h" |
24 |
|
|
#include "publish/settings.h" |
25 |
|
|
#include "reflog.h" |
26 |
|
|
#include "statistics.h" |
27 |
|
|
#include "sync_mediator.h" |
28 |
|
|
#include "sync_union_aufs.h" |
29 |
|
|
#include "sync_union_overlayfs.h" |
30 |
|
|
#include "sync_union_tarball.h" |
31 |
|
|
#include "upload.h" |
32 |
|
|
#include "upload_spooler_definition.h" |
33 |
|
|
#include "util/logging.h" |
34 |
|
|
#include "util/pointer.h" |
35 |
|
|
#include "whitelist.h" |
36 |
|
|
|
37 |
|
|
// TODO(jblomer): Remove Me |
38 |
|
|
namespace swissknife { |
39 |
|
|
class CommandTag { |
40 |
|
|
static const std::string kHeadTag; |
41 |
|
|
static const std::string kPreviousHeadTag; |
42 |
|
|
}; |
43 |
|
|
const std::string CommandTag::kHeadTag = "trunk"; |
44 |
|
|
const std::string CommandTag::kPreviousHeadTag = "trunk-previous"; |
45 |
|
|
} |
46 |
|
|
|
47 |
|
|
namespace publish { |
48 |
|
|
|
49 |
|
✗ |
Repository::Repository(const SettingsRepository &settings, const bool exists) |
50 |
|
✗ |
: settings_(settings) |
51 |
|
✗ |
, statistics_(new perf::Statistics()) |
52 |
|
✗ |
, signature_mgr_(new signature::SignatureManager()) |
53 |
|
✗ |
, download_mgr_(NULL) |
54 |
|
✗ |
, simple_catalog_mgr_(NULL) |
55 |
|
✗ |
, whitelist_(NULL) |
56 |
|
✗ |
, reflog_(NULL) |
57 |
|
✗ |
, manifest_(NULL) |
58 |
|
✗ |
, history_(NULL) |
59 |
|
|
{ |
60 |
|
✗ |
signature_mgr_->Init(); |
61 |
|
|
|
62 |
|
✗ |
if (exists) { |
63 |
|
|
int rvb; |
64 |
|
✗ |
std::string keys = JoinStrings(FindFilesBySuffix( |
65 |
|
✗ |
settings.keychain().keychain_dir(), ".pub"), ":"); |
66 |
|
✗ |
rvb = signature_mgr_->LoadPublicRsaKeys(keys); |
67 |
|
✗ |
if (!rvb) { |
68 |
|
✗ |
signature_mgr_->Fini(); |
69 |
|
✗ |
delete signature_mgr_; |
70 |
|
✗ |
delete statistics_; |
71 |
|
✗ |
throw EPublish("cannot load public rsa key"); |
72 |
|
|
} |
73 |
|
|
} |
74 |
|
|
|
75 |
|
✗ |
if (!settings.cert_bundle().empty()) { |
76 |
|
✗ |
int rvi = setenv("X509_CERT_BUNDLE", settings.cert_bundle().c_str(), |
77 |
|
|
1 /* overwrite */); |
78 |
|
✗ |
if (rvi != 0) |
79 |
|
✗ |
throw EPublish("cannot set X509_CERT_BUNDLE environment variable"); |
80 |
|
|
} |
81 |
|
✗ |
download_mgr_ = new download::DownloadManager(16, |
82 |
|
✗ |
perf::StatisticsTemplate("download", statistics_)); |
83 |
|
✗ |
download_mgr_->UseSystemCertificatePath(); |
84 |
|
|
|
85 |
|
✗ |
if (settings.proxy() != "") { |
86 |
|
✗ |
download_mgr_->SetProxyChain(settings.proxy(), "", |
87 |
|
|
download::DownloadManager::kSetProxyBoth); |
88 |
|
|
} |
89 |
|
|
|
90 |
|
✗ |
if (exists) { |
91 |
|
|
try { |
92 |
|
✗ |
DownloadRootObjects(settings.url(), settings.fqrn(), settings.tmp_dir()); |
93 |
|
✗ |
} catch (const EPublish& e) { |
94 |
|
✗ |
signature_mgr_->Fini(); |
95 |
|
✗ |
delete signature_mgr_; |
96 |
|
✗ |
delete download_mgr_; |
97 |
|
✗ |
delete statistics_; |
98 |
|
✗ |
throw; |
99 |
|
|
} |
100 |
|
|
} |
101 |
|
|
} |
102 |
|
|
|
103 |
|
✗ |
Repository::~Repository() { |
104 |
|
✗ |
if (signature_mgr_ != NULL) signature_mgr_->Fini(); |
105 |
|
|
|
106 |
|
✗ |
delete history_; |
107 |
|
✗ |
delete manifest_; |
108 |
|
✗ |
delete reflog_; |
109 |
|
✗ |
delete whitelist_; |
110 |
|
✗ |
delete signature_mgr_; |
111 |
|
✗ |
delete download_mgr_; |
112 |
|
✗ |
delete simple_catalog_mgr_; |
113 |
|
✗ |
delete statistics_; |
114 |
|
|
} |
115 |
|
|
|
116 |
|
✗ |
const history::History *Repository::history() const { return history_; } |
117 |
|
|
|
118 |
|
✗ |
catalog::SimpleCatalogManager *Repository::GetSimpleCatalogManager() { |
119 |
|
✗ |
if (simple_catalog_mgr_ != NULL) return simple_catalog_mgr_; |
120 |
|
|
|
121 |
|
✗ |
simple_catalog_mgr_ = new catalog::SimpleCatalogManager( |
122 |
|
✗ |
manifest_->catalog_hash(), |
123 |
|
✗ |
settings_.url(), |
124 |
|
✗ |
settings_.tmp_dir(), |
125 |
|
|
download_mgr_, |
126 |
|
|
statistics_, |
127 |
|
✗ |
true /* manage_catalog_files */); |
128 |
|
✗ |
simple_catalog_mgr_->Init(); |
129 |
|
✗ |
return simple_catalog_mgr_; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
|
133 |
|
✗ |
void Repository::DownloadRootObjects( |
134 |
|
|
const std::string &url, const std::string &fqrn, const std::string &tmp_dir) |
135 |
|
|
{ |
136 |
|
✗ |
delete whitelist_; |
137 |
|
✗ |
whitelist_ = new whitelist::Whitelist(fqrn, download_mgr_, signature_mgr_); |
138 |
|
✗ |
whitelist::Failures rv_whitelist = whitelist_->LoadUrl(url); |
139 |
|
✗ |
if (whitelist_->status() != whitelist::Whitelist::kStAvailable) { |
140 |
|
✗ |
throw EPublish(std::string("cannot load whitelist [") + |
141 |
|
✗ |
whitelist::Code2Ascii(rv_whitelist) + "]"); |
142 |
|
|
} |
143 |
|
|
|
144 |
|
✗ |
manifest::ManifestEnsemble ensemble; |
145 |
|
✗ |
const uint64_t minimum_timestamp = 0; |
146 |
|
✗ |
const shash::Any *base_catalog = NULL; |
147 |
|
✗ |
manifest::Failures rv_manifest = manifest::Fetch( |
148 |
|
|
url, fqrn, minimum_timestamp, base_catalog, signature_mgr_, download_mgr_, |
149 |
|
|
&ensemble); |
150 |
|
✗ |
if (rv_manifest != manifest::kFailOk) throw EPublish("cannot load manifest"); |
151 |
|
✗ |
delete manifest_; |
152 |
|
✗ |
manifest_ = new manifest::Manifest(*ensemble.manifest); |
153 |
|
|
|
154 |
|
✗ |
std::string reflog_path; |
155 |
|
|
FILE *reflog_fd = |
156 |
|
✗ |
CreateTempFile(tmp_dir + "/reflog", kPrivateFileMode, "w", &reflog_path); |
157 |
|
✗ |
std::string reflog_url = url + "/.cvmfsreflog"; |
158 |
|
|
// TODO(jblomer): verify reflog hash |
159 |
|
|
// shash::Any reflog_hash(manifest_->GetHashAlgorithm()); |
160 |
|
✗ |
cvmfs::FileSink filesink(reflog_fd); |
161 |
|
|
download::JobInfo download_reflog(&reflog_url, false /* compressed */, |
162 |
|
✗ |
false /* probe hosts */, NULL, &filesink); |
163 |
|
✗ |
download::Failures rv_dl = download_mgr_->Fetch(&download_reflog); |
164 |
|
✗ |
fclose(reflog_fd); |
165 |
|
✗ |
if (rv_dl == download::kFailOk) { |
166 |
|
✗ |
delete reflog_; |
167 |
|
✗ |
reflog_ = manifest::Reflog::Open(reflog_path); |
168 |
|
✗ |
if (reflog_ == NULL) throw EPublish("cannot open reflog"); |
169 |
|
✗ |
reflog_->TakeDatabaseFileOwnership(); |
170 |
|
|
} else { |
171 |
|
✗ |
if (!download_reflog.IsFileNotFound()) { |
172 |
|
✗ |
throw EPublish(std::string("cannot load reflog [") + |
173 |
|
✗ |
download::Code2Ascii(rv_dl) + "]"); |
174 |
|
|
} |
175 |
|
✗ |
assert(reflog_ == NULL); |
176 |
|
|
} |
177 |
|
|
|
178 |
|
✗ |
std::string tags_path; |
179 |
|
|
FILE *tags_fd = |
180 |
|
✗ |
CreateTempFile(tmp_dir + "/tags", kPrivateFileMode, "w", &tags_path); |
181 |
|
✗ |
if (!manifest_->history().IsNull()) { |
182 |
|
✗ |
std::string tags_url = url + "/data/" + manifest_->history().MakePath(); |
183 |
|
✗ |
shash::Any tags_hash(manifest_->history()); |
184 |
|
✗ |
cvmfs::FileSink filesink(tags_fd); |
185 |
|
|
download::JobInfo download_tags(&tags_url, true /* compressed */, |
186 |
|
|
true /* probe hosts */, &tags_hash, |
187 |
|
✗ |
&filesink); |
188 |
|
✗ |
rv_dl = download_mgr_->Fetch(&download_tags); |
189 |
|
✗ |
fclose(tags_fd); |
190 |
|
✗ |
if (rv_dl != download::kFailOk) throw EPublish("cannot load tag database"); |
191 |
|
✗ |
delete history_; |
192 |
|
✗ |
history_ = history::SqliteHistory::OpenWritable(tags_path); |
193 |
|
✗ |
if (history_ == NULL) throw EPublish("cannot open tag database"); |
194 |
|
✗ |
} else { |
195 |
|
✗ |
fclose(tags_fd); |
196 |
|
✗ |
delete history_; |
197 |
|
✗ |
history_ = history::SqliteHistory::Create(tags_path, fqrn); |
198 |
|
✗ |
if (history_ == NULL) throw EPublish("cannot create tag database"); |
199 |
|
|
} |
200 |
|
✗ |
history_->TakeDatabaseFileOwnership(); |
201 |
|
|
|
202 |
|
✗ |
if (!manifest_->meta_info().IsNull()) { |
203 |
|
✗ |
shash::Any info_hash(manifest_->meta_info()); |
204 |
|
✗ |
std::string info_url = url + "/data/" + info_hash.MakePath(); |
205 |
|
✗ |
cvmfs::MemSink metainfo_memsink; |
206 |
|
|
download::JobInfo download_info(&info_url, true /* compressed */, |
207 |
|
|
true /* probe_hosts */, &info_hash, |
208 |
|
✗ |
&metainfo_memsink); |
209 |
|
✗ |
download::Failures rv_info = download_mgr_->Fetch(&download_info); |
210 |
|
✗ |
if (rv_info != download::kFailOk) { |
211 |
|
✗ |
throw EPublish(std::string("cannot load meta info [") + |
212 |
|
✗ |
download::Code2Ascii(rv_info) + "]"); |
213 |
|
|
} |
214 |
|
✗ |
meta_info_ = std::string(reinterpret_cast<char*>(metainfo_memsink.data()), |
215 |
|
✗ |
metainfo_memsink.pos()); |
216 |
|
✗ |
} else { |
217 |
|
✗ |
meta_info_ = "n/a"; |
218 |
|
|
} |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
|
222 |
|
✗ |
std::string Repository::GetFqrnFromUrl(const std::string &url) { |
223 |
|
✗ |
return GetFileName(MakeCanonicalPath(url)); |
224 |
|
|
} |
225 |
|
|
|
226 |
|
|
|
227 |
|
✗ |
bool Repository::IsMasterReplica() { |
228 |
|
✗ |
std::string url = settings_.url() + "/.cvmfs_master_replica"; |
229 |
|
✗ |
download::JobInfo head(&url, false /* probe_hosts */); |
230 |
|
✗ |
download::Failures retval = download_mgr_->Fetch(&head); |
231 |
|
✗ |
if (retval == download::kFailOk) { return true; } |
232 |
|
✗ |
if (head.IsFileNotFound()) { return false; } |
233 |
|
|
|
234 |
|
✗ |
throw EPublish(std::string("error looking for .cvmfs_master_replica [") + |
235 |
|
✗ |
download::Code2Ascii(retval) + "]"); |
236 |
|
|
} |
237 |
|
|
|
238 |
|
|
|
239 |
|
|
//------------------------------------------------------------------------------ |
240 |
|
|
|
241 |
|
|
|
242 |
|
✗ |
void Publisher::ConstructSpoolers() { |
243 |
|
✗ |
if ((spooler_files_ != NULL) && (spooler_catalogs_ != NULL)) |
244 |
|
✗ |
return; |
245 |
|
✗ |
assert((spooler_files_ == NULL) && (spooler_catalogs_ == NULL)); |
246 |
|
|
|
247 |
|
|
upload::SpoolerDefinition sd( |
248 |
|
✗ |
settings_.storage().GetLocator(), |
249 |
|
✗ |
settings_.transaction().hash_algorithm(), |
250 |
|
✗ |
settings_.transaction().compression_algorithm()); |
251 |
|
|
sd.session_token_file = |
252 |
|
✗ |
settings_.transaction().spool_area().gw_session_token(); |
253 |
|
✗ |
sd.key_file = settings_.keychain().gw_key_path(); |
254 |
|
|
|
255 |
|
✗ |
spooler_files_ = |
256 |
|
✗ |
upload::Spooler::Construct(sd, statistics_publish_.weak_ref()); |
257 |
|
✗ |
if (spooler_files_ == NULL) |
258 |
|
✗ |
throw EPublish("could not initialize file spooler"); |
259 |
|
|
|
260 |
|
✗ |
upload::SpoolerDefinition sd_catalogs(sd.Dup2DefaultCompression()); |
261 |
|
✗ |
spooler_catalogs_ = |
262 |
|
✗ |
upload::Spooler::Construct(sd_catalogs, statistics_publish_.weak_ref()); |
263 |
|
✗ |
if (spooler_catalogs_ == NULL) { |
264 |
|
✗ |
delete spooler_files_; |
265 |
|
✗ |
throw EPublish("could not initialize catalog spooler"); |
266 |
|
|
} |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
|
270 |
|
✗ |
void Publisher::CreateKeychain() { |
271 |
|
✗ |
if (settings_.keychain().HasDanglingMasterKeys()) { |
272 |
|
✗ |
throw EPublish("dangling master key pair"); |
273 |
|
|
} |
274 |
|
✗ |
if (settings_.keychain().HasDanglingRepositoryKeys()) { |
275 |
|
✗ |
throw EPublish("dangling repository keys"); |
276 |
|
|
} |
277 |
|
✗ |
if (!settings_.keychain().HasMasterKeys()) |
278 |
|
✗ |
signature_mgr_->GenerateMasterKeyPair(); |
279 |
|
✗ |
if (!settings_.keychain().HasRepositoryKeys()) |
280 |
|
✗ |
signature_mgr_->GenerateCertificate(settings_.fqrn()); |
281 |
|
|
|
282 |
|
✗ |
whitelist_ = new whitelist::Whitelist(settings_.fqrn(), NULL, signature_mgr_); |
283 |
|
|
std::string whitelist_str = whitelist::Whitelist::CreateString( |
284 |
|
✗ |
settings_.fqrn(), |
285 |
|
✗ |
settings_.whitelist_validity_days(), |
286 |
|
✗ |
settings_.transaction().hash_algorithm(), |
287 |
|
✗ |
signature_mgr_); |
288 |
|
✗ |
whitelist::Failures rv_wl = whitelist_->LoadMem(whitelist_str); |
289 |
|
✗ |
if (rv_wl != whitelist::kFailOk) |
290 |
|
✗ |
throw EPublish("whitelist generation failed"); |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
|
294 |
|
✗ |
void Publisher::CreateRootObjects() { |
295 |
|
|
// Reflog |
296 |
|
|
const std::string reflog_path = CreateTempPath( |
297 |
|
✗ |
settings_.transaction().spool_area().tmp_dir() + "/cvmfs_reflog", 0600); |
298 |
|
✗ |
reflog_ = manifest::Reflog::Create(reflog_path, settings_.fqrn()); |
299 |
|
✗ |
if (reflog_ == NULL) throw EPublish("could not create reflog"); |
300 |
|
✗ |
reflog_->TakeDatabaseFileOwnership(); |
301 |
|
|
|
302 |
|
|
// Root file catalog and initial manifest |
303 |
|
✗ |
manifest_ = catalog::WritableCatalogManager::CreateRepository( |
304 |
|
✗ |
settings_.transaction().spool_area().tmp_dir(), |
305 |
|
✗ |
settings_.transaction().is_volatile(), |
306 |
|
✗ |
settings_.transaction().voms_authz(), |
307 |
|
|
spooler_catalogs_); |
308 |
|
✗ |
spooler_catalogs_->WaitForUpload(); |
309 |
|
✗ |
if (manifest_ == NULL) |
310 |
|
✗ |
throw EPublish("could not create initial file catalog"); |
311 |
|
✗ |
reflog_->AddCatalog(manifest_->catalog_hash()); |
312 |
|
|
|
313 |
|
✗ |
manifest_->set_repository_name(settings_.fqrn()); |
314 |
|
✗ |
manifest_->set_ttl(settings_.transaction().ttl_second()); |
315 |
|
|
const bool needs_bootstrap_shortcuts = |
316 |
|
✗ |
!settings_.transaction().voms_authz().empty(); |
317 |
|
✗ |
manifest_->set_has_alt_catalog_path(needs_bootstrap_shortcuts); |
318 |
|
✗ |
manifest_->set_garbage_collectability( |
319 |
|
✗ |
settings_.transaction().is_garbage_collectable()); |
320 |
|
|
|
321 |
|
|
// Tag database |
322 |
|
|
const std::string tags_path = CreateTempPath( |
323 |
|
✗ |
settings_.transaction().spool_area().tmp_dir() + "/cvmfs_tags", 0600); |
324 |
|
✗ |
history_ = history::SqliteHistory::Create(tags_path, settings_.fqrn()); |
325 |
|
✗ |
if (history_ == NULL) throw EPublish("could not create tag database"); |
326 |
|
✗ |
history_->TakeDatabaseFileOwnership(); |
327 |
|
|
history::History::Tag tag_trunk( |
328 |
|
|
"trunk", |
329 |
|
✗ |
manifest_->catalog_hash(), manifest_->catalog_size(), manifest_->revision(), |
330 |
|
✗ |
manifest_->publish_timestamp(), "empty repository", "" /* branch */); |
331 |
|
✗ |
history_->Insert(tag_trunk); |
332 |
|
|
|
333 |
|
|
// Meta information, TODO(jblomer) |
334 |
|
✗ |
meta_info_ = "{}"; |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
|
338 |
|
✗ |
void Publisher::CreateStorage() { |
339 |
|
✗ |
ConstructSpoolers(); |
340 |
|
✗ |
if (!spooler_files_->Create()) |
341 |
|
✗ |
throw EPublish("could not initialize repository storage area"); |
342 |
|
|
} |
343 |
|
|
|
344 |
|
|
|
345 |
|
✗ |
void Publisher::PushCertificate() { |
346 |
|
|
upload::Spooler::CallbackPtr callback = |
347 |
|
✗ |
spooler_files_->RegisterListener(&Publisher::OnProcessCertificate, this); |
348 |
|
✗ |
spooler_files_->ProcessCertificate( |
349 |
|
✗ |
new StringIngestionSource(signature_mgr_->GetCertificate())); |
350 |
|
✗ |
spooler_files_->WaitForUpload(); |
351 |
|
✗ |
spooler_files_->UnregisterListener(callback); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
|
355 |
|
✗ |
void Publisher::PushHistory() { |
356 |
|
✗ |
assert(history_ != NULL); |
357 |
|
✗ |
history_->SetPreviousRevision(manifest_->history()); |
358 |
|
✗ |
const string history_path = history_->filename(); |
359 |
|
✗ |
history_->DropDatabaseFileOwnership(); |
360 |
|
✗ |
delete history_; |
361 |
|
|
|
362 |
|
|
upload::Spooler::CallbackPtr callback = |
363 |
|
✗ |
spooler_files_->RegisterListener(&Publisher::OnProcessHistory, this); |
364 |
|
✗ |
spooler_files_->ProcessHistory(history_path); |
365 |
|
✗ |
spooler_files_->WaitForUpload(); |
366 |
|
✗ |
spooler_files_->UnregisterListener(callback); |
367 |
|
|
|
368 |
|
✗ |
history_ = history::SqliteHistory::OpenWritable(history_path); |
369 |
|
✗ |
assert(history_ != NULL); |
370 |
|
✗ |
history_->TakeDatabaseFileOwnership(); |
371 |
|
|
} |
372 |
|
|
|
373 |
|
|
|
374 |
|
✗ |
void Publisher::PushMetainfo() { |
375 |
|
|
upload::Spooler::CallbackPtr callback = |
376 |
|
✗ |
spooler_files_->RegisterListener(&Publisher::OnProcessMetainfo, this); |
377 |
|
✗ |
spooler_files_->ProcessMetainfo(new StringIngestionSource(meta_info_)); |
378 |
|
✗ |
spooler_files_->WaitForUpload(); |
379 |
|
✗ |
spooler_files_->UnregisterListener(callback); |
380 |
|
|
} |
381 |
|
|
|
382 |
|
|
|
383 |
|
✗ |
void Publisher::PushManifest() { |
384 |
|
✗ |
std::string signed_manifest = manifest_->ExportString(); |
385 |
|
✗ |
shash::Any manifest_hash(settings_.transaction().hash_algorithm()); |
386 |
|
✗ |
shash::HashMem( |
387 |
|
✗ |
reinterpret_cast<const unsigned char *>(signed_manifest.data()), |
388 |
|
✗ |
signed_manifest.length(), &manifest_hash); |
389 |
|
✗ |
signed_manifest += "--\n" + manifest_hash.ToString() + "\n"; |
390 |
|
|
unsigned char *signature; |
391 |
|
|
unsigned sig_size; |
392 |
|
✗ |
bool rvb = signature_mgr_->Sign( |
393 |
|
✗ |
reinterpret_cast<const unsigned char *>(manifest_hash.ToString().data()), |
394 |
|
|
manifest_hash.GetHexSize(), &signature, &sig_size); |
395 |
|
✗ |
if (!rvb) throw EPublish("cannot sign manifest"); |
396 |
|
✗ |
signed_manifest += std::string(reinterpret_cast<char *>(signature), sig_size); |
397 |
|
✗ |
free(signature); |
398 |
|
|
|
399 |
|
|
// Create alternative bootstrapping symlinks for VOMS secured repos |
400 |
|
✗ |
if (manifest_->has_alt_catalog_path()) { |
401 |
|
✗ |
rvb = |
402 |
|
✗ |
spooler_files_->PlaceBootstrappingShortcut(manifest_->certificate()) && |
403 |
|
✗ |
spooler_files_->PlaceBootstrappingShortcut(manifest_->catalog_hash()) && |
404 |
|
✗ |
(manifest_->history().IsNull() || |
405 |
|
✗ |
spooler_files_->PlaceBootstrappingShortcut(manifest_->history())) && |
406 |
|
✗ |
(manifest_->meta_info().IsNull() || |
407 |
|
✗ |
spooler_files_->PlaceBootstrappingShortcut(manifest_->meta_info())); |
408 |
|
✗ |
if (!rvb) EPublish("cannot place VOMS bootstrapping symlinks"); |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
upload::Spooler::CallbackPtr callback = |
412 |
|
✗ |
spooler_files_->RegisterListener(&Publisher::OnUploadManifest, this); |
413 |
|
✗ |
spooler_files_->Upload(".cvmfspublished", |
414 |
|
✗ |
new StringIngestionSource(signed_manifest)); |
415 |
|
✗ |
spooler_files_->WaitForUpload(); |
416 |
|
✗ |
spooler_files_->UnregisterListener(callback); |
417 |
|
|
} |
418 |
|
|
|
419 |
|
|
|
420 |
|
✗ |
void Publisher::PushReflog() { |
421 |
|
✗ |
const string reflog_path = reflog_->database_file(); |
422 |
|
✗ |
reflog_->DropDatabaseFileOwnership(); |
423 |
|
✗ |
delete reflog_; |
424 |
|
|
|
425 |
|
✗ |
shash::Any hash_reflog(settings_.transaction().hash_algorithm()); |
426 |
|
✗ |
manifest::Reflog::HashDatabase(reflog_path, &hash_reflog); |
427 |
|
|
|
428 |
|
|
upload::Spooler::CallbackPtr callback = |
429 |
|
✗ |
spooler_files_->RegisterListener(&Publisher::OnUploadReflog, this); |
430 |
|
✗ |
spooler_files_->UploadReflog(reflog_path); |
431 |
|
✗ |
spooler_files_->WaitForUpload(); |
432 |
|
✗ |
spooler_files_->UnregisterListener(callback); |
433 |
|
|
|
434 |
|
✗ |
manifest_->set_reflog_hash(hash_reflog); |
435 |
|
|
|
436 |
|
✗ |
reflog_ = manifest::Reflog::Open(reflog_path); |
437 |
|
✗ |
assert(reflog_ != NULL); |
438 |
|
✗ |
reflog_->TakeDatabaseFileOwnership(); |
439 |
|
|
} |
440 |
|
|
|
441 |
|
|
|
442 |
|
✗ |
void Publisher::PushWhitelist() { |
443 |
|
|
// TODO(jblomer): PKCS7 handling |
444 |
|
|
upload::Spooler::CallbackPtr callback = |
445 |
|
✗ |
spooler_files_->RegisterListener(&Publisher::OnUploadWhitelist, this); |
446 |
|
✗ |
spooler_files_->Upload(".cvmfswhitelist", |
447 |
|
✗ |
new StringIngestionSource(whitelist_->ExportString())); |
448 |
|
✗ |
spooler_files_->WaitForUpload(); |
449 |
|
✗ |
spooler_files_->UnregisterListener(callback); |
450 |
|
|
} |
451 |
|
|
|
452 |
|
|
|
453 |
|
✗ |
Publisher *Publisher::Create(const SettingsPublisher &settings) { |
454 |
|
✗ |
UniquePtr<Publisher> publisher(new Publisher(settings, false)); |
455 |
|
|
|
456 |
|
✗ |
LogCvmfs(kLogCvmfs, publisher->llvl_ | kLogStdout | kLogNoLinebreak, |
457 |
|
|
"Creating Key Chain... "); |
458 |
|
✗ |
publisher->CreateKeychain(); |
459 |
|
✗ |
publisher->ExportKeychain(); |
460 |
|
✗ |
LogCvmfs(kLogCvmfs, publisher->llvl_ | kLogStdout, "done"); |
461 |
|
|
|
462 |
|
✗ |
LogCvmfs(kLogCvmfs, publisher->llvl_ | kLogStdout | kLogNoLinebreak, |
463 |
|
|
"Creating Backend Storage... "); |
464 |
|
✗ |
publisher->CreateStorage(); |
465 |
|
✗ |
publisher->PushWhitelist(); |
466 |
|
✗ |
LogCvmfs(kLogCvmfs, publisher->llvl_ | kLogStdout, "done"); |
467 |
|
|
|
468 |
|
✗ |
LogCvmfs(kLogCvmfs, publisher->llvl_ | kLogStdout | kLogNoLinebreak, |
469 |
|
|
"Creating Initial Repository... "); |
470 |
|
✗ |
publisher->InitSpoolArea(); |
471 |
|
✗ |
publisher->CreateRootObjects(); |
472 |
|
✗ |
publisher->PushHistory(); |
473 |
|
✗ |
publisher->PushCertificate(); |
474 |
|
✗ |
publisher->PushMetainfo(); |
475 |
|
✗ |
publisher->PushReflog(); |
476 |
|
✗ |
publisher->PushManifest(); |
477 |
|
|
// TODO(jblomer): meta-info |
478 |
|
|
|
479 |
|
|
// Re-create from empty repository in order to properly initialize |
480 |
|
|
// parent Repository object |
481 |
|
✗ |
publisher = new Publisher(settings); |
482 |
|
|
|
483 |
|
✗ |
LogCvmfs(kLogCvmfs, publisher->llvl_ | kLogStdout, "done"); |
484 |
|
|
|
485 |
|
✗ |
return publisher.Release(); |
486 |
|
|
} |
487 |
|
|
|
488 |
|
✗ |
void Publisher::ExportKeychain() { |
489 |
|
✗ |
CreateDirectoryAsOwner(settings_.keychain().keychain_dir(), kDefaultDirMode); |
490 |
|
|
|
491 |
|
|
bool rvb; |
492 |
|
✗ |
rvb = SafeWriteToFile(signature_mgr_->GetActivePubkeys(), |
493 |
|
✗ |
settings_.keychain().master_public_key_path(), 0644); |
494 |
|
✗ |
if (!rvb) throw EPublish("cannot export public master key"); |
495 |
|
✗ |
rvb = SafeWriteToFile(signature_mgr_->GetCertificate(), |
496 |
|
✗ |
settings_.keychain().certificate_path(), 0644); |
497 |
|
✗ |
if (!rvb) throw EPublish("cannot export certificate"); |
498 |
|
|
|
499 |
|
✗ |
rvb = SafeWriteToFile(signature_mgr_->GetPrivateKey(), |
500 |
|
✗ |
settings_.keychain().private_key_path(), 0600); |
501 |
|
✗ |
if (!rvb) throw EPublish("cannot export private certificate key"); |
502 |
|
✗ |
rvb = SafeWriteToFile(signature_mgr_->GetPrivateMasterKey(), |
503 |
|
✗ |
settings_.keychain().master_private_key_path(), 0600); |
504 |
|
✗ |
if (!rvb) throw EPublish("cannot export private master key"); |
505 |
|
|
|
506 |
|
|
int rvi; |
507 |
|
✗ |
rvi = chown(settings_.keychain().master_public_key_path().c_str(), |
508 |
|
|
settings_.owner_uid(), settings_.owner_gid()); |
509 |
|
✗ |
if (rvi != 0) throw EPublish("cannot set key file ownership"); |
510 |
|
✗ |
rvi = chown(settings_.keychain().certificate_path().c_str(), |
511 |
|
|
settings_.owner_uid(), settings_.owner_gid()); |
512 |
|
✗ |
if (rvi != 0) throw EPublish("cannot set key file ownership"); |
513 |
|
✗ |
rvi = chown(settings_.keychain().private_key_path().c_str(), |
514 |
|
|
settings_.owner_uid(), settings_.owner_gid()); |
515 |
|
✗ |
if (rvi != 0) throw EPublish("cannot set key file ownership"); |
516 |
|
✗ |
rvi = chown(settings_.keychain().master_private_key_path().c_str(), |
517 |
|
|
settings_.owner_uid(), settings_.owner_gid()); |
518 |
|
✗ |
if (rvi != 0) throw EPublish("cannot set key file ownership"); |
519 |
|
|
} |
520 |
|
|
|
521 |
|
✗ |
void Publisher::OnProcessCertificate(const upload::SpoolerResult &result) { |
522 |
|
✗ |
if (result.return_code != 0) { |
523 |
|
✗ |
throw EPublish("cannot write certificate to storage"); |
524 |
|
|
} |
525 |
|
✗ |
manifest_->set_certificate(result.content_hash); |
526 |
|
✗ |
reflog_->AddCertificate(result.content_hash); |
527 |
|
|
} |
528 |
|
|
|
529 |
|
✗ |
void Publisher::OnProcessHistory(const upload::SpoolerResult &result) { |
530 |
|
✗ |
if (result.return_code != 0) { |
531 |
|
✗ |
throw EPublish("cannot write tag database to storage"); |
532 |
|
|
} |
533 |
|
✗ |
manifest_->set_history(result.content_hash); |
534 |
|
✗ |
reflog_->AddHistory(result.content_hash); |
535 |
|
|
} |
536 |
|
|
|
537 |
|
✗ |
void Publisher::OnProcessMetainfo(const upload::SpoolerResult &result) { |
538 |
|
✗ |
if (result.return_code != 0) { |
539 |
|
✗ |
throw EPublish("cannot write repository meta info to storage"); |
540 |
|
|
} |
541 |
|
✗ |
manifest_->set_meta_info(result.content_hash); |
542 |
|
✗ |
reflog_->AddMetainfo(result.content_hash); |
543 |
|
|
} |
544 |
|
|
|
545 |
|
✗ |
void Publisher::OnUploadManifest(const upload::SpoolerResult &result) { |
546 |
|
✗ |
if (result.return_code != 0) { |
547 |
|
✗ |
throw EPublish("cannot write manifest to storage"); |
548 |
|
|
} |
549 |
|
|
} |
550 |
|
|
|
551 |
|
✗ |
void Publisher::OnUploadReflog(const upload::SpoolerResult &result) { |
552 |
|
✗ |
if (result.return_code != 0) { |
553 |
|
✗ |
throw EPublish("cannot write reflog to storage"); |
554 |
|
|
} |
555 |
|
|
} |
556 |
|
|
|
557 |
|
✗ |
void Publisher::OnUploadWhitelist(const upload::SpoolerResult &result) { |
558 |
|
✗ |
if (result.return_code != 0) { |
559 |
|
✗ |
throw EPublish("cannot write whitelist to storage"); |
560 |
|
|
} |
561 |
|
|
} |
562 |
|
|
|
563 |
|
✗ |
void Publisher::CreateDirectoryAsOwner(const std::string &path, int mode) |
564 |
|
|
{ |
565 |
|
✗ |
const bool rvb = MkdirDeep(path, mode); |
566 |
|
✗ |
if (!rvb) throw EPublish("cannot create directory " + path); |
567 |
|
✗ |
int rvi = chown(path.c_str(), settings_.owner_uid(), settings_.owner_gid()); |
568 |
|
✗ |
if (rvi != 0) throw EPublish("cannot set ownership on directory " + path); |
569 |
|
|
} |
570 |
|
|
|
571 |
|
✗ |
void Publisher::InitSpoolArea() { |
572 |
|
✗ |
CreateDirectoryAsOwner(settings_.transaction().spool_area().workspace(), |
573 |
|
|
kPrivateDirMode); |
574 |
|
✗ |
CreateDirectoryAsOwner(settings_.transaction().spool_area().tmp_dir(), |
575 |
|
|
kPrivateDirMode); |
576 |
|
✗ |
CreateDirectoryAsOwner(settings_.transaction().spool_area().cache_dir(), |
577 |
|
|
kPrivateDirMode); |
578 |
|
✗ |
CreateDirectoryAsOwner(settings_.transaction().spool_area().scratch_dir(), |
579 |
|
|
kDefaultDirMode); |
580 |
|
✗ |
CreateDirectoryAsOwner(settings_.transaction().spool_area().ovl_work_dir(), |
581 |
|
|
kPrivateDirMode); |
582 |
|
|
|
583 |
|
|
// On a managed node, the mount points are already mounted |
584 |
|
✗ |
if (!DirectoryExists(settings_.transaction().spool_area().readonly_mnt())) { |
585 |
|
✗ |
CreateDirectoryAsOwner(settings_.transaction().spool_area().readonly_mnt(), |
586 |
|
|
kDefaultDirMode); |
587 |
|
|
} |
588 |
|
✗ |
if (!DirectoryExists(settings_.transaction().spool_area().union_mnt())) { |
589 |
|
✗ |
CreateDirectoryAsOwner( |
590 |
|
✗ |
settings_.transaction().spool_area().union_mnt(), kDefaultDirMode); |
591 |
|
|
} |
592 |
|
|
} |
593 |
|
|
|
594 |
|
✗ |
Publisher::Publisher(const SettingsPublisher &settings, const bool exists) |
595 |
|
✗ |
: Repository(SettingsRepository(settings), exists) |
596 |
|
✗ |
, settings_(settings) |
597 |
|
✗ |
, statistics_publish_(new perf::StatisticsTemplate("publish", statistics_)) |
598 |
|
✗ |
, llvl_(settings.is_silent() ? kLogNone : kLogNormal) |
599 |
|
✗ |
, in_transaction_(settings.transaction().spool_area().transaction_lock()) |
600 |
|
✗ |
, is_publishing_(settings.transaction().spool_area().publishing_lock()) |
601 |
|
✗ |
, spooler_files_(NULL) |
602 |
|
✗ |
, spooler_catalogs_(NULL) |
603 |
|
✗ |
, catalog_mgr_(NULL) |
604 |
|
✗ |
, sync_parameters_(NULL) |
605 |
|
✗ |
, sync_mediator_(NULL) |
606 |
|
✗ |
, sync_union_(NULL) |
607 |
|
|
{ |
608 |
|
✗ |
if (settings.transaction().layout_revision() != kRequiredLayoutRevision) { |
609 |
|
✗ |
unsigned layout_revision = settings.transaction().layout_revision(); |
610 |
|
✗ |
throw EPublish( |
611 |
|
✗ |
"This repository uses layout revision " + StringifyInt(layout_revision) |
612 |
|
✗ |
+ ".\n" |
613 |
|
✗ |
"This version of CernVM-FS requires layout revision " + StringifyInt( |
614 |
|
✗ |
kRequiredLayoutRevision) + ", which is\n" |
615 |
|
✗ |
"incompatible to " + StringifyInt(layout_revision) + ".\n\n" |
616 |
|
|
"Please run `cvmfs_server migrate` to update your repository before " |
617 |
|
|
"proceeding.", |
618 |
|
✗ |
EPublish::kFailLayoutRevision); |
619 |
|
|
} |
620 |
|
|
|
621 |
|
✗ |
if (!exists) |
622 |
|
✗ |
return; |
623 |
|
|
|
624 |
|
✗ |
CreateDirectoryAsOwner(settings_.transaction().spool_area().tmp_dir(), |
625 |
|
|
kPrivateDirMode); |
626 |
|
|
|
627 |
|
✗ |
if (settings.storage().type() == upload::SpoolerDefinition::Gateway) { |
628 |
|
✗ |
if (!settings.keychain().HasGatewayKey()) { |
629 |
|
✗ |
throw EPublish("gateway key missing: " + |
630 |
|
✗ |
settings.keychain().gw_key_path()); |
631 |
|
|
} |
632 |
|
✗ |
gw_key_ = gateway::ReadGatewayKey(settings.keychain().gw_key_path()); |
633 |
|
✗ |
if (!gw_key_.IsValid()) { |
634 |
|
✗ |
throw EPublish("cannot read gateway key: " + |
635 |
|
✗ |
settings.keychain().gw_key_path()); |
636 |
|
|
} |
637 |
|
|
} |
638 |
|
|
|
639 |
|
✗ |
if ((settings.storage().type() != upload::SpoolerDefinition::Gateway) && |
640 |
|
✗ |
!settings.transaction().in_enter_session()) |
641 |
|
|
{ |
642 |
|
✗ |
int rvb = signature_mgr_->LoadCertificatePath( |
643 |
|
✗ |
settings.keychain().certificate_path()); |
644 |
|
✗ |
if (!rvb) |
645 |
|
✗ |
throw EPublish("cannot load certificate, thus cannot commit changes"); |
646 |
|
✗ |
rvb = signature_mgr_->LoadPrivateKeyPath( |
647 |
|
✗ |
settings.keychain().private_key_path(), ""); |
648 |
|
✗ |
if (!rvb) |
649 |
|
✗ |
throw EPublish("cannot load private key, thus cannot commit changes"); |
650 |
|
|
// The private master key might be on a key card instead |
651 |
|
✗ |
if (FileExists(settings.keychain().master_private_key_path())) { |
652 |
|
✗ |
rvb = signature_mgr_->LoadPrivateMasterKeyPath( |
653 |
|
✗ |
settings.keychain().master_private_key_path()); |
654 |
|
✗ |
if (!rvb) throw EPublish("cannot load private master key"); |
655 |
|
|
} |
656 |
|
✗ |
if (!signature_mgr_->KeysMatch()) |
657 |
|
✗ |
throw EPublish("corrupted keychain"); |
658 |
|
|
} |
659 |
|
|
|
660 |
|
✗ |
if (settings.is_managed()) |
661 |
|
✗ |
managed_node_ = new ManagedNode(this); |
662 |
|
✗ |
session_ = new Session(settings_, llvl_); |
663 |
|
✗ |
if (in_transaction_.IsSet()) |
664 |
|
✗ |
ConstructSpoolers(); |
665 |
|
|
} |
666 |
|
|
|
667 |
|
✗ |
Publisher::~Publisher() { |
668 |
|
✗ |
delete sync_union_; |
669 |
|
✗ |
delete sync_mediator_; |
670 |
|
✗ |
delete sync_parameters_; |
671 |
|
✗ |
delete catalog_mgr_; |
672 |
|
✗ |
delete spooler_catalogs_; |
673 |
|
✗ |
delete spooler_files_; |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
|
677 |
|
✗ |
void Publisher::ConstructSyncManagers() { |
678 |
|
✗ |
ConstructSpoolers(); |
679 |
|
|
|
680 |
|
✗ |
if (catalog_mgr_ == NULL) { |
681 |
|
✗ |
catalog_mgr_ = new catalog::WritableCatalogManager( |
682 |
|
✗ |
settings_.transaction().base_hash(), |
683 |
|
✗ |
settings_.url(), |
684 |
|
✗ |
settings_.transaction().spool_area().tmp_dir(), |
685 |
|
|
spooler_catalogs_, |
686 |
|
|
download_mgr_, |
687 |
|
✗ |
settings_.transaction().enforce_limits(), |
688 |
|
✗ |
settings_.transaction().limit_nested_catalog_kentries(), |
689 |
|
✗ |
settings_.transaction().limit_root_catalog_kentries(), |
690 |
|
✗ |
settings_.transaction().limit_file_size_mb(), |
691 |
|
|
statistics_, |
692 |
|
✗ |
settings_.transaction().use_catalog_autobalance(), |
693 |
|
✗ |
settings_.transaction().autobalance_max_weight(), |
694 |
|
✗ |
settings_.transaction().autobalance_min_weight(), |
695 |
|
✗ |
""); |
696 |
|
✗ |
catalog_mgr_->Init(); |
697 |
|
|
} |
698 |
|
|
|
699 |
|
✗ |
if (sync_parameters_ == NULL) { |
700 |
|
✗ |
SyncParameters *p = new SyncParameters(); |
701 |
|
✗ |
p->spooler = spooler_files_; |
702 |
|
✗ |
p->repo_name = settings_.fqrn(); |
703 |
|
✗ |
p->dir_union = settings_.transaction().spool_area().union_mnt(); |
704 |
|
✗ |
p->dir_scratch = settings_.transaction().spool_area().scratch_dir(); |
705 |
|
✗ |
p->dir_rdonly = settings_.transaction().spool_area().readonly_mnt(); |
706 |
|
✗ |
p->dir_temp = settings_.transaction().spool_area().tmp_dir(); |
707 |
|
✗ |
p->base_hash = settings_.transaction().base_hash(); |
708 |
|
✗ |
p->stratum0 = settings_.url(); |
709 |
|
|
// p->manifest_path = SHOULD NOT BE NEEDED |
710 |
|
|
// p->spooler_definition = SHOULD NOT BE NEEDED; |
711 |
|
|
// p->union_fs_type = SHOULD NOT BE NEEDED |
712 |
|
✗ |
p->print_changeset = settings_.transaction().print_changeset(); |
713 |
|
✗ |
p->dry_run = settings_.transaction().dry_run(); |
714 |
|
✗ |
sync_parameters_ = p; |
715 |
|
|
} |
716 |
|
|
|
717 |
|
✗ |
if (sync_mediator_ == NULL) { |
718 |
|
✗ |
sync_mediator_ = |
719 |
|
✗ |
new SyncMediator(catalog_mgr_, sync_parameters_, *statistics_publish_); |
720 |
|
|
} |
721 |
|
|
|
722 |
|
✗ |
if (sync_union_ == NULL) { |
723 |
|
✗ |
switch (settings_.transaction().union_fs()) { |
724 |
|
✗ |
case kUnionFsAufs: |
725 |
|
✗ |
sync_union_ = new publish::SyncUnionAufs( |
726 |
|
|
sync_mediator_, |
727 |
|
✗ |
settings_.transaction().spool_area().readonly_mnt(), |
728 |
|
✗ |
settings_.transaction().spool_area().union_mnt(), |
729 |
|
✗ |
settings_.transaction().spool_area().scratch_dir()); |
730 |
|
✗ |
break; |
731 |
|
✗ |
case kUnionFsOverlay: |
732 |
|
✗ |
sync_union_ = new publish::SyncUnionOverlayfs( |
733 |
|
|
sync_mediator_, |
734 |
|
✗ |
settings_.transaction().spool_area().readonly_mnt(), |
735 |
|
✗ |
settings_.transaction().spool_area().union_mnt(), |
736 |
|
✗ |
settings_.transaction().spool_area().scratch_dir()); |
737 |
|
✗ |
break; |
738 |
|
✗ |
case kUnionFsTarball: |
739 |
|
✗ |
sync_union_ = new publish::SyncUnionTarball( |
740 |
|
✗ |
sync_mediator_, |
741 |
|
✗ |
settings_.transaction().spool_area().readonly_mnt(), |
742 |
|
|
// TODO(jblomer): get from settings |
743 |
|
|
"tar_file", |
744 |
|
|
"base_directory", |
745 |
|
|
-1u, |
746 |
|
|
-1u, |
747 |
|
|
"to_delete", |
748 |
|
✗ |
false /* create_catalog */); |
749 |
|
✗ |
break; |
750 |
|
✗ |
default: |
751 |
|
✗ |
throw EPublish("unknown union file system"); |
752 |
|
|
} |
753 |
|
✗ |
bool rvb = sync_union_->Initialize(); |
754 |
|
✗ |
if (!rvb) { |
755 |
|
✗ |
delete sync_union_; |
756 |
|
✗ |
sync_union_ = NULL; |
757 |
|
✗ |
throw EPublish("cannot initialize union file system engine"); |
758 |
|
|
} |
759 |
|
|
} |
760 |
|
|
} |
761 |
|
|
|
762 |
|
✗ |
void Publisher::ExitShell() { |
763 |
|
✗ |
std::string session_dir = Env::GetEnterSessionDir(); |
764 |
|
✗ |
std::string session_pid_tmp = session_dir + "/session_pid"; |
765 |
|
✗ |
std::string session_pid; |
766 |
|
✗ |
int fd_session_pid = open(session_pid_tmp.c_str(), O_RDONLY); |
767 |
|
✗ |
if (fd_session_pid < 0) throw EPublish("Session pid cannot be retrieved"); |
768 |
|
✗ |
SafeReadToString(fd_session_pid, &session_pid); |
769 |
|
|
|
770 |
|
✗ |
pid_t pid_child = String2Uint64(session_pid); |
771 |
|
✗ |
kill(pid_child, SIGUSR1); |
772 |
|
|
} |
773 |
|
|
|
774 |
|
✗ |
void Publisher::Sync() { |
775 |
|
✗ |
ServerLockFileGuard g(is_publishing_); |
776 |
|
|
|
777 |
|
✗ |
ConstructSyncManagers(); |
778 |
|
|
|
779 |
|
✗ |
sync_union_->Traverse(); |
780 |
|
✗ |
bool rvb = sync_mediator_->Commit(manifest_); |
781 |
|
✗ |
if (!rvb) throw EPublish("cannot write change set to storage"); |
782 |
|
|
|
783 |
|
✗ |
if (!settings_.transaction().dry_run()) { |
784 |
|
✗ |
spooler_files_->WaitForUpload(); |
785 |
|
✗ |
spooler_catalogs_->WaitForUpload(); |
786 |
|
✗ |
spooler_files_->FinalizeSession(false /* commit */); |
787 |
|
|
|
788 |
|
|
const std::string old_root_hash = |
789 |
|
✗ |
settings_.transaction().base_hash().ToString(true /* with_suffix */); |
790 |
|
|
const std::string new_root_hash = |
791 |
|
✗ |
manifest_->catalog_hash().ToString(true /* with_suffix */); |
792 |
|
✗ |
rvb = spooler_catalogs_->FinalizeSession(true /* commit */, |
793 |
|
|
old_root_hash, new_root_hash, |
794 |
|
✗ |
/* TODO(jblomer) */ sync_parameters_->repo_tag); |
795 |
|
✗ |
if (!rvb) |
796 |
|
✗ |
throw EPublish("failed to commit transaction"); |
797 |
|
|
|
798 |
|
|
// Reset to the new catalog root hash |
799 |
|
✗ |
settings_.GetTransaction()->SetBaseHash(manifest_->catalog_hash()); |
800 |
|
|
// TODO(jblomer): think about how to deal with the scratch area at |
801 |
|
|
// this point |
802 |
|
|
// WipeScratchArea(); |
803 |
|
|
} |
804 |
|
|
|
805 |
|
✗ |
delete sync_union_; |
806 |
|
✗ |
delete sync_mediator_; |
807 |
|
✗ |
delete sync_parameters_; |
808 |
|
✗ |
delete catalog_mgr_; |
809 |
|
✗ |
sync_union_ = NULL; |
810 |
|
✗ |
sync_mediator_ = NULL; |
811 |
|
✗ |
sync_parameters_ = NULL; |
812 |
|
✗ |
catalog_mgr_ = NULL; |
813 |
|
|
|
814 |
|
✗ |
if (!settings_.transaction().dry_run()) { |
815 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "New revision: %" PRIu64, |
816 |
|
✗ |
manifest_->revision()); |
817 |
|
✗ |
reflog_->AddCatalog(manifest_->catalog_hash()); |
818 |
|
|
} |
819 |
|
|
} |
820 |
|
|
|
821 |
|
✗ |
void Publisher::Publish() { |
822 |
|
✗ |
if (!in_transaction_.IsSet()) |
823 |
|
✗ |
throw EPublish("cannot publish outside transaction"); |
824 |
|
|
|
825 |
|
✗ |
PushReflog(); |
826 |
|
✗ |
PushManifest(); |
827 |
|
✗ |
in_transaction_.Clear(); |
828 |
|
|
} |
829 |
|
|
|
830 |
|
|
|
831 |
|
✗ |
void Publisher::MarkReplicatible(bool value) { |
832 |
|
✗ |
ConstructSpoolers(); |
833 |
|
|
|
834 |
|
✗ |
if (value) { |
835 |
|
✗ |
spooler_files_->Upload("/dev/null", "/.cvmfs_master_replica"); |
836 |
|
|
} else { |
837 |
|
✗ |
spooler_files_->RemoveAsync("/.cvmfs_master_replica"); |
838 |
|
|
} |
839 |
|
✗ |
spooler_files_->WaitForUpload(); |
840 |
|
✗ |
if (spooler_files_->GetNumberOfErrors() > 0) |
841 |
|
✗ |
throw EPublish("cannot set replication mode"); |
842 |
|
|
} |
843 |
|
|
|
844 |
|
✗ |
void Publisher::Ingest() {} |
845 |
|
✗ |
void Publisher::Migrate() {} |
846 |
|
✗ |
void Publisher::Resign() {} |
847 |
|
✗ |
void Publisher::Rollback() {} |
848 |
|
✗ |
void Publisher::UpdateMetaInfo() {} |
849 |
|
|
|
850 |
|
✗ |
void Publisher::Transaction() { |
851 |
|
✗ |
TransactionRetry(); |
852 |
|
✗ |
session()->SetKeepAlive(true); |
853 |
|
|
} |
854 |
|
|
|
855 |
|
|
//------------------------------------------------------------------------------ |
856 |
|
|
|
857 |
|
|
|
858 |
|
✗ |
Replica::Replica(const SettingsReplica &settings) |
859 |
|
✗ |
: Repository(SettingsRepository(settings)) |
860 |
|
|
{ |
861 |
|
|
} |
862 |
|
|
|
863 |
|
|
|
864 |
|
✗ |
Replica::~Replica() { |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
} // namespace publish |
868 |
|
|
|