GCC Code Coverage Report


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