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