GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/publish/settings.cc
Date: 2026-08-30 02:40:36
Exec Total Coverage
Lines: 0 364 0.0%
Branches: 0 812 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5
6 #include "publish/settings.h"
7
8 #include <unistd.h>
9
10 #include <cstdlib>
11 #include <memory>
12 #include <string>
13 #include <vector>
14
15 #include "crypto/hash.h"
16 #include "options.h"
17 #include "publish/except.h"
18 #include "publish/repository.h"
19 #include "sanitizer.h"
20 #include "util/posix.h"
21 #include "util/string.h"
22
23 namespace publish {
24
25 void SettingsSpoolArea::UseSystemTempDir() {
26 if (getenv("TMPDIR") != NULL)
27 tmp_dir_ = getenv("TMPDIR");
28 else
29 tmp_dir_ = "/tmp";
30 }
31
32 void SettingsSpoolArea::SetSpoolArea(const std::string &path) {
33 workspace_ = path;
34 tmp_dir_ = workspace_() + "/tmp";
35 }
36
37 void SettingsSpoolArea::SetUnionMount(const std::string &path) {
38 union_mnt_ = path;
39 }
40
41 void SettingsSpoolArea::SetRepairMode(const EUnionMountRepairMode val) {
42 repair_mode_ = val;
43 }
44
45 void SettingsSpoolArea::EnsureDirectories() {
46 std::vector<std::string> targets;
47 targets.push_back(tmp_dir());
48 targets.push_back(readonly_mnt());
49 targets.push_back(scratch_dir());
50 targets.push_back(cache_dir());
51 targets.push_back(log_dir());
52 targets.push_back(ovl_work_dir());
53
54 for (unsigned i = 0; i < targets.size(); ++i) {
55 const bool rv = MkdirDeep(targets[i], 0700, true /* veryfy_writable */);
56 if (!rv)
57 throw publish::EPublish("cannot create directory " + targets[i]);
58 }
59 }
60
61
62 //------------------------------------------------------------------------------
63
64
65 void SettingsTransaction::SetLayoutRevision(const unsigned revision) {
66 layout_revision_ = revision;
67 }
68
69 void SettingsTransaction::SetInEnterSession(const bool value) {
70 in_enter_session_ = value;
71 }
72
73 void SettingsTransaction::SetBaseHash(const shash::Any &hash) {
74 base_hash_ = hash;
75 }
76
77 void SettingsTransaction::SetHashAlgorithm(const std::string &algorithm) {
78 hash_algorithm_ = shash::ParseHashAlgorithm(algorithm);
79 }
80
81 void SettingsTransaction::SetCompressionAlgorithm(
82 const std::string &algorithm) {
83 compression_algorithm_ = zlib::ParseCompressionAlgorithm(algorithm);
84 }
85
86 void SettingsTransaction::SetEnforceLimits(bool value) {
87 enforce_limits_ = value;
88 }
89
90 void SettingsTransaction::SetEnableMtimeNs(bool value) {
91 enable_mtime_ns_ = value;
92 }
93
94 void SettingsTransaction::SetLimitNestedCatalogKentries(unsigned value) {
95 limit_nested_catalog_kentries_ = value;
96 }
97
98 void SettingsTransaction::SetLimitRootCatalogKentries(unsigned value) {
99 limit_root_catalog_kentries_ = value;
100 }
101
102 void SettingsTransaction::SetLimitFileSizeMb(unsigned value) {
103 limit_file_size_mb_ = value;
104 }
105
106 void SettingsTransaction::SetUseCatalogAutobalance(bool value) {
107 use_catalog_autobalance_ = value;
108 }
109
110 void SettingsTransaction::SetAutobalanceMaxWeight(unsigned value) {
111 autobalance_max_weight_ = value;
112 }
113
114 void SettingsTransaction::SetAutobalanceMinWeight(unsigned value) {
115 autobalance_min_weight_ = value;
116 }
117
118 void SettingsTransaction::SetPrintChangeset(bool value) {
119 print_changeset_ = value;
120 }
121
122 void SettingsTransaction::SetDryRun(bool value) { dry_run_ = value; }
123
124 void SettingsTransaction::SetAllowNonexistentPath(bool value) {
125 allow_nonexistent_path_ = value;
126 }
127
128 void SettingsTransaction::SetUnionFsType(const std::string &union_fs) {
129 if (union_fs == "aufs") {
130 union_fs_ = kUnionFsAufs;
131 } else if ((union_fs == "overlay") || (union_fs == "overlayfs")) {
132 union_fs_ = kUnionFsOverlay;
133 } else if (union_fs == "tarball") {
134 union_fs_ = kUnionFsTarball;
135 } else {
136 throw EPublish("unsupported union file system: " + union_fs);
137 }
138 }
139
140 void SettingsTransaction::DetectUnionFsType() {
141 // TODO(jblomer): shall we switch the order?
142 if (DirectoryExists("/sys/fs/aufs")) {
143 union_fs_ = kUnionFsAufs;
144 return;
145 }
146 // TODO(jblomer): modprobe aufs, try again
147 if (DirectoryExists("/sys/module/overlay")) {
148 union_fs_ = kUnionFsOverlay;
149 return;
150 }
151 // TODO(jblomer): modprobe overlay, try again
152 throw EPublish("neither AUFS nor OverlayFS detected on the system");
153 }
154
155 bool SettingsTransaction::ValidateUnionFs() {
156 // TODO(jblomer)
157 return true;
158 }
159
160 void SettingsTransaction::SetTimeout(unsigned seconds) { timeout_s_ = seconds; }
161
162 int SettingsTransaction::GetTimeoutS() const {
163 if (timeout_s_.is_default())
164 return -1;
165 return timeout_s_();
166 }
167
168 void SettingsTransaction::SetLeasePath(const std::string &path) {
169 lease_path_ = path;
170 }
171
172 void SettingsTransaction::SetTemplate(const std::string &from,
173 const std::string &to) {
174 if (from.empty())
175 throw EPublish("template transaction's 'from' path must not be empty");
176 if (to.empty())
177 throw EPublish("template transaction's 'to' path must not be empty");
178 template_from_ = (from[0] == '/') ? from.substr(1) : from;
179 template_to_ = (to[0] == '/') ? to.substr(1) : to;
180 }
181
182 //------------------------------------------------------------------------------
183
184
185 std::string SettingsStorage::GetLocator() const {
186 return std::string(upload::SpoolerDefinition::kDriverNames[type_()]) + ","
187 + tmp_dir_() + "," + endpoint_();
188 }
189
190 void SettingsStorage::MakeS3(const std::string &s3_config,
191 const std::string &tmp_dir) {
192 type_ = upload::SpoolerDefinition::S3;
193 tmp_dir_ = tmp_dir;
194 endpoint_ = "cvmfs/" + fqrn_() + "@" + s3_config;
195 }
196
197 void SettingsStorage::MakeLocal(const std::string &path) {
198 type_ = upload::SpoolerDefinition::Local;
199 endpoint_ = path;
200 tmp_dir_ = path + "/data/txn";
201 }
202
203 void SettingsStorage::MakeGateway(const std::string &host,
204 unsigned int port,
205 const std::string &tmp_dir) {
206 type_ = upload::SpoolerDefinition::Gateway;
207 endpoint_ = "http://" + host + ":" + StringifyInt(port) + "/api/v1";
208 tmp_dir_ = tmp_dir;
209 }
210
211 void SettingsStorage::SetLocator(const std::string &locator) {
212 std::vector<std::string> tokens = SplitString(locator, ',');
213 if (tokens.size() != 3) {
214 throw EPublish("malformed storage locator, expected format is "
215 "<type>,<temporary directory>,<endpoint>");
216 }
217 if (tokens[0] == "local") {
218 type_ = upload::SpoolerDefinition::Local;
219 } else if (tokens[0] == "S3") {
220 type_ = upload::SpoolerDefinition::S3;
221 } else if (tokens[0] == "gw") {
222 type_ = upload::SpoolerDefinition::Gateway;
223 } else {
224 throw EPublish("unsupported storage type: " + tokens[0]);
225 }
226 tmp_dir_ = tokens[1];
227 endpoint_ = tokens[2];
228 }
229
230
231 //------------------------------------------------------------------------------
232
233 void SettingsKeychain::SetKeychainDir(const std::string &keychain_dir) {
234 keychain_dir_ = keychain_dir;
235 master_private_key_path_ = keychain_dir + "/" + fqrn_() + ".masterkey";
236 master_public_key_path_ = keychain_dir + "/" + fqrn_() + ".pub";
237 private_key_path_ = keychain_dir + "/" + fqrn_() + ".key";
238 certificate_path_ = keychain_dir + "/" + fqrn_() + ".crt";
239 gw_key_path_ = keychain_dir + "/" + fqrn_() + ".gw";
240 }
241
242
243 bool SettingsKeychain::HasDanglingMasterKeys() const {
244 return (FileExists(master_private_key_path_())
245 && !FileExists(master_public_key_path_()))
246 || (!FileExists(master_private_key_path_())
247 && FileExists(master_public_key_path_()));
248 }
249
250
251 bool SettingsKeychain::HasMasterKeys() const {
252 return FileExists(master_private_key_path_())
253 && FileExists(master_public_key_path_());
254 }
255
256
257 bool SettingsKeychain::HasDanglingRepositoryKeys() const {
258 return (FileExists(private_key_path_()) && !FileExists(certificate_path_()))
259 || (!FileExists(private_key_path_())
260 && FileExists(certificate_path_()));
261 }
262
263
264 bool SettingsKeychain::HasRepositoryKeys() const {
265 return FileExists(private_key_path_()) && FileExists(certificate_path_());
266 }
267
268 bool SettingsKeychain::HasGatewayKey() const {
269 return FileExists(gw_key_path_());
270 }
271
272 //------------------------------------------------------------------------------
273
274
275 SettingsRepository::SettingsRepository(
276 const SettingsPublisher &settings_publisher)
277 : fqrn_(settings_publisher.fqrn())
278 , url_(settings_publisher.url())
279 , proxy_(settings_publisher.proxy())
280 , tmp_dir_(settings_publisher.transaction().spool_area().tmp_dir())
281 , keychain_(settings_publisher.fqrn()) {
282 keychain_.SetKeychainDir(settings_publisher.keychain().keychain_dir());
283 }
284
285
286 SettingsRepository::SettingsRepository(const SettingsReplica &settings_replica)
287 : fqrn_(settings_replica.fqrn())
288 , url_(settings_replica.url())
289 , keychain_(settings_replica.fqrn()) { }
290
291
292 void SettingsRepository::SetUrl(const std::string &url) {
293 // TODO(jblomer): sanitiation, check availability
294 url_ = url;
295 }
296
297
298 void SettingsRepository::SetProxy(const std::string &proxy) { proxy_ = proxy; }
299
300
301 void SettingsRepository::SetTmpDir(const std::string &tmp_dir) {
302 tmp_dir_ = tmp_dir;
303 }
304
305
306 void SettingsRepository::SetCertBundle(const std::string &cert_bundle) {
307 cert_bundle_ = cert_bundle;
308 }
309
310
311 //------------------------------------------------------------------------------
312
313
314 const unsigned SettingsPublisher::kDefaultWhitelistValidity = 30;
315
316
317 SettingsPublisher::SettingsPublisher(
318 const SettingsRepository &settings_repository)
319 : fqrn_(settings_repository.fqrn())
320 , url_(settings_repository.url())
321 , proxy_(settings_repository.proxy())
322 , owner_uid_(0)
323 , owner_gid_(0)
324 , whitelist_validity_days_(kDefaultWhitelistValidity)
325 , is_silent_(false)
326 , is_managed_(false)
327 , storage_(fqrn_())
328 , transaction_(fqrn_())
329 , keychain_(fqrn_()) {
330 keychain_.SetKeychainDir(settings_repository.keychain().keychain_dir());
331 }
332
333
334 void SettingsPublisher::SetUrl(const std::string &url) {
335 // TODO(jblomer): sanitiation, check availability
336 url_ = url;
337 }
338
339
340 void SettingsPublisher::SetProxy(const std::string &proxy) { proxy_ = proxy; }
341
342
343 void SettingsPublisher::SetOwner(const std::string &user_name) {
344 const bool retval = GetUidOf(
345 user_name, owner_uid_.GetPtr(), owner_gid_.GetPtr());
346 if (!retval) {
347 throw EPublish("unknown user name for repository owner: " + user_name);
348 }
349 }
350
351 void SettingsPublisher::SetOwner(uid_t uid, gid_t gid) {
352 owner_uid_ = uid;
353 owner_gid_ = gid;
354 }
355
356 void SettingsPublisher::SetIsSilent(bool value) { is_silent_ = value; }
357
358 void SettingsPublisher::SetIsManaged(bool value) { is_managed_ = value; }
359
360 void SettingsPublisher::SetIgnoreInvalidLease(bool value) {
361 ignore_invalid_lease_ = value;
362 }
363
364
365 //------------------------------------------------------------------------------
366
367
368 SettingsBuilder::~SettingsBuilder() { delete options_mgr_; }
369
370
371 std::map<std::string, std::string> SettingsBuilder::GetSessionEnvironment() {
372 std::map<std::string, std::string> result;
373 const std::string session_dir = Env::GetEnterSessionDir();
374 if (session_dir.empty())
375 return result;
376
377 // Get the repository name from the ephemeral writable shell
378 BashOptionsManager omgr;
379 omgr.set_taint_environment(false);
380 omgr.ParsePath(session_dir + "/env.conf", false /* external */);
381
382 // We require at least CVMFS_FQRN to be set
383 std::string fqrn;
384 if (!omgr.GetValue("CVMFS_FQRN", &fqrn)) {
385 throw EPublish("no repositories found in ephemeral writable shell",
386 EPublish::kFailInvocation);
387 }
388
389 std::vector<std::string> keys = omgr.GetAllKeys();
390 for (unsigned i = 0; i < keys.size(); ++i) {
391 result[keys[i]] = omgr.GetValueOrDie(keys[i]);
392 }
393 return result;
394 }
395
396
397 std::string SettingsBuilder::GetSingleAlias() {
398 std::map<std::string, std::string> session_env = GetSessionEnvironment();
399 if (!session_env.empty())
400 return session_env["CVMFS_FQRN"];
401
402 std::vector<std::string> repositories = FindDirectories(config_path_);
403 if (repositories.empty()) {
404 throw EPublish("no repositories available in " + config_path_,
405 EPublish::kFailInvocation);
406 }
407
408 for (unsigned i = 0; i < repositories.size(); ++i) {
409 repositories[i] = GetFileName(repositories[i]);
410 }
411 if (repositories.size() > 1) {
412 throw EPublish("multiple repositories available in " + config_path_
413 + ":\n " + JoinStrings(repositories, "\n "),
414 EPublish::kFailInvocation);
415 }
416 return repositories[0];
417 }
418
419
420 SettingsRepository SettingsBuilder::CreateSettingsRepository(
421 const std::string &ident) {
422 if (HasPrefix(ident, "http://", true /* ignore case */)
423 || HasPrefix(ident, "https://", true /* ignore case */)
424 || HasPrefix(ident, "file://", true /* ignore case */)) {
425 const std::string fqrn = Repository::GetFqrnFromUrl(ident);
426 const sanitizer::RepositorySanitizer sanitizer;
427 if (!sanitizer.IsValid(fqrn)) {
428 throw EPublish("malformed repository name: " + fqrn);
429 }
430 SettingsRepository settings(fqrn);
431 settings.SetUrl(ident);
432 return settings;
433 }
434
435 const std::string alias = ident.empty() ? GetSingleAlias() : ident;
436 const std::string repo_path = config_path_ + "/" + alias;
437 const std::string server_path = repo_path + "/server.conf";
438 const std::string replica_path = repo_path + "/replica.conf";
439 std::string fqrn = alias;
440
441 delete options_mgr_;
442 options_mgr_ = new BashOptionsManager();
443 std::string arg;
444 options_mgr_->set_taint_environment(false);
445 options_mgr_->ParsePath("/etc/cvmfs/server.local", false /* external */);
446 options_mgr_->ParsePath(server_path, false /* external */);
447 options_mgr_->ParsePath(replica_path, false /* external */);
448 if (options_mgr_->GetValue("CVMFS_REPOSITORY_NAME", &arg))
449 fqrn = arg;
450 SettingsRepository settings(fqrn);
451
452 if (options_mgr_->GetValue("CVMFS_PUBLIC_KEY", &arg))
453 settings.GetKeychain()->SetKeychainDir(arg);
454 if (options_mgr_->GetValue("CVMFS_STRATUM0", &arg))
455 settings.SetUrl(arg);
456 if (options_mgr_->GetValue("CVMFS_SERVER_PROXY", &arg))
457 settings.SetProxy(arg);
458 // For a replica, the stratum 1 url is the "local" location, hence it takes
459 // precedence over the stratum 0 url
460 if (options_mgr_->GetValue("CVMFS_STRATUM1", &arg))
461 settings.SetUrl(arg);
462 if (options_mgr_->GetValue("CVMFS_SPOOL_DIR", &arg))
463 settings.SetTmpDir(arg + "/tmp");
464 if (options_mgr_->GetValue("X509_CERT_BUNDLE", &arg))
465 settings.SetCertBundle(arg);
466
467 return settings;
468 }
469
470 std::string SettingsPublisher::GetReadOnlyXAttr(const std::string &attr) {
471 std::string value;
472 const bool rvb = platform_getxattr(
473 this->transaction().spool_area().readonly_mnt(), attr, &value);
474 if (!rvb) {
475 throw EPublish("cannot get extended attribute " + attr);
476 }
477 return value;
478 }
479
480 SettingsPublisher *SettingsBuilder::CreateSettingsPublisherFromSession() {
481 const std::string session_dir = Env::GetEnterSessionDir();
482 std::map<std::string, std::string> session_env = GetSessionEnvironment();
483 const std::string fqrn = session_env["CVMFS_FQRN"];
484
485 std::unique_ptr<SettingsPublisher> settings_publisher(
486 new SettingsPublisher(SettingsRepository(fqrn)));
487 // TODO(jblomer): work in progress
488 settings_publisher->GetTransaction()->SetInEnterSession(true);
489 settings_publisher->GetTransaction()->GetSpoolArea()->SetSpoolArea(
490 session_dir);
491
492 const std::string base_hash = settings_publisher->GetReadOnlyXAttr(
493 "user.root_hash");
494
495 BashOptionsManager omgr;
496 omgr.set_taint_environment(false);
497 omgr.ParsePath(settings_publisher->transaction().spool_area().client_config(),
498 false /* external */);
499
500 std::string arg;
501 settings_publisher->SetUrl(settings_publisher->GetReadOnlyXAttr("user.host"));
502 settings_publisher->SetProxy(
503 settings_publisher->GetReadOnlyXAttr("user.proxy"));
504 if (omgr.GetValue("CVMFS_KEYS_DIR", &arg))
505 settings_publisher->GetKeychain()->SetKeychainDir(arg);
506 settings_publisher->GetTransaction()->SetLayoutRevision(
507 Publisher::kRequiredLayoutRevision);
508 settings_publisher->GetTransaction()->SetBaseHash(
509 shash::MkFromHexPtr(shash::HexPtr(base_hash), shash::kSuffixCatalog));
510 settings_publisher->GetTransaction()->SetUnionFsType("overlayfs");
511 settings_publisher->SetOwner(geteuid(), getegid());
512
513 return settings_publisher.release();
514 }
515
516 void SettingsBuilder::ApplyOptionsFromServerPath(
517 const OptionsManager &options_mgr_, SettingsPublisher *settings_publisher) {
518 std::string arg;
519 if (options_mgr_.GetValue("CVMFS_CREATOR_VERSION", &arg)) {
520 settings_publisher->GetTransaction()->SetLayoutRevision(String2Uint64(arg));
521 }
522 if (options_mgr_.GetValue("CVMFS_UNION_FS_TYPE", &arg)) {
523 settings_publisher->GetTransaction()->SetUnionFsType(arg);
524 }
525 if (options_mgr_.GetValue("CVMFS_HASH_ALGORITHM", &arg)) {
526 settings_publisher->GetTransaction()->SetHashAlgorithm(arg);
527 }
528 if (options_mgr_.GetValue("CVMFS_UPSTREAM_STORAGE", &arg)) {
529 settings_publisher->GetStorage()->SetLocator(arg);
530 }
531 if (options_mgr_.GetValue("CVMFS_KEYS_DIR", &arg)) {
532 settings_publisher->GetKeychain()->SetKeychainDir(arg);
533 }
534 if (options_mgr_.GetValue("CVMFS_COMPRESSION_ALGORITHM", &arg)) {
535 settings_publisher->GetTransaction()->SetCompressionAlgorithm(arg);
536 }
537 if (options_mgr_.GetValue("CVMFS_ENFORCE_LIMITS", &arg)) {
538 settings_publisher->GetTransaction()->SetEnforceLimits(
539 options_mgr_.IsOn(arg));
540 }
541 if (options_mgr_.GetValue("CVMFS_ENABLE_MTIME_NS", &arg)) {
542 settings_publisher->GetTransaction()->SetEnableMtimeNs(
543 options_mgr_.IsOn(arg));
544 }
545 if (options_mgr_.GetValue("CVMFS_NESTED_KCATALOG_LIMIT", &arg)) {
546 settings_publisher->GetTransaction()->SetLimitNestedCatalogKentries(
547 String2Uint64(arg));
548 }
549 if (options_mgr_.GetValue("CVMFS_ROOT_KCATALOG_LIMIT", &arg)) {
550 settings_publisher->GetTransaction()->SetLimitRootCatalogKentries(
551 String2Uint64(arg));
552 }
553 if (options_mgr_.GetValue("CVMFS_FILE_MBYTE_LIMIT", &arg)) {
554 settings_publisher->GetTransaction()->SetLimitFileSizeMb(
555 String2Uint64(arg));
556 }
557 if (options_mgr_.GetValue("CVMFS_AUTOCATALOGS", &arg)) {
558 settings_publisher->GetTransaction()->SetUseCatalogAutobalance(
559 options_mgr_.IsOn(arg));
560 }
561 if (options_mgr_.GetValue("CVMFS_AUTOCATALOGS_MAX_WEIGHT", &arg)) {
562 settings_publisher->GetTransaction()->SetAutobalanceMaxWeight(
563 String2Uint64(arg));
564 }
565 if (options_mgr_.GetValue("CVMFS_AUTOCATALOGS_MIN_WEIGHT", &arg)) {
566 settings_publisher->GetTransaction()->SetAutobalanceMinWeight(
567 String2Uint64(arg));
568 }
569 if (options_mgr_.GetValue("CVMFS_ALLOW_NONEXISTENT_PATH", &arg)) {
570 settings_publisher->GetTransaction()->SetAllowNonexistentPath(
571 options_mgr_.IsOn(arg));
572 }
573 if (options_mgr_.GetValue("CVMFS_AUTO_REPAIR_MOUNTPOINT", &arg)) {
574 if (!options_mgr_.IsOn(arg)) {
575 settings_publisher->GetTransaction()->GetSpoolArea()->SetRepairMode(
576 kUnionMountRepairNever);
577 }
578 }
579 }
580
581 SettingsPublisher *SettingsBuilder::CreateSettingsPublisher(
582 const std::string &ident, bool needs_managed) {
583 // we are creating a publisher, it need to have the `server.conf` file
584 // present, otherwise something is wrong and we should exit early
585 const std::string alias(ident.empty() ? GetSingleAlias() : ident);
586
587 std::map<std::string, std::string> session_env = GetSessionEnvironment();
588 // We can be in an ephemeral writable shell but interested in a different
589 // repository
590
591 const std::string server_path = config_path_ + "/" + alias + "/server.conf";
592
593 // Instead of returning the Settings from session, we need more processing
594 if (!session_env.empty() && (session_env["CVMFS_FQRN"] == alias)) {
595 SettingsPublisher
596 *settings_publisher = CreateSettingsPublisherFromSession();
597 if (FileExists(server_path)) {
598 delete options_mgr_;
599 options_mgr_ = new BashOptionsManager();
600 options_mgr_->set_taint_environment(false);
601 options_mgr_->ParsePath(server_path, false /* external */);
602 ApplyOptionsFromServerPath(*options_mgr_, settings_publisher);
603 }
604 return settings_publisher;
605 }
606
607 if (FileExists(server_path) == false) {
608 throw EPublish(
609 "Unable to find the configuration file `server.conf` for the cvmfs "
610 "publisher: "
611 + alias,
612 EPublish::kFailRepositoryNotFound);
613 }
614
615 const SettingsRepository settings_repository = CreateSettingsRepository(
616 alias);
617 if (needs_managed && !IsManagedRepository())
618 throw EPublish("remote repositories are not supported in this context");
619
620 if (options_mgr_->GetValueOrDie("CVMFS_REPOSITORY_TYPE") != "stratum0") {
621 throw EPublish("Repository " + alias + " is not a stratum 0 repository",
622 EPublish::kFailRepositoryType);
623 }
624
625 std::unique_ptr<SettingsPublisher> settings_publisher(
626 new SettingsPublisher(settings_repository));
627
628 try {
629 const std::string xattr = settings_publisher->GetReadOnlyXAttr(
630 "user.root_hash");
631 settings_publisher->GetTransaction()->SetBaseHash(
632 shash::MkFromHexPtr(shash::HexPtr(xattr), shash::kSuffixCatalog));
633 } catch (const EPublish &e) {
634 // We ignore the exception.
635 // In case of exception, the base hash remains unset.
636 }
637
638 settings_publisher->SetIsManaged(IsManagedRepository());
639 settings_publisher->SetOwner(options_mgr_->GetValueOrDie("CVMFS_USER"));
640 settings_publisher->GetStorage()->SetLocator(
641 options_mgr_->GetValueOrDie("CVMFS_UPSTREAM_STORAGE"));
642
643 ApplyOptionsFromServerPath(*options_mgr_, &*settings_publisher);
644
645 // TODO(jblomer): process other parameters
646 return settings_publisher.release();
647 }
648
649 } // namespace publish
650