GCC Code Coverage Report


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

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