GCC Code Coverage Report


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