| 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 |
|
|
|