GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/publish/repository.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 0 531 0.0%
Branches: 0 1163 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "cvmfs_config.h"
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 bool rvb = MkdirDeep(path, kPrivateDirMode);
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 kPrivateDirMode);
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 kPrivateDirMode);
587 }
588 if (!DirectoryExists(settings_.transaction().spool_area().union_mnt())) {
589 CreateDirectoryAsOwner(
590 settings_.transaction().spool_area().union_mnt(), kPrivateDirMode);
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 catalog_mgr_->Init();
696 }
697
698 if (sync_parameters_ == NULL) {
699 SyncParameters *p = new SyncParameters();
700 p->spooler = spooler_files_;
701 p->repo_name = settings_.fqrn();
702 p->dir_union = settings_.transaction().spool_area().union_mnt();
703 p->dir_scratch = settings_.transaction().spool_area().scratch_dir();
704 p->dir_rdonly = settings_.transaction().spool_area().readonly_mnt();
705 p->dir_temp = settings_.transaction().spool_area().tmp_dir();
706 p->base_hash = settings_.transaction().base_hash();
707 p->stratum0 = settings_.url();
708 // p->manifest_path = SHOULD NOT BE NEEDED
709 // p->spooler_definition = SHOULD NOT BE NEEDED;
710 // p->union_fs_type = SHOULD NOT BE NEEDED
711 p->print_changeset = settings_.transaction().print_changeset();
712 p->dry_run = settings_.transaction().dry_run();
713 sync_parameters_ = p;
714 }
715
716 if (sync_mediator_ == NULL) {
717 sync_mediator_ =
718 new SyncMediator(catalog_mgr_, sync_parameters_, *statistics_publish_);
719 }
720
721 if (sync_union_ == NULL) {
722 switch (settings_.transaction().union_fs()) {
723 case kUnionFsAufs:
724 sync_union_ = new publish::SyncUnionAufs(
725 sync_mediator_,
726 settings_.transaction().spool_area().readonly_mnt(),
727 settings_.transaction().spool_area().union_mnt(),
728 settings_.transaction().spool_area().scratch_dir());
729 break;
730 case kUnionFsOverlay:
731 sync_union_ = new publish::SyncUnionOverlayfs(
732 sync_mediator_,
733 settings_.transaction().spool_area().readonly_mnt(),
734 settings_.transaction().spool_area().union_mnt(),
735 settings_.transaction().spool_area().scratch_dir());
736 break;
737 case kUnionFsTarball:
738 sync_union_ = new publish::SyncUnionTarball(
739 sync_mediator_,
740 settings_.transaction().spool_area().readonly_mnt(),
741 // TODO(jblomer): get from settings
742 "tar_file",
743 "base_directory",
744 -1u,
745 -1u,
746 "to_delete",
747 false /* create_catalog */);
748 break;
749 default:
750 throw EPublish("unknown union file system");
751 }
752 bool rvb = sync_union_->Initialize();
753 if (!rvb) {
754 delete sync_union_;
755 sync_union_ = NULL;
756 throw EPublish("cannot initialize union file system engine");
757 }
758 }
759 }
760
761 void Publisher::ExitShell() {
762 std::string session_dir = Env::GetEnterSessionDir();
763 std::string session_pid_tmp = session_dir + "/session_pid";
764 std::string session_pid;
765 int fd_session_pid = open(session_pid_tmp.c_str(), O_RDONLY);
766 if (fd_session_pid < 0) throw EPublish("Session pid cannot be retrieved");
767 SafeReadToString(fd_session_pid, &session_pid);
768
769 pid_t pid_child = String2Uint64(session_pid);
770 kill(pid_child, SIGUSR1);
771 }
772
773 void Publisher::Sync() {
774 ServerLockFileGuard g(is_publishing_);
775
776 ConstructSyncManagers();
777
778 sync_union_->Traverse();
779 bool rvb = sync_mediator_->Commit(manifest_);
780 if (!rvb) throw EPublish("cannot write change set to storage");
781
782 if (!settings_.transaction().dry_run()) {
783 spooler_files_->WaitForUpload();
784 spooler_catalogs_->WaitForUpload();
785 spooler_files_->FinalizeSession(false /* commit */);
786
787 const std::string old_root_hash =
788 settings_.transaction().base_hash().ToString(true /* with_suffix */);
789 const std::string new_root_hash =
790 manifest_->catalog_hash().ToString(true /* with_suffix */);
791 rvb = spooler_catalogs_->FinalizeSession(true /* commit */,
792 old_root_hash, new_root_hash,
793 /* TODO(jblomer) */ sync_parameters_->repo_tag);
794 if (!rvb)
795 throw EPublish("failed to commit transaction");
796
797 // Reset to the new catalog root hash
798 settings_.GetTransaction()->SetBaseHash(manifest_->catalog_hash());
799 // TODO(jblomer): think about how to deal with the scratch area at
800 // this point
801 // WipeScratchArea();
802 }
803
804 delete sync_union_;
805 delete sync_mediator_;
806 delete sync_parameters_;
807 delete catalog_mgr_;
808 sync_union_ = NULL;
809 sync_mediator_ = NULL;
810 sync_parameters_ = NULL;
811 catalog_mgr_ = NULL;
812
813 if (!settings_.transaction().dry_run()) {
814 LogCvmfs(kLogCvmfs, kLogStdout, "New revision: %" PRIu64,
815 manifest_->revision());
816 reflog_->AddCatalog(manifest_->catalog_hash());
817 }
818 }
819
820 void Publisher::Publish() {
821 if (!in_transaction_.IsSet())
822 throw EPublish("cannot publish outside transaction");
823
824 PushReflog();
825 PushManifest();
826 in_transaction_.Clear();
827 }
828
829
830 void Publisher::MarkReplicatible(bool value) {
831 ConstructSpoolers();
832
833 if (value) {
834 spooler_files_->Upload("/dev/null", "/.cvmfs_master_replica");
835 } else {
836 spooler_files_->RemoveAsync("/.cvmfs_master_replica");
837 }
838 spooler_files_->WaitForUpload();
839 if (spooler_files_->GetNumberOfErrors() > 0)
840 throw EPublish("cannot set replication mode");
841 }
842
843 void Publisher::Ingest() {}
844 void Publisher::Migrate() {}
845 void Publisher::Resign() {}
846 void Publisher::Rollback() {}
847 void Publisher::UpdateMetaInfo() {}
848
849 void Publisher::Transaction() {
850 TransactionRetry();
851 session()->SetKeepAlive(true);
852 }
853
854 //------------------------------------------------------------------------------
855
856
857 Replica::Replica(const SettingsReplica &settings)
858 : Repository(SettingsRepository(settings))
859 {
860 }
861
862
863 Replica::~Replica() {
864 }
865
866 } // namespace publish
867