GCC Code Coverage Report


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