GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_extern.cc
Date: 2026-10-25 02:40:14
Exec Total Coverage
Lines: 551 717 76.8%
Branches: 369 819 45.1%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "cache_extern.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <inttypes.h>
10 #include <stdint.h>
11 #include <sys/socket.h>
12 #include <unistd.h>
13
14 #include <algorithm>
15 #include <cassert>
16 #ifdef __APPLE__
17 #include <cstdlib>
18 #endif
19 #include <atomic>
20 #include <cstring>
21 #include <map>
22 #include <memory>
23 #include <new>
24 #include <set>
25 #include <string>
26
27 #include "cache.pb.h"
28 #include "crypto/hash.h"
29 #include "util/concurrency.h"
30 #include "util/exception.h"
31 #include "util/logging.h"
32 #include "util/posix.h"
33 #ifdef __APPLE__
34 #include "util/smalloc.h"
35 #endif
36 #include "util/string.h"
37
38 using namespace std; // NOLINT
39
40 namespace {
41
42 43853 int Ack2Errno(cvmfs::EnumStatus status_code) {
43
4/12
✓ Branch 0 taken 43709 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 41 times.
✓ Branch 5 taken 66 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 37 times.
✗ Branch 11 not taken.
43853 switch (status_code) {
44 43709 case cvmfs::STATUS_OK:
45 43709 return 0;
46 ✗ case cvmfs::STATUS_NOSUPPORT:
47 ✗ return -EOPNOTSUPP;
48 ✗ case cvmfs::STATUS_FORBIDDEN:
49 ✗ return -EPERM;
50 ✗ case cvmfs::STATUS_NOSPACE:
51 ✗ return -ENOSPC;
52 41 case cvmfs::STATUS_NOENTRY:
53 41 return -ENOENT;
54 66 case cvmfs::STATUS_MALFORMED:
55 66 return -EINVAL;
56 ✗ case cvmfs::STATUS_IOERR:
57 ✗ return -EIO;
58 ✗ case cvmfs::STATUS_CORRUPTED:
59 ✗ return -EIO;
60 ✗ case cvmfs::STATUS_TIMEOUT:
61 ✗ return -EIO;
62 ✗ case cvmfs::STATUS_BADCOUNT:
63 ✗ return -EINVAL;
64 37 case cvmfs::STATUS_OUTOFBOUNDS:
65 37 return -EINVAL;
66 ✗ default:
67 ✗ return -EIO;
68 }
69 }
70
71 } // anonymous namespace
72
73 const shash::Any ExternalCacheManager::kInvalidHandle;
74
75
76 ✗ int ExternalCacheManager::AbortTxn(void *txn) {
77 ✗ const int result = Reset(txn);
78 #ifdef __APPLE__
79 free(reinterpret_cast<Transaction *>(txn)->buffer);
80 #endif
81 ✗ return result;
82 }
83
84
85 618 bool ExternalCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) {
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 618 times.
618 assert(quota_mgr != NULL);
87 618 quota_mgr_ = quota_mgr;
88 594 LogCvmfs(kLogCache, kLogDebug, "set quota manager");
89 618 return true;
90 }
91
92
93 118483 void ExternalCacheManager::CallRemotely(ExternalCacheManager::RpcJob *rpc_job) {
94
2/2
✓ Branch 0 taken 51592 times.
✓ Branch 1 taken 66891 times.
118483 if (!spawned_) {
95 51592 transport_.SendFrame(rpc_job->frame_send());
96 51592 const uint32_t save_att_size = rpc_job->frame_recv()->att_size();
97 bool again;
98
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 51592 times.
51625 do {
99 51625 again = false;
100 51625 const bool retval = transport_.RecvFrame(rpc_job->frame_recv());
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51625 times.
51625 assert(retval);
102
2/2
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 51592 times.
51625 if (rpc_job->frame_recv()->IsMsgOutOfBand()) {
103 google::protobuf::MessageLite *msg_typed = rpc_job->frame_recv()
104 33 ->GetMsgTyped();
105
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
33 assert(msg_typed->GetTypeName() == "cvmfs.MsgDetach");
106
2/4
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 33 times.
✗ Branch 6 not taken.
33 quota_mgr_->BroadcastBackchannels("R"); // release pinned catalogs
107 33 rpc_job->frame_recv()->Reset(save_att_size);
108 33 again = true;
109 }
110 } while (again);
111 } else {
112
1/2
✓ Branch 1 taken 66891 times.
✗ Branch 2 not taken.
66891 Signal signal;
113 {
114 66891 const MutexLockGuard guard(lock_inflight_rpcs_);
115
1/2
✓ Branch 2 taken 66924 times.
✗ Branch 3 not taken.
66924 inflight_rpcs_.push_back(RpcInFlight(rpc_job, &signal));
116 66924 }
117 {
118 66924 const MutexLockGuard guard(lock_send_fd_);
119
1/2
✓ Branch 2 taken 66924 times.
✗ Branch 3 not taken.
66924 transport_.SendFrame(rpc_job->frame_send());
120 66924 }
121
1/2
✓ Branch 1 taken 66924 times.
✗ Branch 2 not taken.
66924 signal.Wait();
122 66924 }
123 118516 }
124
125
126 21116 int ExternalCacheManager::ChangeRefcount(const shash::Any &id, int change_by) {
127
1/2
✓ Branch 1 taken 21116 times.
✗ Branch 2 not taken.
21116 cvmfs::MsgHash object_id;
128
1/2
✓ Branch 1 taken 21116 times.
✗ Branch 2 not taken.
21116 transport_.FillMsgHash(id, &object_id);
129
1/2
✓ Branch 1 taken 21116 times.
✗ Branch 2 not taken.
21116 cvmfs::MsgRefcountReq msg_refcount;
130
1/2
✓ Branch 1 taken 21116 times.
✗ Branch 2 not taken.
21116 msg_refcount.set_session_id(session_id_);
131
1/2
✓ Branch 2 taken 21116 times.
✗ Branch 3 not taken.
21116 msg_refcount.set_req_id(NextRequestId());
132
1/2
✓ Branch 1 taken 21083 times.
✗ Branch 2 not taken.
21116 msg_refcount.set_allocated_object_id(&object_id);
133
1/2
✓ Branch 1 taken 21083 times.
✗ Branch 2 not taken.
21083 msg_refcount.set_change_by(change_by);
134
1/2
✓ Branch 1 taken 21083 times.
✗ Branch 2 not taken.
21083 RpcJob rpc_job(&msg_refcount);
135
1/2
✓ Branch 1 taken 21116 times.
✗ Branch 2 not taken.
21083 CallRemotely(&rpc_job);
136
1/2
✓ Branch 1 taken 21116 times.
✗ Branch 2 not taken.
21116 (void)msg_refcount.release_object_id();
137
138
1/2
✓ Branch 1 taken 21116 times.
✗ Branch 2 not taken.
21116 cvmfs::MsgRefcountReply *msg_reply = rpc_job.msg_refcount_reply();
139 42232 return Ack2Errno(msg_reply->status());
140 21116 }
141
142
143 9441 int ExternalCacheManager::Close(int fd) {
144 9441 ReadOnlyHandle handle;
145 {
146 9441 const WriteLockGuard guard(rwlock_fd_table_);
147
1/2
✓ Branch 1 taken 9441 times.
✗ Branch 2 not taken.
9441 handle = fd_table_.GetHandle(fd);
148
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 9408 times.
9441 if (handle.id == kInvalidHandle)
149 33 return -EBADF;
150
1/2
✓ Branch 1 taken 9408 times.
✗ Branch 2 not taken.
9408 const int retval = fd_table_.CloseFd(fd);
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9408 times.
9408 assert(retval == 0);
152
2/2
✓ Branch 1 taken 9408 times.
✓ Branch 2 taken 33 times.
9441 }
153
154
1/2
✓ Branch 1 taken 9408 times.
✗ Branch 2 not taken.
9408 return ChangeRefcount(handle.id, -1);
155 }
156
157
158 2230 int ExternalCacheManager::CommitTxn(void *txn) {
159 2230 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
160
1/2
✓ Branch 2 taken 198 times.
✗ Branch 3 not taken.
198 LogCvmfs(kLogCache, kLogDebug, "committing %s",
161 396 std::string(transaction->id.ToString()).c_str());
162 2230 const int retval = Flush(true, transaction);
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2230 times.
2230 if (retval != 0)
164 ✗ return retval;
165
166 2230 const int refcount = transaction->open_fds - 1;
167
2/2
✓ Branch 0 taken 2228 times.
✓ Branch 1 taken 2 times.
2230 if (refcount != 0)
168 2228 return ChangeRefcount(transaction->id, refcount);
169 #ifdef __APPLE__
170 free(transaction->buffer);
171 #endif
172 2 return 0;
173 }
174
175
176 ✗ int ExternalCacheManager::ConnectLocator(const std::string &locator,
177 bool print_error) {
178 ✗ vector<string> tokens = SplitString(locator, '=');
179 ✗ int result = -1;
180 ✗ if (tokens[0] == "unix") {
181 ✗ result = ConnectSocket(tokens[1]);
182 ✗ } else if (tokens[0] == "tcp") {
183 ✗ vector<string> tcp_address = SplitString(tokens[1], ':');
184 ✗ if (tcp_address.size() != 2)
185 ✗ return -EINVAL;
186 ✗ result = ConnectTcpEndpoint(tcp_address[0], String2Uint64(tcp_address[1]));
187 ✗ } else {
188 ✗ return -EINVAL;
189 }
190 ✗ if (result < 0) {
191 ✗ if (print_error) {
192 ✗ if (errno) {
193 ✗ LogCvmfs(kLogCache, kLogDebug | kLogStderr,
194 ✗ "Failed to connect to socket: %s", strerror(errno));
195 } else {
196 ✗ LogCvmfs(kLogCache, kLogDebug | kLogStderr,
197 "Failed to connect to socket (unknown error)");
198 }
199 }
200 ✗ return -EIO;
201 }
202 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslog, "connected to cache plugin at %s",
203 locator.c_str());
204 ✗ return result;
205 }
206
207
208 620 ExternalCacheManager *ExternalCacheManager::Create(int fd_connection,
209 unsigned max_open_fds,
210 const string &ident) {
211 std::unique_ptr<ExternalCacheManager> cache_mgr(
212
2/4
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 620 times.
✗ Branch 5 not taken.
620 new ExternalCacheManager(fd_connection, max_open_fds));
213
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 620 times.
620 assert(cache_mgr.get() != nullptr);
214
215
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 cvmfs::MsgHandshake msg_handshake;
216
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 msg_handshake.set_protocol_version(kPbProtocolVersion);
217 msg_handshake.set_name(ident);
218
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 CacheTransport::Frame frame_send(&msg_handshake);
219
1/2
✓ Branch 2 taken 620 times.
✗ Branch 3 not taken.
620 cache_mgr->transport_.SendFrame(&frame_send);
220
221
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 CacheTransport::Frame frame_recv;
222
1/2
✓ Branch 2 taken 620 times.
✗ Branch 3 not taken.
620 const bool retval = cache_mgr->transport_.RecvFrame(&frame_recv);
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 620 times.
620 if (!retval)
224 ✗ return NULL;
225
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 google::protobuf::MessageLite *msg_typed = frame_recv.GetMsgTyped();
226
2/4
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 620 times.
620 if (msg_typed->GetTypeName() != "cvmfs.MsgHandshakeAck")
227 ✗ return NULL;
228 620 cvmfs::MsgHandshakeAck *msg_ack = reinterpret_cast<cvmfs::MsgHandshakeAck *>(
229 msg_typed);
230 620 cache_mgr->session_id_ = msg_ack->session_id();
231 620 cache_mgr->capabilities_ = msg_ack->capabilities();
232 620 cache_mgr->max_object_size_ = msg_ack->max_object_size();
233
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 620 times.
620 assert(cache_mgr->max_object_size_ > 0);
234
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 620 times.
620 if (cache_mgr->max_object_size_ > kMaxSupportedObjectSize) {
235 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
236 "external cache manager object size too large (%u)",
237 ✗ cache_mgr->max_object_size_);
238 ✗ return NULL;
239 }
240
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 620 times.
620 if (cache_mgr->max_object_size_ < kMinSupportedObjectSize) {
241 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
242 "external cache manager object size too small (%u)",
243 ✗ cache_mgr->max_object_size_);
244 ✗ return NULL;
245 }
246
3/4
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 594 times.
✓ Branch 4 taken 26 times.
620 if (msg_ack->has_pid())
247 594 cache_mgr->pid_plugin_ = msg_ack->pid();
248 620 return cache_mgr.release();
249 620 }
250
251
252 /**
253 * Tries to connect to the plugin at locator, or, if it doesn't exist, spawns
254 * a new plugin using cmdline. Two processes could try to spawn the plugin at
255 * the same time. In this case, the plugin should indicate to the client to
256 * retry connecting.
257 */
258 ✗ ExternalCacheManager::PluginHandle *ExternalCacheManager::CreatePlugin(
259 const std::string &locator, const std::vector<std::string> &cmd_line) {
260 ✗ std::unique_ptr<PluginHandle> plugin_handle(new PluginHandle());
261 ✗ unsigned num_attempts = 0;
262 ✗ bool try_again = false;
263 ✗ do {
264 ✗ num_attempts++;
265 ✗ if (num_attempts > 2) {
266 // Prevent violate busy loops
267 ✗ SafeSleepMs(1000);
268 }
269 ✗ plugin_handle->fd_connection_ = ConnectLocator(locator, num_attempts > 1);
270 ✗ if (plugin_handle->IsValid()) {
271 ✗ break;
272 ✗ } else if (plugin_handle->fd_connection_ == -EINVAL) {
273 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslog, "Invalid locator: %s",
274 locator.c_str());
275 ✗ plugin_handle->error_msg_ = "Invalid locator: " + locator;
276 ✗ break;
277 } else {
278 ✗ if (num_attempts > 1) {
279 ✗ LogCvmfs(kLogCache, kLogDebug | kLogStderr,
280 "Failed to connect to external cache manager: %d",
281 ✗ plugin_handle->fd_connection_);
282 }
283 ✗ plugin_handle->error_msg_ = "Failed to connect to external cache manager";
284 }
285
286 ✗ try_again = SpawnPlugin(cmd_line);
287 } while (try_again);
288
289 ✗ return plugin_handle.release();
290 }
291
292
293 2187 void ExternalCacheManager::CtrlTxn(const Label &label,
294 const int flags,
295 void *txn) {
296 2187 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
297 2187 transaction->label = label;
298 2187 transaction->label_modified = true;
299 2187 }
300
301
302 ✗ string ExternalCacheManager::Describe() { return "External cache manager\n"; }
303
304
305 66 bool ExternalCacheManager::DoFreeState(void *data) {
306 FdTable<ReadOnlyHandle>
307 66 *fd_table = reinterpret_cast<FdTable<ReadOnlyHandle> *>(data);
308
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 delete fd_table;
309 66 return true;
310 }
311
312
313 9546 int ExternalCacheManager::DoOpen(const shash::Any &id) {
314 9546 int fd = -1;
315 {
316 9546 const WriteLockGuard guard(rwlock_fd_table_);
317
1/2
✓ Branch 2 taken 9546 times.
✗ Branch 3 not taken.
9546 fd = fd_table_.OpenFd(ReadOnlyHandle(id));
318
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 9480 times.
9546 if (fd < 0) {
319
1/2
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
66 LogCvmfs(kLogCache, kLogDebug, "error while creating new fd: %s",
320 strerror(-fd));
321 66 return fd;
322 }
323
2/2
✓ Branch 1 taken 9480 times.
✓ Branch 2 taken 66 times.
9546 }
324
325
1/2
✓ Branch 1 taken 9480 times.
✗ Branch 2 not taken.
9480 const int status_refcnt = ChangeRefcount(id, 1);
326
2/2
✓ Branch 0 taken 9406 times.
✓ Branch 1 taken 74 times.
9480 if (status_refcnt == 0)
327 9406 return fd;
328
329 74 const WriteLockGuard guard(rwlock_fd_table_);
330
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 const int retval = fd_table_.CloseFd(fd);
331
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 assert(retval == 0);
332 74 return status_refcnt;
333 74 }
334
335
336 66 int ExternalCacheManager::DoRestoreState(void *data) {
337 // When DoRestoreState is called, we have fd 0 assigned to the root file
338 // catalog unless this is a lower layer cache in a tiered setup
339
2/2
✓ Branch 1 taken 8382 times.
✓ Branch 2 taken 66 times.
8448 for (unsigned i = 1; i < fd_table_.GetMaxFds(); ++i) {
340
3/6
✓ Branch 2 taken 8382 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8382 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 8382 times.
8382 assert(fd_table_.GetHandle(i) == ReadOnlyHandle());
341 }
342
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 const ReadOnlyHandle handle_root = fd_table_.GetHandle(0);
343
344 66 FdTable<ReadOnlyHandle> *other = reinterpret_cast<FdTable<ReadOnlyHandle> *>(
345 data);
346
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 fd_table_.AssignFrom(*other);
347
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 cvmfs::MsgIoctl msg_ioctl;
348
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 msg_ioctl.set_session_id(session_id_);
349
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 msg_ioctl.set_conncnt_change_by(-1);
350
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 CacheTransport::Frame frame(&msg_ioctl);
351
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 transport_.SendFrame(&frame);
352
353 66 int new_root_fd = -1;
354
2/4
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 66 times.
66 if (handle_root != ReadOnlyHandle()) {
355 ✗ new_root_fd = fd_table_.OpenFd(handle_root);
356 // There must be a free file descriptor because the root file catalog gets
357 // closed before a reload
358 ✗ assert(new_root_fd >= 0);
359 }
360 66 return new_root_fd;
361 66 }
362
363
364 66 void *ExternalCacheManager::DoSaveState() {
365
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 cvmfs::MsgIoctl msg_ioctl;
366
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 msg_ioctl.set_session_id(session_id_);
367
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 msg_ioctl.set_conncnt_change_by(1);
368
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 CacheTransport::Frame frame(&msg_ioctl);
369
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 transport_.SendFrame(&frame);
370
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
132 return fd_table_.Clone();
371 66 }
372
373
374 4257 int ExternalCacheManager::Dup(int fd) {
375
1/2
✓ Branch 1 taken 4257 times.
✗ Branch 2 not taken.
4257 const shash::Any id = GetHandle(fd);
376
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 4224 times.
4257 if (id == kInvalidHandle)
377 33 return -EBADF;
378
1/2
✓ Branch 1 taken 4224 times.
✗ Branch 2 not taken.
4224 return DoOpen(id);
379 }
380
381
382 620 ExternalCacheManager::ExternalCacheManager(int fd_connection,
383 620 unsigned max_open_fds)
384 620 : pid_plugin_(0)
385
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 , fd_table_(max_open_fds, ReadOnlyHandle())
386
1/2
✓ Branch 1 taken 620 times.
✗ Branch 2 not taken.
620 , transport_(fd_connection)
387 620 , session_id_(-1)
388 620 , max_object_size_(0)
389 620 , spawned_(false)
390 620 , terminated_(false)
391 1240 , capabilities_(cvmfs::CAP_NONE) {
392 620 int retval = pthread_rwlock_init(&rwlock_fd_table_, NULL);
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 620 times.
620 assert(retval == 0);
394 620 retval = pthread_mutex_init(&lock_send_fd_, NULL);
395
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 620 times.
620 assert(retval == 0);
396 620 retval = pthread_mutex_init(&lock_inflight_rpcs_, NULL);
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 620 times.
620 assert(retval == 0);
398 620 memset(&thread_read_, 0, sizeof(thread_read_));
399 620 next_request_id_.store(0);
400 620 }
401
402
403 2472 ExternalCacheManager::~ExternalCacheManager() {
404 1236 terminated_ = true;
405 std::atomic_thread_fence(std::memory_order_seq_cst);
406
1/2
✓ Branch 0 taken 618 times.
✗ Branch 1 not taken.
1236 if (session_id_ >= 0) {
407 1236 cvmfs::MsgQuit msg_quit;
408 1236 msg_quit.set_session_id(session_id_);
409 1236 CacheTransport::Frame frame(&msg_quit);
410 1236 transport_.SendFrame(&frame);
411 }
412 1236 shutdown(transport_.fd_connection(), SHUT_RDWR);
413
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 585 times.
1236 if (spawned_)
414 66 pthread_join(thread_read_, NULL);
415 1236 close(transport_.fd_connection());
416 1236 pthread_rwlock_destroy(&rwlock_fd_table_);
417 1236 pthread_mutex_destroy(&lock_send_fd_);
418 1236 pthread_mutex_destroy(&lock_inflight_rpcs_);
419 2472 }
420
421
422 22480 int ExternalCacheManager::Flush(bool do_commit, Transaction *transaction) {
423
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 22478 times.
22480 if (transaction->committed)
424 2 return 0;
425
1/2
✓ Branch 2 taken 20064 times.
✗ Branch 3 not taken.
20064 LogCvmfs(kLogCache, kLogDebug, "flushing %u bytes for %s",
426 transaction->buf_pos,
427
1/2
✓ Branch 1 taken 20064 times.
✗ Branch 2 not taken.
40128 std::string(transaction->id.ToString()).c_str());
428
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 cvmfs::MsgHash object_id;
429
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 transport_.FillMsgHash(transaction->id, &object_id);
430
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 cvmfs::MsgStoreReq msg_store;
431
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 msg_store.set_session_id(session_id_);
432
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 msg_store.set_req_id(transaction->transaction_id);
433
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 msg_store.set_allocated_object_id(&object_id);
434
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 msg_store.set_part_nr((transaction->size / max_object_size_) + 1);
435
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 msg_store.set_expected_size(transaction->expected_size);
436
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 msg_store.set_last_part(do_commit);
437
438
2/2
✓ Branch 0 taken 22470 times.
✓ Branch 1 taken 8 times.
22478 if (transaction->label_modified) {
439 cvmfs::EnumObjectType object_type;
440
1/2
✓ Branch 1 taken 22470 times.
✗ Branch 2 not taken.
22470 transport_.FillObjectType(transaction->label.flags, &object_type);
441
1/2
✓ Branch 1 taken 22470 times.
✗ Branch 2 not taken.
22470 msg_store.set_object_type(object_type);
442
1/2
✓ Branch 1 taken 22470 times.
✗ Branch 2 not taken.
44940 msg_store.set_description(transaction->label.GetDescription());
443 }
444
445
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 RpcJob rpc_job(&msg_store);
446 22478 rpc_job.set_attachment_send(transaction->buffer, transaction->buf_pos);
447 // TODO(jblomer): allow for out of order chunk upload
448
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 CallRemotely(&rpc_job);
449
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 (void)msg_store.release_object_id();
450
451
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 cvmfs::MsgStoreReply *msg_reply = rpc_job.msg_store_reply();
452
1/2
✓ Branch 1 taken 22478 times.
✗ Branch 2 not taken.
22478 if (msg_reply->status() == cvmfs::STATUS_OK) {
453 22478 transaction->flushed = true;
454
2/2
✓ Branch 0 taken 2230 times.
✓ Branch 1 taken 20248 times.
22478 if (do_commit)
455 2230 transaction->committed = true;
456 }
457 22478 return Ack2Errno(msg_reply->status());
458 22478 }
459
460
461 5764 shash::Any ExternalCacheManager::GetHandle(int fd) {
462 5764 const ReadLockGuard guard(rwlock_fd_table_);
463
1/2
✓ Branch 1 taken 5764 times.
✗ Branch 2 not taken.
5764 const ReadOnlyHandle handle = fd_table_.GetHandle(fd);
464 5764 return handle.id;
465 5764 }
466
467
468 585 int64_t ExternalCacheManager::GetSize(int fd) {
469
1/2
✓ Branch 1 taken 585 times.
✗ Branch 2 not taken.
585 const shash::Any id = GetHandle(fd);
470
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 552 times.
585 if (id == kInvalidHandle)
471 33 return -EBADF;
472
473
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 cvmfs::MsgHash object_id;
474
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 transport_.FillMsgHash(id, &object_id);
475
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 cvmfs::MsgObjectInfoReq msg_info;
476
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 msg_info.set_session_id(session_id_);
477
1/2
✓ Branch 2 taken 552 times.
✗ Branch 3 not taken.
552 msg_info.set_req_id(NextRequestId());
478
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 msg_info.set_allocated_object_id(&object_id);
479
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 RpcJob rpc_job(&msg_info);
480
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 CallRemotely(&rpc_job);
481
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 (void)msg_info.release_object_id();
482
483
1/2
✓ Branch 1 taken 552 times.
✗ Branch 2 not taken.
552 cvmfs::MsgObjectInfoReply *msg_reply = rpc_job.msg_object_info_reply();
484
2/2
✓ Branch 1 taken 519 times.
✓ Branch 2 taken 33 times.
552 if (msg_reply->status() == cvmfs::STATUS_OK) {
485
2/4
✓ Branch 1 taken 519 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 519 times.
519 assert(msg_reply->has_size());
486 519 return msg_reply->size();
487 }
488 33 return Ack2Errno(msg_reply->status());
489 552 }
490
491
492 33 void *ExternalCacheManager::MainRead(void *data) {
493 33 ExternalCacheManager *cache_mgr = reinterpret_cast<ExternalCacheManager *>(
494 data);
495 33 LogCvmfs(kLogCache, kLogDebug, "starting external cache reader thread");
496
497 33 unsigned char buffer[cache_mgr->max_object_size_];
498 while (true) {
499
1/2
✓ Branch 1 taken 99957 times.
✗ Branch 2 not taken.
99957 CacheTransport::Frame frame_recv;
500 99957 frame_recv.set_attachment(buffer, cache_mgr->max_object_size_);
501
1/2
✓ Branch 1 taken 99957 times.
✗ Branch 2 not taken.
99957 const bool retval = cache_mgr->transport_.RecvFrame(&frame_recv);
502
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 99924 times.
99957 if (!retval)
503 33 break;
504
505 uint64_t req_id;
506 99924 uint64_t part_nr = 0;
507
1/2
✓ Branch 1 taken 99924 times.
✗ Branch 2 not taken.
99924 google::protobuf::MessageLite *msg = frame_recv.GetMsgTyped();
508
3/4
✓ Branch 1 taken 99924 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 627 times.
✓ Branch 6 taken 99297 times.
99924 if (msg->GetTypeName() == "cvmfs.MsgRefcountReply") {
509 627 req_id = reinterpret_cast<cvmfs::MsgRefcountReply *>(msg)->req_id();
510
3/4
✓ Branch 1 taken 99297 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 297 times.
✓ Branch 6 taken 99000 times.
99297 } else if (msg->GetTypeName() == "cvmfs.MsgObjectInfoReply") {
511 297 req_id = reinterpret_cast<cvmfs::MsgObjectInfoReply *>(msg)->req_id();
512
3/4
✓ Branch 1 taken 99000 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 59400 times.
✓ Branch 6 taken 39600 times.
99000 } else if (msg->GetTypeName() == "cvmfs.MsgReadReply") {
513 59400 req_id = reinterpret_cast<cvmfs::MsgReadReply *>(msg)->req_id();
514
3/4
✓ Branch 1 taken 39600 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6600 times.
✓ Branch 6 taken 33000 times.
39600 } else if (msg->GetTypeName() == "cvmfs.MsgStoreReply") {
515 6600 req_id = reinterpret_cast<cvmfs::MsgStoreReply *>(msg)->req_id();
516 6600 part_nr = reinterpret_cast<cvmfs::MsgStoreReply *>(msg)->part_nr();
517
2/4
✓ Branch 1 taken 33000 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 33000 times.
33000 } else if (msg->GetTypeName() == "cvmfs.MsgInfoReply") {
518 ✗ req_id = reinterpret_cast<cvmfs::MsgInfoReply *>(msg)->req_id();
519
2/4
✓ Branch 1 taken 33000 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 33000 times.
33000 } else if (msg->GetTypeName() == "cvmfs.MsgShrinkReply") {
520 ✗ req_id = reinterpret_cast<cvmfs::MsgShrinkReply *>(msg)->req_id();
521
2/4
✓ Branch 1 taken 33000 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 33000 times.
33000 } else if (msg->GetTypeName() == "cvmfs.MsgListReply") {
522 ✗ req_id = reinterpret_cast<cvmfs::MsgListReply *>(msg)->req_id();
523
2/4
✓ Branch 1 taken 33000 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 33000 times.
33000 } else if (msg->GetTypeName() == "cvmfs.MsgBreadcrumbReply") {
524 ✗ req_id = reinterpret_cast<cvmfs::MsgBreadcrumbReply *>(msg)->req_id();
525
2/4
✓ Branch 1 taken 33000 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 33000 times.
✗ Branch 6 not taken.
33000 } else if (msg->GetTypeName() == "cvmfs.MsgDetach") {
526 // Release pinned catalogs
527
2/4
✓ Branch 2 taken 33000 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 33000 times.
✗ Branch 6 not taken.
33000 cache_mgr->quota_mgr_->BroadcastBackchannels("R");
528 33000 continue;
529 } else {
530 ✗ PANIC(kLogSyslogErr | kLogDebug, "unexpected message %s",
531 std::string(msg->GetTypeName()).c_str());
532 }
533
534 66924 RpcInFlight rpc_inflight;
535 {
536 66924 const MutexLockGuard guard(cache_mgr->lock_inflight_rpcs_);
537
1/2
✓ Branch 1 taken 67023 times.
✗ Branch 2 not taken.
67023 for (unsigned i = 0; i < cache_mgr->inflight_rpcs_.size(); ++i) {
538 67023 RpcJob *rpc_job = cache_mgr->inflight_rpcs_[i].rpc_job;
539
5/6
✓ Branch 1 taken 66924 times.
✓ Branch 2 taken 99 times.
✓ Branch 4 taken 66924 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 66924 times.
✓ Branch 7 taken 99 times.
67023 if ((rpc_job->req_id() == req_id) && (rpc_job->part_nr() == part_nr)) {
540 66924 rpc_inflight = cache_mgr->inflight_rpcs_[i];
541
1/2
✓ Branch 2 taken 66924 times.
✗ Branch 3 not taken.
66924 cache_mgr->inflight_rpcs_.erase(cache_mgr->inflight_rpcs_.begin()
542 66924 + i);
543 66924 break;
544 }
545 }
546 66924 }
547
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66924 times.
66924 if (rpc_inflight.rpc_job == NULL) {
548 ✗ LogCvmfs(kLogCache, kLogSyslogWarn | kLogDebug,
549 "got unmatched rpc reply");
550 ✗ continue;
551 }
552
1/2
✓ Branch 2 taken 66924 times.
✗ Branch 3 not taken.
66924 rpc_inflight.rpc_job->frame_recv()->MergeFrom(frame_recv);
553
1/2
✓ Branch 1 taken 66924 times.
✗ Branch 2 not taken.
66924 rpc_inflight.signal->Wakeup();
554
3/3
✓ Branch 1 taken 66924 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 33000 times.
199881 }
555
556
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if (!cache_mgr->terminated_) {
557 ✗ PANIC(kLogSyslogErr | kLogDebug,
558 "connection to external cache manager broken (%d)", errno);
559 }
560
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 LogCvmfs(kLogCache, kLogDebug, "stopping external cache reader thread");
561 33 return NULL;
562
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 }
563
564
565 5322 int ExternalCacheManager::Open(const LabeledObject &object) {
566 5322 return DoOpen(object.id);
567 }
568
569
570 2 int ExternalCacheManager::OpenFromTxn(void *txn) {
571 2 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
572 ✗ LogCvmfs(kLogCache, kLogDebug, "open fd for transaction %s",
573 ✗ std::string(transaction->id.ToString()).c_str());
574 2 const int retval = Flush(true, transaction);
575
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (retval != 0)
576 ✗ return retval;
577
578 2 int fd = -1;
579 {
580 2 const WriteLockGuard guard(rwlock_fd_table_);
581
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 fd = fd_table_.OpenFd(ReadOnlyHandle(transaction->id));
582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (fd < 0) {
583 ✗ LogCvmfs(kLogCache, kLogDebug, "error while creating new fd: %s",
584 strerror(-fd));
585 ✗ return fd;
586 }
587
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 }
588 2 transaction->open_fds++;
589 2 return fd;
590 }
591
592
593 856 int64_t ExternalCacheManager::Pread(int fd,
594 void *buf,
595 uint64_t size,
596 uint64_t offset) {
597
1/2
✓ Branch 1 taken 856 times.
✗ Branch 2 not taken.
856 const shash::Any id = GetHandle(fd);
598
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 823 times.
856 if (id == kInvalidHandle)
599 33 return -EBADF;
600
601
1/2
✓ Branch 1 taken 823 times.
✗ Branch 2 not taken.
823 cvmfs::MsgHash object_id;
602
1/2
✓ Branch 1 taken 823 times.
✗ Branch 2 not taken.
823 transport_.FillMsgHash(id, &object_id);
603 823 uint64_t nbytes = 0;
604
2/2
✓ Branch 0 taken 73664 times.
✓ Branch 1 taken 681 times.
74345 while (nbytes < size) {
605 73664 const uint64_t batch_size = std::min(
606 73664 size - nbytes, static_cast<uint64_t>(max_object_size_));
607
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 cvmfs::MsgReadReq msg_read;
608
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 msg_read.set_session_id(session_id_);
609
1/2
✓ Branch 2 taken 73664 times.
✗ Branch 3 not taken.
73664 msg_read.set_req_id(NextRequestId());
610
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 msg_read.set_allocated_object_id(&object_id);
611
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 msg_read.set_offset(offset + nbytes);
612
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 msg_read.set_size(batch_size);
613
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 RpcJob rpc_job(&msg_read);
614 73664 rpc_job.set_attachment_recv(reinterpret_cast<char *>(buf) + nbytes,
615 batch_size);
616
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 CallRemotely(&rpc_job);
617
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 (void)msg_read.release_object_id();
618
619
1/2
✓ Branch 1 taken 73664 times.
✗ Branch 2 not taken.
73664 cvmfs::MsgReadReply *msg_reply = rpc_job.msg_read_reply();
620
2/2
✓ Branch 1 taken 73627 times.
✓ Branch 2 taken 37 times.
73664 if (msg_reply->status() == cvmfs::STATUS_OK) {
621 73627 nbytes += rpc_job.frame_recv()->att_size();
622 // Fuse sends in rounded up buffers, so short reads are expected
623
2/2
✓ Branch 2 taken 105 times.
✓ Branch 3 taken 73522 times.
73627 if (rpc_job.frame_recv()->att_size() < batch_size)
624 105 return nbytes;
625 } else {
626 37 return Ack2Errno(msg_reply->status());
627 }
628
4/4
✓ Branch 1 taken 73522 times.
✓ Branch 2 taken 142 times.
✓ Branch 4 taken 73522 times.
✓ Branch 5 taken 142 times.
73806 }
629 681 return size;
630 823 }
631
632
633 66 int ExternalCacheManager::Readahead(int fd) {
634
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 const shash::Any id = GetHandle(fd);
635
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 33 times.
66 if (id == kInvalidHandle)
636 33 return -EBADF;
637 // No-op
638 33 return 0;
639 }
640
641
642 103 int ExternalCacheManager::Reset(void *txn) {
643 103 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
644 103 transaction->buf_pos = 0;
645 103 transaction->size = 0;
646 103 transaction->open_fds = 0;
647 103 transaction->committed = false;
648 103 transaction->label_modified = true;
649
650
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 33 times.
103 if (!transaction->flushed)
651 70 return 0;
652
653
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 cvmfs::MsgHash object_id;
654
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 transport_.FillMsgHash(transaction->id, &object_id);
655
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 cvmfs::MsgStoreAbortReq msg_abort;
656
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 msg_abort.set_session_id(session_id_);
657
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 msg_abort.set_req_id(transaction->transaction_id);
658
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 msg_abort.set_allocated_object_id(&object_id);
659
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 RpcJob rpc_job(&msg_abort);
660
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 CallRemotely(&rpc_job);
661
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 (void)msg_abort.release_object_id();
662
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 cvmfs::MsgStoreReply *msg_reply = rpc_job.msg_store_reply();
663 33 transaction->transaction_id = NextRequestId();
664 33 transaction->flushed = false;
665 33 return Ack2Errno(msg_reply->status());
666 33 }
667
668
669 70 manifest::Breadcrumb ExternalCacheManager::LoadBreadcrumb(
670 const std::string &fqrn) {
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (!(capabilities_ & cvmfs::CAP_BREADCRUMB))
672 ✗ return manifest::Breadcrumb();
673
674
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 cvmfs::MsgBreadcrumbLoadReq msg_breadcrumb_load;
675
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 msg_breadcrumb_load.set_session_id(session_id_);
676
1/2
✓ Branch 2 taken 70 times.
✗ Branch 3 not taken.
70 msg_breadcrumb_load.set_req_id(NextRequestId());
677 msg_breadcrumb_load.set_fqrn(fqrn);
678
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 RpcJob rpc_job(&msg_breadcrumb_load);
679
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 CallRemotely(&rpc_job);
680
681
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 manifest::Breadcrumb breadcrumb;
682
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 cvmfs::MsgBreadcrumbReply *msg_reply = rpc_job.msg_breadcrumb_reply();
683
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 35 times.
70 if (msg_reply->status() == cvmfs::STATUS_OK) {
684
2/4
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 35 times.
35 assert(msg_reply->has_breadcrumb());
685
2/4
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 35 times.
35 assert(msg_reply->breadcrumb().fqrn() == fqrn);
686
1/2
✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
35 const bool rv = transport_.ParseMsgHash(msg_reply->breadcrumb().hash(),
687 &breadcrumb.catalog_hash);
688
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 assert(rv);
689 35 breadcrumb.catalog_hash.suffix = shash::kSuffixCatalog;
690 35 breadcrumb.timestamp = msg_reply->breadcrumb().timestamp();
691
2/4
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
35 if (msg_reply->breadcrumb().has_revision()) {
692 35 breadcrumb.revision = msg_reply->breadcrumb().revision();
693 } else {
694 ✗ breadcrumb.revision = 0;
695 }
696 }
697 70 return breadcrumb;
698 70 }
699
700
701 35 bool ExternalCacheManager::StoreBreadcrumb(const manifest::Manifest &manifest) {
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (!(capabilities_ & cvmfs::CAP_BREADCRUMB))
703 ✗ return false;
704
705
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 cvmfs::MsgHash hash;
706
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 transport_.FillMsgHash(manifest.catalog_hash(), &hash);
707
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 cvmfs::MsgBreadcrumb breadcrumb;
708
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
70 breadcrumb.set_fqrn(manifest.repository_name());
709
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 breadcrumb.set_allocated_hash(&hash);
710
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 breadcrumb.set_timestamp(manifest.publish_timestamp());
711
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 breadcrumb.set_revision(manifest.revision());
712
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 cvmfs::MsgBreadcrumbStoreReq msg_breadcrumb_store;
713
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 msg_breadcrumb_store.set_session_id(session_id_);
714
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 msg_breadcrumb_store.set_req_id(NextRequestId());
715
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 msg_breadcrumb_store.set_allocated_breadcrumb(&breadcrumb);
716
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 RpcJob rpc_job(&msg_breadcrumb_store);
717
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 CallRemotely(&rpc_job);
718
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 (void)msg_breadcrumb_store.release_breadcrumb();
719
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 (void)breadcrumb.release_hash();
720
721
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 cvmfs::MsgBreadcrumbReply *msg_reply = rpc_job.msg_breadcrumb_reply();
722 35 return msg_reply->status() == cvmfs::STATUS_OK;
723 35 }
724
725
726 33 void ExternalCacheManager::Spawn() {
727 33 const int retval = pthread_create(&thread_read_, NULL, MainRead, this);
728
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 assert(retval == 0);
729 33 spawned_ = true;
730 33 }
731
732
733 /**
734 * Returns true if the plugin could be spawned or was spawned by another
735 * process.
736 */
737 ✗ bool ExternalCacheManager::SpawnPlugin(const vector<string> &cmd_line) {
738 ✗ if (cmd_line.empty())
739 ✗ return false;
740
741 int pipe_ready[2];
742 ✗ MakePipe(pipe_ready);
743 ✗ set<int> preserve_filedes;
744 ✗ preserve_filedes.insert(pipe_ready[1]);
745
746 ✗ const int fd_null_read = open("/dev/null", O_RDONLY);
747 ✗ const int fd_null_write = open("/dev/null", O_WRONLY);
748 ✗ assert((fd_null_read >= 0) && (fd_null_write >= 0));
749 ✗ map<int, int> map_fildes;
750 ✗ map_fildes[fd_null_read] = 0;
751 ✗ map_fildes[fd_null_write] = 1;
752 ✗ map_fildes[fd_null_write] = 2;
753
754 pid_t child_pid;
755 ✗ int retval = setenv(CacheTransport::kEnvReadyNotifyFd,
756 ✗ StringifyInt(pipe_ready[1]).c_str(), 1);
757 ✗ assert(retval == 0);
758 ✗ retval = ManagedExec(cmd_line,
759 preserve_filedes,
760 map_fildes,
761 false, // drop_credentials
762 false, // clear_env
763 true, // double fork
764 &child_pid);
765 ✗ unsetenv(CacheTransport::kEnvReadyNotifyFd);
766 ✗ close(fd_null_read);
767 ✗ close(fd_null_write);
768 ✗ if (!retval) {
769 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
770 "failed to start cache plugin '%s'",
771 ✗ JoinStrings(cmd_line, " ").c_str());
772 ✗ ClosePipe(pipe_ready);
773 ✗ return false;
774 }
775
776 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslog,
777 "started cache plugin '%s' (pid %d), waiting for it to become ready",
778 ✗ JoinStrings(cmd_line, " ").c_str(), child_pid);
779 ✗ close(pipe_ready[1]);
780 char buf;
781 ✗ if (read(pipe_ready[0], &buf, 1) != 1) {
782 ✗ close(pipe_ready[0]);
783 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
784 "cache plugin did not start properly");
785 ✗ return false;
786 }
787 ✗ close(pipe_ready[0]);
788
789 ✗ if (buf == CacheTransport::kReadyNotification)
790 ✗ return true;
791 ✗ LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
792 "cache plugin failed to create an endpoint");
793 ✗ return false;
794 }
795
796
797 2296 int ExternalCacheManager::StartTxn(const shash::Any &id,
798 uint64_t size,
799 void *txn) {
800
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 2230 times.
2296 if (!(capabilities_ & cvmfs::CAP_WRITE))
801 66 return -EROFS;
802
803 2230 Transaction *transaction = new (txn) Transaction(id);
804 2230 transaction->expected_size = size;
805 2230 transaction->transaction_id = NextRequestId();
806 #ifdef __APPLE__
807 transaction->buffer = reinterpret_cast<unsigned char *>(
808 smalloc(max_object_size_));
809 #endif
810 2230 return 0;
811 }
812
813
814 2298 int64_t ExternalCacheManager::Write(const void *buf, uint64_t size, void *txn) {
815 2298 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
816
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2298 times.
2298 assert(!transaction->committed);
817
1/2
✓ Branch 2 taken 264 times.
✗ Branch 3 not taken.
264 LogCvmfs(kLogCache, kLogDebug, "writing %" PRIu64 " bytes for %s", size,
818 528 transaction->id.ToString().c_str());
819
820
1/2
✓ Branch 0 taken 2298 times.
✗ Branch 1 not taken.
2298 if (transaction->expected_size != kSizeUnknown) {
821
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2298 times.
2298 if (transaction->size + size > transaction->expected_size) {
822 ✗ LogCvmfs(kLogCache, kLogDebug,
823 "Transaction size (%" PRIu64 ") > expected size (%" PRIu64 ")",
824 ✗ transaction->size + size, transaction->expected_size);
825 ✗ return -EFBIG;
826 }
827 }
828
829 2298 uint64_t written = 0;
830 2298 const unsigned char *read_pos = reinterpret_cast<const unsigned char *>(buf);
831
2/2
✓ Branch 0 taken 22511 times.
✓ Branch 1 taken 2298 times.
24809 while (written < size) {
832
2/2
✓ Branch 0 taken 20248 times.
✓ Branch 1 taken 2263 times.
22511 if (transaction->buf_pos == max_object_size_) {
833 20248 bool do_commit = false;
834
1/2
✓ Branch 0 taken 20248 times.
✗ Branch 1 not taken.
20248 if (transaction->expected_size != kSizeUnknown)
835 20248 do_commit = (transaction->size + written) == transaction->expected_size;
836
1/2
✓ Branch 1 taken 20248 times.
✗ Branch 2 not taken.
20248 const int retval = Flush(do_commit, transaction);
837
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20248 times.
20248 if (retval != 0) {
838 ✗ transaction->size += written;
839 ✗ return retval;
840 }
841 20248 transaction->size += transaction->buf_pos;
842 20248 transaction->buf_pos = 0;
843 }
844 22511 const uint64_t remaining = size - written;
845 22511 const uint64_t space_in_buffer = max_object_size_ - transaction->buf_pos;
846 22511 const uint64_t batch_size = std::min(remaining, space_in_buffer);
847 22511 memcpy(transaction->buffer + transaction->buf_pos, read_pos, batch_size);
848 22511 transaction->buf_pos += batch_size;
849 22511 written += batch_size;
850 22511 read_pos += batch_size;
851 }
852 2298 return written;
853 }
854
855
856 //------------------------------------------------------------------------------
857
858
859 206 bool ExternalQuotaManager::DoListing(cvmfs::EnumObjectType type,
860 vector<cvmfs::MsgListRecord> *result) {
861
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206 if (!(cache_mgr_->capabilities_ & cvmfs::CAP_LIST))
862 ✗ return false;
863
864 206 uint64_t listing_id = 0;
865 206 bool more_data = false;
866
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 206 times.
338 do {
867
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 cvmfs::MsgListReq msg_list;
868
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 msg_list.set_session_id(cache_mgr_->session_id_);
869
1/2
✓ Branch 2 taken 338 times.
✗ Branch 3 not taken.
338 msg_list.set_req_id(cache_mgr_->NextRequestId());
870
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 msg_list.set_listing_id(listing_id);
871
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 msg_list.set_object_type(type);
872
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 ExternalCacheManager::RpcJob rpc_job(&msg_list);
873
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 cache_mgr_->CallRemotely(&rpc_job);
874
875
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 cvmfs::MsgListReply *msg_reply = rpc_job.msg_list_reply();
876
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 338 times.
338 if (msg_reply->status() != cvmfs::STATUS_OK)
877 ✗ return false;
878 338 more_data = !msg_reply->is_last_part();
879 338 listing_id = msg_reply->listing_id();
880
3/4
✓ Branch 1 taken 6604342 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6604004 times.
✓ Branch 4 taken 338 times.
6604342 for (int i = 0; i < msg_reply->list_record_size(); ++i) {
881
2/4
✓ Branch 1 taken 6604004 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6604004 times.
✗ Branch 5 not taken.
6604004 result->push_back(msg_reply->list_record(i));
882 }
883
2/4
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 338 times.
✗ Branch 5 not taken.
338 } while (more_data);
884
885 206 return true;
886 }
887
888
889 74 bool ExternalQuotaManager::Cleanup(const uint64_t leave_size) {
890
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 if (!(cache_mgr_->capabilities_ & cvmfs::CAP_SHRINK))
891 ✗ return false;
892
893
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 cvmfs::MsgShrinkReq msg_shrink;
894
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 msg_shrink.set_session_id(cache_mgr_->session_id_);
895
1/2
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
74 msg_shrink.set_req_id(cache_mgr_->NextRequestId());
896
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 msg_shrink.set_shrink_to(leave_size);
897
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 ExternalCacheManager::RpcJob rpc_job(&msg_shrink);
898
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 cache_mgr_->CallRemotely(&rpc_job);
899
900
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 cvmfs::MsgShrinkReply *msg_reply = rpc_job.msg_shrink_reply();
901 74 return msg_reply->status() == cvmfs::STATUS_OK;
902 74 }
903
904
905 618 ExternalQuotaManager *ExternalQuotaManager::Create(
906 ExternalCacheManager *cache_mgr) {
907 std::unique_ptr<ExternalQuotaManager> quota_mgr(
908
2/4
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 618 times.
✗ Branch 5 not taken.
618 new ExternalQuotaManager(cache_mgr));
909
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 618 times.
618 assert(quota_mgr.get() != nullptr);
910
911 1236 return quota_mgr.release();
912 618 }
913
914
915 156 int ExternalQuotaManager::GetInfo(QuotaInfo *quota_info) {
916
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156 times.
156 if (!(cache_mgr_->capabilities_ & cvmfs::CAP_INFO))
917 ✗ return Ack2Errno(cvmfs::STATUS_NOSUPPORT);
918
919
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 cvmfs::MsgInfoReq msg_info;
920
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 msg_info.set_session_id(cache_mgr_->session_id_);
921
1/2
✓ Branch 2 taken 156 times.
✗ Branch 3 not taken.
156 msg_info.set_req_id(cache_mgr_->NextRequestId());
922
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 ExternalCacheManager::RpcJob rpc_job(&msg_info);
923
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 cache_mgr_->CallRemotely(&rpc_job);
924
925
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 cvmfs::MsgInfoReply *msg_reply = rpc_job.msg_info_reply();
926
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 if (msg_reply->status() == cvmfs::STATUS_OK) {
927 156 quota_info->size = msg_reply->size_bytes();
928 156 quota_info->used = msg_reply->used_bytes();
929 156 quota_info->pinned = msg_reply->pinned_bytes();
930
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 if (msg_reply->no_shrink() >= 0)
931 156 quota_info->no_shrink = msg_reply->no_shrink();
932 }
933 156 return Ack2Errno(msg_reply->status());
934 156 }
935
936
937 37 uint64_t ExternalQuotaManager::GetCapacity() {
938 37 QuotaInfo info;
939
1/2
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
37 const int retval = GetInfo(&info);
940
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (retval != 0)
941 ✗ return uint64_t(-1);
942 37 return info.size;
943 }
944
945
946 ✗ uint64_t ExternalQuotaManager::GetCleanupRate(uint64_t period_s) {
947 ✗ QuotaInfo info;
948 ✗ const int retval = GetInfo(&info);
949 ✗ if (retval != 0)
950 ✗ return 0;
951 ✗ return info.no_shrink;
952 }
953
954
955 47 uint64_t ExternalQuotaManager::GetSize() {
956 47 QuotaInfo info;
957
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 const int retval = GetInfo(&info);
958
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if (retval != 0)
959 ✗ return 0;
960 47 return info.used;
961 }
962
963
964 72 uint64_t ExternalQuotaManager::GetSizePinned() {
965 72 QuotaInfo info;
966
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 const int retval = GetInfo(&info);
967
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 if (retval != 0)
968 ✗ return 0;
969 72 return info.pinned;
970 }
971
972
973 ✗ bool ExternalQuotaManager::HasCapability(Capabilities capability) {
974 ✗ switch (capability) {
975 ✗ case kCapIntrospectSize:
976 ✗ return cache_mgr_->capabilities_ & cvmfs::CAP_INFO;
977 ✗ case kCapIntrospectCleanupRate:
978 ✗ return cache_mgr_->capabilities_ & cvmfs::CAP_SHRINK_RATE;
979 ✗ case kCapList:
980 ✗ return cache_mgr_->capabilities_ & cvmfs::CAP_LIST;
981 ✗ case kCapShrink:
982 ✗ return cache_mgr_->capabilities_ & cvmfs::CAP_SHRINK;
983 ✗ case kCapListeners:
984 ✗ return true;
985 ✗ default:
986 ✗ return false;
987 }
988 }
989
990
991 35 vector<string> ExternalQuotaManager::List() {
992 35 vector<string> result;
993 35 vector<cvmfs::MsgListRecord> raw_list;
994
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 const bool retval = DoListing(cvmfs::OBJECT_REGULAR, &raw_list);
995
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (!retval)
996 ✗ return result;
997
2/2
✓ Branch 1 taken 3302002 times.
✓ Branch 2 taken 35 times.
3302037 for (unsigned i = 0; i < raw_list.size(); ++i)
998
2/4
✓ Branch 2 taken 3302002 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3302002 times.
✗ Branch 6 not taken.
3302002 result.push_back(raw_list[i].description());
999 35 return result;
1000 35 }
1001
1002
1003 33 vector<string> ExternalQuotaManager::ListCatalogs() {
1004 33 vector<string> result;
1005 33 vector<cvmfs::MsgListRecord> raw_list;
1006
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 const bool retval = DoListing(cvmfs::OBJECT_CATALOG, &raw_list);
1007
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if (!retval)
1008 ✗ return result;
1009
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
33 for (unsigned i = 0; i < raw_list.size(); ++i)
1010 ✗ result.push_back(raw_list[i].description());
1011 33 return result;
1012 33 }
1013
1014
1015 35 vector<string> ExternalQuotaManager::ListPinned() {
1016 35 vector<string> result;
1017
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 35 times.
280 vector<cvmfs::MsgListRecord> raw_lists[3];
1018
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 bool retval = DoListing(cvmfs::OBJECT_REGULAR, &raw_lists[0]);
1019
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (!retval)
1020 ✗ return result;
1021
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 retval = DoListing(cvmfs::OBJECT_CATALOG, &raw_lists[1]);
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (!retval)
1023 ✗ return result;
1024
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 retval = DoListing(cvmfs::OBJECT_VOLATILE, &raw_lists[2]);
1025
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (!retval)
1026 ✗ return result;
1027
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 35 times.
140 for (unsigned i = 0; i < sizeof(raw_lists) / sizeof(raw_lists[0]); ++i) {
1028
2/2
✓ Branch 1 taken 3302002 times.
✓ Branch 2 taken 105 times.
3302107 for (unsigned j = 0; j < raw_lists[i].size(); ++j) {
1029
2/2
✓ Branch 2 taken 200 times.
✓ Branch 3 taken 3301802 times.
3302002 if (raw_lists[i][j].pinned())
1030
2/4
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 200 times.
✗ Branch 6 not taken.
200 result.push_back(raw_lists[i][j].description());
1031 }
1032 }
1033 35 return result;
1034
2/4
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
140 }
1035
1036
1037 33 vector<string> ExternalQuotaManager::ListVolatile() {
1038 33 vector<string> result;
1039 33 vector<cvmfs::MsgListRecord> raw_list;
1040
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 const bool retval = DoListing(cvmfs::OBJECT_VOLATILE, &raw_list);
1041
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if (!retval)
1042 ✗ return result;
1043
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
33 for (unsigned i = 0; i < raw_list.size(); ++i)
1044 ✗ result.push_back(raw_list[i].description());
1045 33 return result;
1046 33 }
1047
1048
1049 33 void ExternalQuotaManager::RegisterBackChannel(int back_channel[2],
1050 const string &channel_id) {
1051
1/2
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 const shash::Md5 hash_id = shash::Md5(shash::AsciiPtr(channel_id));
1052
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 MakePipe(back_channel);
1053 33 LockBackChannels();
1054
2/4
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 33 times.
33 assert(back_channels_.find(hash_id) == back_channels_.end());
1055
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 back_channels_[hash_id] = back_channel[1];
1056 33 UnlockBackChannels();
1057 33 }
1058
1059
1060 33 void ExternalQuotaManager::UnregisterBackChannel(int back_channel[2],
1061 const string &channel_id) {
1062
1/2
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 const shash::Md5 hash_id = shash::Md5(shash::AsciiPtr(channel_id));
1063 33 LockBackChannels();
1064
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 back_channels_.erase(hash_id);
1065 33 UnlockBackChannels();
1066
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 ClosePipe(back_channel);
1067 33 }
1068