GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/publish/repository_managed.cc
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 0 223 0.0%
Branches: 0 357 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5
6 #include <cstdio>
7
8 #include "crypto/hash.h"
9 #include "manifest.h"
10 #include "publish/except.h"
11 #include "publish/repository.h"
12 #include "publish/repository_util.h"
13 #include "upload.h"
14 #include "upload_spooler_definition.h"
15 #include "util/pointer.h"
16 #include "util/posix.h"
17 #include "util/string.h"
18
19
20 namespace publish {
21
22 void Publisher::ManagedNode::Open() {
23 AlterMountpoint(kAlterUnionOpen, kLogSyslog);
24 }
25
26
27 void Publisher::ManagedNode::Lock() {
28 AlterMountpoint(kAlterUnionLock, kLogSyslog);
29 }
30
31
32 void Publisher::ManagedNode::Unmount() {
33 try {
34 AlterMountpoint(kAlterUnionUnmount, kLogSyslog);
35 } catch (const EPublish &e) {
36 AlterMountpoint(kAlterUnionLazyUnmount, kLogSyslog);
37 AlterMountpoint(kAlterRdOnlyKillUnmount, kLogSyslog);
38 AlterMountpoint(kAlterRdOnlyLazyUnmount, kLogSyslog);
39 return;
40 }
41
42 try {
43 AlterMountpoint(kAlterRdOnlyUnmount, kLogSyslog);
44 } catch (const EPublish &e) {
45 AlterMountpoint(kAlterRdOnlyKillUnmount, kLogSyslog);
46 AlterMountpoint(kAlterRdOnlyLazyUnmount, kLogSyslog);
47 }
48 }
49
50 void Publisher::ManagedNode::Mount() {
51 AlterMountpoint(kAlterRdOnlyMount, kLogSyslog);
52 AlterMountpoint(kAlterUnionMount, kLogSyslog);
53 }
54
55 void Publisher::ManagedNode::ClearScratch() {
56 const std::string scratch_dir = publisher_->settings_.transaction()
57 .spool_area()
58 .scratch_dir();
59 const std::string scratch_wastebin = publisher_->settings_.transaction()
60 .spool_area()
61 .scratch_wastebin();
62 const std::string
63 tmp_dir = publisher_->settings_.transaction().spool_area().tmp_dir();
64
65 const std::string waste_dir = CreateTempDir(scratch_wastebin + "/waste");
66 if (waste_dir.empty())
67 throw EPublish("cannot create wastebin directory");
68 const int rvi =
69 rename(scratch_dir.c_str(), (waste_dir + "/delete-me").c_str());
70 if (rvi != 0)
71 throw EPublish("cannot move scratch directory to wastebin");
72
73 publisher_->CreateDirectoryAsOwner(scratch_dir, kDefaultDirMode);
74
75 AlterMountpoint(kAlterScratchWipe, kLogSyslog);
76
77 std::vector<mode_t> modes;
78 std::vector<std::string> names;
79 ListDirectory(tmp_dir, &names, &modes);
80 for (unsigned i = 0; i < names.size(); ++i) {
81 if (HasPrefix(names[i], "receiver.", false /* ignore_case */))
82 continue;
83
84 unlink((tmp_dir + "/" + names[i]).c_str());
85 }
86 }
87
88
89 int Publisher::ManagedNode::Check(bool is_quiet) {
90 const ServerLockFileCheck publish_check(publisher_->is_publishing_);
91 const std::string rdonly_mnt = publisher_->settings_.transaction()
92 .spool_area()
93 .readonly_mnt();
94 const std::string
95 union_mnt = publisher_->settings_.transaction().spool_area().union_mnt();
96 const std::string fqrn = publisher_->settings_.fqrn();
97 const EUnionMountRepairMode repair_mode =
98 publisher_->settings_.transaction().spool_area().repair_mode();
99
100 int result = kFailOk;
101
102 shash::Any expected_hash = publisher_->manifest()->catalog_hash();
103 const UniquePtr<CheckoutMarker> marker(CheckoutMarker::CreateFrom(
104 publisher_->settings_.transaction().spool_area().checkout_marker()));
105 if (marker.IsValid())
106 expected_hash = marker->hash();
107
108 if (!IsMountPoint(rdonly_mnt)) {
109 result |= kFailRdOnlyBroken;
110 } else {
111 const std::string root_hash_xattr = "user.root_hash";
112 std::string root_hash_str;
113 const bool retval =
114 platform_getxattr(rdonly_mnt, root_hash_xattr, &root_hash_str);
115 if (retval) {
116 const shash::Any root_hash = shash::MkFromHexPtr(
117 shash::HexPtr(root_hash_str), shash::kSuffixCatalog);
118 if (expected_hash != root_hash) {
119 if (marker.IsValid()) {
120 result |= kFailRdOnlyWrongRevision;
121 } else {
122 result |= kFailRdOnlyOutdated;
123 }
124 }
125 } else {
126 if (errno == ENOTCONN) {
127 // Fuse module crashed
128 result |= kFailRdOnlyBroken;
129 } else {
130 throw EPublish("cannot retrieve root hash from read-only mount point");
131 }
132 }
133 }
134
135 // The process that opens the transaction does not stay alive for the life
136 // time of the transaction
137 if (!IsMountPoint(union_mnt)) {
138 result |= kFailUnionBroken;
139 } else {
140 const FileSystemInfo fs_info = GetFileSystemInfo(union_mnt);
141 if (publisher_->in_transaction_.IsSet() && fs_info.is_rdonly)
142 result |= kFailUnionLocked;
143 if (!publisher_->in_transaction_.IsSet() && !fs_info.is_rdonly)
144 result |= kFailUnionWritable;
145 }
146
147 if (result == kFailOk)
148 return result;
149
150 // Report & Repair
151
152 int logFlags = kLogStderr;
153 if (is_quiet)
154 logFlags |= kLogNone;
155 if (result & kFailRdOnlyBroken) {
156 LogCvmfs(kLogCvmfs, logFlags, "%s is not mounted properly",
157 rdonly_mnt.c_str());
158 }
159 if (result & kFailRdOnlyOutdated) {
160 LogCvmfs(kLogCvmfs, logFlags,
161 "%s is not based on the newest published revision", fqrn.c_str());
162 }
163 if (result & kFailRdOnlyWrongRevision) {
164 LogCvmfs(kLogCvmfs, logFlags, "%s is not based on the checked out revision",
165 fqrn.c_str());
166 }
167 if (result & kFailUnionBroken) {
168 LogCvmfs(kLogCvmfs, logFlags, "%s is not mounted properly",
169 union_mnt.c_str());
170 }
171 if (result & kFailUnionWritable) {
172 LogCvmfs(kLogCvmfs, logFlags,
173 "%s is not in a transaction but %s is mounted read/write",
174 fqrn.c_str(), union_mnt.c_str());
175 }
176 if (result & kFailUnionLocked) {
177 LogCvmfs(kLogCvmfs, logFlags,
178 "%s is in a transaction but %s is not mounted read/write",
179 fqrn.c_str(), union_mnt.c_str());
180 }
181
182 // Check whether we can repair
183
184 switch (repair_mode) {
185 case kUnionMountRepairNever:
186 return result;
187 case kUnionMountRepairAlways:
188 break;
189 case kUnionMountRepairSafe:
190 if (!publish_check.owns_lock()) {
191 LogCvmfs(
192 kLogCvmfs, logFlags,
193 "WARNING: The repository %s is currently publishing and should "
194 "not\n"
195 "be touched. If you are absolutely sure, that this is _not_ the "
196 "case,\nplease run the following command and retry:\n\n"
197 " rm -fR %s\n",
198 fqrn.c_str(), publisher_->is_publishing_.path().c_str());
199 return result;
200 }
201
202 if (publisher_->in_transaction_.IsSet()) {
203 LogCvmfs(kLogCvmfs, logFlags,
204 "Repository %s is in a transaction and cannot be repaired.\n"
205 "--> Run `cvmfs_server abort $name` to revert and repair.",
206 fqrn.c_str());
207 return result;
208 }
209
210 break;
211 default:
212 abort();
213 }
214
215 LogCvmfs(kLogCvmfs, kLogSyslog, "(%s) attempting mountpoint repair (%d)",
216 fqrn.c_str(), result);
217
218 // consecutively bring the mountpoints into a sane state by working bottom up:
219 // 1. solve problems with the rdonly mountpoint
220 // Note: this might require to 'break' the union mount
221 // (kFailUnionBroken -> 1)
222 // 1.1. solve outdated rdonly mountpoint (kFailRdOnlyOutdated -> 0)
223 // 1.2. remount rdonly mountpoint (kFailRdOnlyBroken -> 0)
224 // 2. solve problems with the union mountpoint
225 // 2.1. mount the union mountpoint read-only (kFailUnionBroken -> 0)
226 // 2.2. remount the union mountpoint read-only (kFailUnionWritable -> 0)
227 // 2.2. remount the union mountpoint read-write (kFailUnionLocked -> 0)
228
229 int log_flags = kLogSyslog;
230 if (!is_quiet)
231 log_flags |= kLogStderr;
232
233 if ((result & kFailRdOnlyOutdated) || (result & kFailRdOnlyWrongRevision)) {
234 if ((result & kFailUnionBroken) == 0) {
235 AlterMountpoint(kAlterUnionUnmount, log_flags);
236 result |= kFailUnionBroken;
237 }
238
239 if ((result & kFailRdOnlyBroken) == 0) {
240 AlterMountpoint(kAlterRdOnlyUnmount, log_flags);
241 result |= kFailRdOnlyBroken;
242 }
243
244 SetRootHash(expected_hash);
245 result &= ~kFailRdOnlyOutdated;
246 result &= ~kFailRdOnlyWrongRevision;
247 }
248
249 if (result & kFailRdOnlyBroken) {
250 if ((result & kFailUnionBroken) == 0) {
251 AlterMountpoint(kAlterUnionUnmount, log_flags);
252 result |= kFailUnionBroken;
253 }
254 AlterMountpoint(kAlterRdOnlyMount, log_flags);
255 result &= ~kFailRdOnlyBroken;
256 }
257
258 if (result & kFailUnionBroken) {
259 AlterMountpoint(kAlterUnionMount, log_flags);
260 // read-only mount by default
261 if (publisher_->in_transaction_.IsSet())
262 result |= kFailUnionLocked;
263
264 result &= ~kFailUnionBroken;
265 result &= ~kFailUnionWritable;
266 }
267
268 if (result & kFailUnionLocked) {
269 AlterMountpoint(kAlterUnionOpen, log_flags);
270 result &= ~kFailUnionLocked;
271 }
272
273 if (result & kFailUnionWritable) {
274 AlterMountpoint(kAlterUnionLock, log_flags);
275 result &= ~kFailUnionWritable;
276 }
277
278 LogCvmfs(kLogCvmfs, kLogSyslog, "finished mountpoint repair (%d)", result);
279
280 return result;
281 }
282
283 void Publisher::ManagedNode::AlterMountpoint(EMountpointAlterations how,
284 int log_level) {
285 std::string mountpoint;
286 std::string info_msg;
287 std::string suid_helper_verb;
288 switch (how) {
289 case kAlterUnionUnmount:
290 mountpoint = publisher_->settings_.transaction().spool_area().union_mnt();
291 info_msg = "Trying to unmount " + mountpoint;
292 suid_helper_verb = "rw_umount";
293 break;
294 case kAlterUnionLazyUnmount:
295 mountpoint = publisher_->settings_.transaction().spool_area().union_mnt();
296 info_msg = "Trying to lazily unmount " + mountpoint;
297 suid_helper_verb = "rw_lazy_umount";
298 break;
299 case kAlterRdOnlyUnmount:
300 mountpoint = publisher_->settings_.transaction()
301 .spool_area()
302 .readonly_mnt();
303 info_msg = "Trying to unmount " + mountpoint;
304 suid_helper_verb = "rdonly_umount";
305 break;
306 case kAlterRdOnlyKillUnmount:
307 mountpoint = publisher_->settings_.transaction()
308 .spool_area()
309 .readonly_mnt();
310 info_msg = "Trying to forcefully stop " + mountpoint;
311 suid_helper_verb = "kill_cvmfs";
312 break;
313 case kAlterRdOnlyLazyUnmount:
314 mountpoint = publisher_->settings_.transaction()
315 .spool_area()
316 .readonly_mnt();
317 info_msg = "Trying to lazily unmount " + mountpoint;
318 suid_helper_verb = "rdonly_lazy_umount";
319 break;
320 case kAlterUnionMount:
321 mountpoint = publisher_->settings_.transaction().spool_area().union_mnt();
322 info_msg = "Trying to mount " + mountpoint;
323 suid_helper_verb = "rw_mount";
324 break;
325 case kAlterRdOnlyMount:
326 mountpoint = publisher_->settings_.transaction()
327 .spool_area()
328 .readonly_mnt();
329 info_msg = "Trying to mount " + mountpoint;
330 suid_helper_verb = "rdonly_mount";
331 break;
332 case kAlterUnionOpen:
333 mountpoint = publisher_->settings_.transaction().spool_area().union_mnt();
334 info_msg = "Trying to remount " + mountpoint + " read/write";
335 suid_helper_verb = "open";
336 break;
337 case kAlterUnionLock:
338 mountpoint = publisher_->settings_.transaction().spool_area().union_mnt();
339 info_msg = "Trying to remount " + mountpoint + " read-only";
340 suid_helper_verb = "lock";
341 break;
342 case kAlterScratchWipe:
343 mountpoint = publisher_->settings_.transaction()
344 .spool_area()
345 .scratch_dir();
346 info_msg = "Trying to wipe out " + mountpoint + " (async cleanup)";
347 suid_helper_verb = "clear_scratch_async";
348 break;
349 default:
350 throw EPublish("internal error: unknown mountpoint alteration");
351 }
352
353 if (log_level & kLogStdout) {
354 LogCvmfs(kLogCvmfs, kLogStderr | kLogNoLinebreak, "Note: %s... ",
355 info_msg.c_str());
356 }
357
358 try {
359 RunSuidHelper(suid_helper_verb, publisher_->settings_.fqrn());
360 LogCvmfs(kLogCvmfs, (log_level & ~kLogStdout), "%s... success",
361 info_msg.c_str());
362 if (log_level & kLogStdout)
363 LogCvmfs(kLogCvmfs, kLogStdout, "success");
364 } catch (const EPublish &) {
365 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "%s... fail",
366 info_msg.c_str());
367 throw EPublish(info_msg + "... fail");
368 }
369 }
370
371
372 void Publisher::ManagedNode::SetRootHash(const shash::Any &hash) {
373 const std::string config_path = publisher_->settings_.transaction()
374 .spool_area()
375 .client_lconfig();
376 SetInConfig(config_path, "CVMFS_ROOT_HASH", hash.ToString());
377 }
378
379 } // namespace publish
380