GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.cc
Date: 2026-09-20 02:39:58
Exec Total Coverage
Lines: 987 1424 69.3%
Branches: 769 1964 39.2%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * This module implements a "managed local cache".
5 * This way, we are able to track access times of files in the cache
6 * and remove files based on least recently used strategy.
7 *
8 * We setup another SQLite catalog, a "cache catalog", that helps us
9 * in the bookkeeping of files, file sizes and access times.
10 *
11 * We might choose to not manage the local cache. This is indicated
12 * by limit == 0 and everything succeeds in that case.
13 */
14
15 #define __STDC_LIMIT_MACROS
16
17
18 #include "quota_posix.h"
19
20 #include <dirent.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <pthread.h>
25 #include <signal.h>
26 #include <stdint.h>
27 #include <sys/dir.h>
28 #include <sys/stat.h>
29 #include <sys/xattr.h>
30
31 #ifndef __APPLE__
32 #include <sys/statfs.h>
33 #endif
34 #include <sys/statvfs.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38
39 #include <algorithm>
40 #include <cassert>
41 #include <cstdio>
42 #include <cstdlib>
43 #include <cstring>
44 #include <limits>
45 #include <map>
46 #include <memory>
47 #include <set>
48 #include <string>
49 #include <vector>
50
51 #include "crypto/hash.h"
52 #include "duplex_sqlite3.h"
53 #include "monitor.h"
54 #include "statistics.h"
55 #include "util/capabilities.h"
56 #include "util/concurrency.h"
57 #include "util/exception.h"
58 #include "util/logging.h"
59 #include "util/posix.h"
60 #include "util/smalloc.h"
61 #include "util/string.h"
62
63 using namespace std; // NOLINT
64
65
66 2632 int PosixQuotaManager::BindReturnPipe(int pipe_wronly) {
67
2/2
✓ Branch 0 taken 2576 times.
✓ Branch 1 taken 56 times.
2632 if (!shared_)
68 2576 return pipe_wronly;
69
70 // Connect writer's end
71
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 const int result = open(
72
2/4
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 56 times.
✗ Branch 6 not taken.
112 (workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str(),
73 O_WRONLY | O_NONBLOCK);
74
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 if (result >= 0) {
75 28 Nonblock2Block(result);
76 } else {
77 28 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
78 28 "failed to bind return pipe (%d)", errno);
79 }
80 56 return result;
81 }
82
83
84 885 void PosixQuotaManager::CheckHighPinWatermark() {
85 885 const uint64_t watermark = kHighPinWatermark * cleanup_threshold_ / 100;
86
3/4
✓ Branch 0 taken 885 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 801 times.
885 if ((cleanup_threshold_ > 0) && (pinned_ > watermark)) {
87 84 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
88 "high watermark of pinned files (%" PRIu64 "M > %" PRIu64 "M)",
89 84 pinned_ / (1024 * 1024), watermark / (1024 * 1024));
90
2/4
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 84 times.
✗ Branch 6 not taken.
84 BroadcastBackchannels("R"); // clients: please release pinned catalogs
91 }
92 885 }
93
94
95 ✗ void PosixQuotaManager::CleanupPipes() {
96 ✗ DIR *dirp = opendir(workspace_dir_.c_str());
97 ✗ assert(dirp != NULL);
98
99 platform_dirent64 *dent;
100 ✗ bool found_leftovers = false;
101 ✗ while ((dent = platform_readdir(dirp)) != NULL) {
102 ✗ const string name = dent->d_name;
103 ✗ const string path = workspace_dir_ + "/" + name;
104 platform_stat64 info;
105 ✗ const int retval = platform_stat(path.c_str(), &info);
106 ✗ if (retval != 0)
107 ✗ continue;
108 ✗ if (S_ISFIFO(info.st_mode) && (name.substr(0, 4) == "pipe")) {
109 ✗ if (!found_leftovers) {
110 ✗ LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn,
111 "removing left-over FIFOs from cache directory");
112 }
113 ✗ found_leftovers = true;
114 ✗ unlink(path.c_str());
115 }
116 }
117 ✗ closedir(dirp);
118 }
119
120
121 /**
122 * Cleans up in data cache, until cache size is below leave_size.
123 * The actual unlinking is done in a separate process (fork).
124 *
125 * \return True on success, false otherwise
126 */
127 252 bool PosixQuotaManager::Cleanup(const uint64_t leave_size) {
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252 if (!spawned_)
129 ✗ return DoCleanup(leave_size);
130
131 bool result;
132 int pipe_cleanup[2];
133
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 MakeReturnPipe(pipe_cleanup);
134
135 252 LruCommand cmd;
136 252 cmd.command_type = kCleanup;
137 252 cmd.size = leave_size;
138 252 cmd.return_pipe = pipe_cleanup[1];
139
140
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
141
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 ManagedReadHalfPipe(pipe_cleanup[0], &result, sizeof(result));
142
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 CloseReturnPipe(pipe_cleanup);
143
144 252 return result;
145 }
146
147
148 3290 void PosixQuotaManager::CloseDatabase() {
149
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_list_catalogs_)
150 3290 sqlite3_finalize(stmt_list_catalogs_);
151
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_list_pinned_)
152 3290 sqlite3_finalize(stmt_list_pinned_);
153
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_list_volatile_)
154 3290 sqlite3_finalize(stmt_list_volatile_);
155
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_list_)
156 3290 sqlite3_finalize(stmt_list_);
157
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_lru_)
158 3290 sqlite3_finalize(stmt_lru_);
159
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_rm_)
160 3290 sqlite3_finalize(stmt_rm_);
161
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_rm_batch_)
162 3290 sqlite3_finalize(stmt_rm_batch_);
163
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_size_)
164 3290 sqlite3_finalize(stmt_size_);
165
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_touch_)
166 3290 sqlite3_finalize(stmt_touch_);
167
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_unpin_)
168 3290 sqlite3_finalize(stmt_unpin_);
169
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_block_)
170 3290 sqlite3_finalize(stmt_block_);
171
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_unblock_)
172 3290 sqlite3_finalize(stmt_unblock_);
173
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (stmt_new_)
174 3290 sqlite3_finalize(stmt_new_);
175
1/2
✓ Branch 0 taken 3290 times.
✗ Branch 1 not taken.
3290 if (database_)
176 3290 sqlite3_close(database_);
177 3290 UnlockFile(fd_lock_cachedb_);
178
179 3290 stmt_list_catalogs_ = NULL;
180 3290 stmt_list_pinned_ = NULL;
181 3290 stmt_list_volatile_ = NULL;
182 3290 stmt_list_ = NULL;
183 3290 stmt_rm_ = NULL;
184 3290 stmt_rm_batch_ = NULL;
185 3290 stmt_size_ = NULL;
186 3290 stmt_touch_ = NULL;
187 3290 stmt_unpin_ = NULL;
188 3290 stmt_block_ = NULL;
189 3290 stmt_unblock_ = NULL;
190 3290 stmt_new_ = NULL;
191 3290 database_ = NULL;
192
193 3290 pinned_chunks_.clear();
194 3290 }
195
196
197 2492 void PosixQuotaManager::CloseReturnPipe(int pipe[2]) {
198
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 2436 times.
2492 if (shared_) {
199 56 close(pipe[0]);
200 56 UnlinkReturnPipe(pipe[1]);
201 } else {
202 2436 ClosePipe(pipe);
203 }
204 2492 }
205
206
207 2802124 bool PosixQuotaManager::Contains(const string &hash_str) {
208 2802124 bool result = false;
209
210 2802124 sqlite3_bind_text(stmt_size_, 1, &hash_str[0], hash_str.length(),
211 SQLITE_STATIC);
212
2/2
✓ Branch 1 taken 651 times.
✓ Branch 2 taken 2801473 times.
2802124 if (sqlite3_step(stmt_size_) == SQLITE_ROW)
213 651 result = true;
214 2802124 sqlite3_reset(stmt_size_);
215 2802124 LogCvmfs(kLogQuota, kLogDebug, "contains %s returns %d", hash_str.c_str(),
216 result);
217
218 2802124 return result;
219 }
220
221
222 3235 void PosixQuotaManager::CheckFreeSpace() {
223
3/4
✓ Branch 0 taken 3235 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 3207 times.
3235 if ((limit_ == 0) || (gauge_ >= limit_))
224 28 return;
225
226 struct statvfs vfs_info;
227
1/2
✓ Branch 1 taken 3207 times.
✗ Branch 2 not taken.
3207 const int retval = statvfs((cache_dir_ + "/cachedb").c_str(), &vfs_info);
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3207 times.
3207 if (retval != 0) {
229 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
230 "failed to query %s for free space (%d)", cache_dir_.c_str(),
231 ✗ errno);
232 ✗ return;
233 }
234 3207 const int64_t free_space_byte = vfs_info.f_bavail * vfs_info.f_bsize;
235
1/2
✓ Branch 1 taken 3207 times.
✗ Branch 2 not taken.
3207 LogCvmfs(kLogQuota, kLogDebug, "free space: %" PRId64 " MB",
236 free_space_byte / (1024 * 1024));
237
238 3207 const int64_t required_byte = limit_ - gauge_;
239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3207 times.
3207 if (free_space_byte < required_byte) {
240 ✗ LogCvmfs(kLogQuota, kLogSyslogWarn,
241 "too little free space on the file system hosting the cache,"
242 " %" PRId64 " MB available",
243 free_space_byte / (1024 * 1024));
244 }
245 }
246
247
248 3319 PosixQuotaManager *PosixQuotaManager::Create(const string &cache_workspace,
249 const uint64_t limit,
250 const uint64_t cleanup_threshold,
251 const bool rebuild_database) {
252
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 3263 times.
3319 if (cleanup_threshold >= limit) {
253 56 LogCvmfs(kLogQuota, kLogDebug,
254 "invalid parameters: limit %" PRIu64 ", "
255 "cleanup_threshold %" PRIu64,
256 limit, cleanup_threshold);
257 56 return NULL;
258 }
259
260 PosixQuotaManager *quota_manager = new PosixQuotaManager(
261
1/2
✓ Branch 2 taken 3263 times.
✗ Branch 3 not taken.
3263 limit, cleanup_threshold, cache_workspace);
262
263 // Initialize cache catalog
264
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 3235 times.
3263 if (!quota_manager->InitDatabase(rebuild_database)) {
265
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 delete quota_manager;
266 28 return NULL;
267 }
268 3235 quota_manager->CheckFreeSpace();
269 3235 MakePipe(quota_manager->pipe_lru_);
270
271 3235 quota_manager->protocol_revision_ = kProtocolRevision;
272 3235 quota_manager->initialized_ = true;
273 3235 return quota_manager;
274 }
275
276
277 /**
278 * Connects to a running shared local quota manager. Creates one if necessary.
279 */
280 56 PosixQuotaManager *PosixQuotaManager::CreateShared(
281 const std::string &exe_path,
282 const std::string &cache_workspace,
283 const uint64_t limit,
284 const uint64_t cleanup_threshold,
285 bool foreground) {
286 56 string cache_dir;
287 56 string workspace_dir;
288
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 ParseDirectories(cache_workspace, &cache_dir, &workspace_dir);
289
290 pid_t new_cachemgr_pid;
291
292 // Create lock file: only one fuse client at a time
293
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 const int fd_lockfile = LockFile(workspace_dir + "/lock_cachemgr");
294
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 if (fd_lockfile < 0) {
295
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
296
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
56 (workspace_dir + "/lock_cachemgr").c_str(), errno);
297 28 return NULL;
298 }
299
300 PosixQuotaManager *quota_mgr = new PosixQuotaManager(limit, cleanup_threshold,
301
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 cache_workspace);
302 28 quota_mgr->shared_ = true;
303 28 quota_mgr->spawned_ = true;
304
305 // Try to connect to pipe
306
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const string fifo_path = workspace_dir + "/cachemgr";
307
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug, "trying to connect to existing pipe");
308
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (quota_mgr->pipe_lru_[1] >= 0) {
310 ✗ const int fd_lockfile_rw = open((workspace_dir + "/lock_cachemgr").c_str(),
311 O_RDWR, 0600);
312 ✗ unsigned lockfile_magicnumber = 0;
313 ✗ const ssize_t result_mn = SafeRead(fd_lockfile_rw, &lockfile_magicnumber,
314 sizeof(lockfile_magicnumber));
315 ✗ const ssize_t result = SafeRead(fd_lockfile_rw, &new_cachemgr_pid,
316 sizeof(new_cachemgr_pid));
317 ✗ close(fd_lockfile_rw);
318
319 ✗ if ((lockfile_magicnumber != kLockFileMagicNumber) || (result < 0)
320 ✗ || (result_mn < 0)
321 ✗ || (static_cast<size_t>(result) < sizeof(new_cachemgr_pid))) {
322 ✗ if (result != 0) {
323 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
324 "could not read cache manager pid from lockfile");
325 ✗ UnlockFile(fd_lockfile);
326 ✗ delete quota_mgr;
327 ✗ return NULL;
328 } else {
329 // support reload from old versions of the cache manager
330 // lock file is empty in this case, try a plain ReadHalfPipe to get pid
331 ✗ quota_mgr->SetCacheMgrPid(quota_mgr->GetPid());
332 }
333 } else {
334 ✗ quota_mgr->SetCacheMgrPid(new_cachemgr_pid);
335 }
336
337
338 ✗ LogCvmfs(kLogQuota, kLogDebug, "connected to existing cache manager pipe");
339 ✗ quota_mgr->initialized_ = true;
340 ✗ Nonblock2Block(quota_mgr->pipe_lru_[1]);
341 ✗ UnlockFile(fd_lockfile);
342 ✗ quota_mgr->GetLimits(&quota_mgr->limit_, &quota_mgr->cleanup_threshold_);
343 ✗ LogCvmfs(kLogQuota, kLogDebug,
344 "received limit %" PRIu64 ", threshold %" PRIu64,
345 quota_mgr->limit_, quota_mgr->cleanup_threshold_);
346 ✗ if (FileExists(workspace_dir + "/cachemgr.protocol")) {
347 ✗ quota_mgr->protocol_revision_ = quota_mgr->GetProtocolRevision();
348 ✗ LogCvmfs(kLogQuota, kLogDebug, "connected protocol revision %u",
349 quota_mgr->protocol_revision_);
350 } else {
351 ✗ LogCvmfs(kLogQuota, kLogDebug, "connected to ancient cache manager");
352 }
353 ✗ return quota_mgr;
354 }
355 28 const int connect_error = errno;
356
357 // Lock file: let existing cache manager finish first
358
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 const int fd_lockfile_fifo = LockFile(workspace_dir + "/lock_cachemgr.fifo");
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (fd_lockfile_fifo < 0) {
360 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
361 ✗ (workspace_dir + "/lock_cachemgr.fifo").c_str(), errno);
362 ✗ UnlockFile(fd_lockfile);
363 ✗ delete quota_mgr;
364 ✗ return NULL;
365 }
366
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 UnlockFile(fd_lockfile_fifo);
367
368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (connect_error == ENXIO) {
369 ✗ LogCvmfs(kLogQuota, kLogDebug, "left-over FIFO found, unlinking");
370 ✗ unlink(fifo_path.c_str());
371 }
372
373 // Creating a new FIFO for the cache manager (to be bound later)
374 28 int retval = mkfifo(fifo_path.c_str(), 0600);
375
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (retval != 0) {
376 ✗ LogCvmfs(kLogQuota, kLogDebug, "failed to create cache manager FIFO (%d)",
377 ✗ errno);
378 ✗ UnlockFile(fd_lockfile);
379 ✗ delete quota_mgr;
380 ✗ return NULL;
381 }
382
383 // Create new cache manager
384 int pipe_boot[2];
385 int pipe_handshake[2];
386
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 MakePipe(pipe_boot);
387
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 MakePipe(pipe_handshake);
388
389 28 vector<string> command_line;
390
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 command_line.push_back(exe_path);
391
2/4
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
28 command_line.push_back("__cachemgr__");
392
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 command_line.push_back(cache_workspace);
393
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 command_line.push_back(StringifyInt(pipe_boot[1]));
394
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 command_line.push_back(StringifyInt(pipe_handshake[0]));
395
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 command_line.push_back(StringifyInt(limit));
396
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 command_line.push_back(StringifyInt(cleanup_threshold));
397 // do not propagate foreground in order to reliably get pid from exec
398 // instead, daemonize right here
399
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 command_line.push_back(StringifyInt(true)); // foreground
400
3/6
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 28 times.
✗ Branch 8 not taken.
28 command_line.push_back(StringifyInt(GetLogSyslogLevel()));
401
3/6
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 28 times.
✗ Branch 8 not taken.
28 command_line.push_back(StringifyInt(GetLogSyslogFacility()));
402
5/14
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 28 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 28 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 28 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
28 command_line.push_back(GetLogDebugFile() + ":" + GetLogMicroSyslog());
403
404 28 set<int> preserve_filedes;
405
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 preserve_filedes.insert(0);
406
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 preserve_filedes.insert(1);
407
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 preserve_filedes.insert(2);
408
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 preserve_filedes.insert(pipe_boot[1]);
409
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 preserve_filedes.insert(pipe_handshake[0]);
410
411
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (foreground) {
412
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 retval = ManagedExec(command_line, preserve_filedes, map<int, int>(),
413 /*drop_credentials*/ false,
414 /*clear_env*/ false,
415 /*double_fork*/ true, &new_cachemgr_pid);
416 } else {
417 ✗ retval = ExecAsDaemon(command_line, &new_cachemgr_pid);
418 }
419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (!retval) {
420 ✗ UnlockFile(fd_lockfile);
421 ✗ ClosePipe(pipe_boot);
422 ✗ ClosePipe(pipe_handshake);
423 ✗ delete quota_mgr;
424 ✗ LogCvmfs(kLogQuota, kLogDebug, "failed to start cache manager");
425 ✗ return NULL;
426 }
427
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug,
428 "new cache manager pid: %d protocol revision %d", new_cachemgr_pid,
429 QuotaManager::kProtocolRevision);
430 28 quota_mgr->SetCacheMgrPid(new_cachemgr_pid);
431
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
28 const int fd_lockfile_rw = open((workspace_dir + "/lock_cachemgr").c_str(),
432 O_RDWR | O_TRUNC, 0600);
433 28 const unsigned magic_number = PosixQuotaManager::kLockFileMagicNumber;
434
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const bool result_mn = SafeWrite(fd_lockfile_rw, &magic_number,
435 sizeof(magic_number));
436
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const bool result = SafeWrite(fd_lockfile_rw, &new_cachemgr_pid,
437 sizeof(new_cachemgr_pid));
438
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
28 if (!result || !result_mn) {
439 ✗ PANIC(kLogSyslogErr, "could not write cache manager pid to lockfile");
440 }
441
442
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 close(fd_lockfile_rw);
443 // Wait for cache manager to be ready
444
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 close(pipe_boot[1]);
445
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 close(pipe_handshake[0]);
446 char buf;
447
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
28 if (read(pipe_boot[0], &buf, 1) != 1) {
448
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 UnlockFile(fd_lockfile);
449
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 close(pipe_boot[0]);
450
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 close(pipe_handshake[1]);
451
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 delete quota_mgr;
452
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
453 "cache manager did not start");
454 28 return NULL;
455 }
456 ✗ close(pipe_boot[0]);
457
458 // Connect write end
459 ✗ quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
460 ✗ if (quota_mgr->pipe_lru_[1] < 0) {
461 ✗ LogCvmfs(kLogQuota, kLogDebug,
462 ✗ "failed to connect to newly created FIFO (%d)", errno);
463 ✗ close(pipe_handshake[1]);
464 ✗ UnlockFile(fd_lockfile);
465 ✗ delete quota_mgr;
466 ✗ return NULL;
467 }
468
469 // Finalize handshake
470 ✗ buf = 'C';
471 ✗ if (write(pipe_handshake[1], &buf, 1) != 1) {
472 ✗ UnlockFile(fd_lockfile);
473 ✗ close(pipe_handshake[1]);
474 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not finalize handshake");
475 ✗ delete quota_mgr;
476 ✗ return NULL;
477 }
478 ✗ close(pipe_handshake[1]);
479
480 ✗ Nonblock2Block(quota_mgr->pipe_lru_[1]);
481 ✗ LogCvmfs(kLogQuota, kLogDebug, "connected to a new cache manager");
482 ✗ quota_mgr->protocol_revision_ = kProtocolRevision;
483
484 ✗ UnlockFile(fd_lockfile);
485
486 ✗ quota_mgr->initialized_ = true;
487 ✗ quota_mgr->GetLimits(&quota_mgr->limit_, &quota_mgr->cleanup_threshold_);
488 ✗ LogCvmfs(kLogQuota, kLogDebug,
489 "received limit %" PRIu64 ", "
490 "threshold %" PRIu64,
491 quota_mgr->limit_, quota_mgr->cleanup_threshold_);
492 ✗ return quota_mgr;
493 56 }
494
495
496 280 bool PosixQuotaManager::DoCleanup(const uint64_t leave_size) {
497
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 224 times.
280 if (gauge_ <= leave_size)
498 56 return true;
499
500 // TODO(jblomer) transaction
501
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
502 "clean up cache until at most %lu KB is used", leave_size / 1024);
503
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 LogCvmfs(kLogQuota, kLogDebug, "gauge %" PRIu64, gauge_);
504
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 cleanup_recorder_.Tick();
505
506 bool result;
507 224 vector<string> trash;
508
509 // Note that volatile files start counting from the smallest int64 number:
510 // the absolute sequence number with the first bit set in two's complement.
511 // So -1 can be a marker that will never appear in the database.
512 224 int64_t max_acseq = -1;
513 224 std::vector<EvictCandidate> lru_ordered_open;
514
515 do {
516
1/2
✓ Branch 1 taken 1624 times.
✗ Branch 2 not taken.
1624 sqlite3_reset(stmt_lru_);
517
3/4
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 1400 times.
✓ Branch 3 taken 1624 times.
✗ Branch 4 not taken.
1848 sqlite3_bind_int64(stmt_lru_, 1,
518 224 (max_acseq == -1) ? std::numeric_limits<int64_t>::min()
519 : (max_acseq + 1));
520
521 1624 std::vector<EvictCandidate> candidates;
522
1/2
✓ Branch 1 taken 1624 times.
✗ Branch 2 not taken.
1624 candidates.reserve(kEvictBatchSize);
523 1624 string hash_str;
524 1624 unsigned i = 0;
525
3/4
✓ Branch 1 taken 1457904 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1456280 times.
✓ Branch 4 taken 1624 times.
1457904 while (sqlite3_step(stmt_lru_) == SQLITE_ROW) {
526 hash_str = reinterpret_cast<const char *>(
527
2/4
✓ Branch 1 taken 1456280 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1456280 times.
✗ Branch 5 not taken.
1456280 sqlite3_column_text(stmt_lru_, 0));
528
1/2
✓ Branch 2 taken 1456280 times.
✗ Branch 3 not taken.
1456280 LogCvmfs(kLogQuota, kLogDebug, "add %s to candidates for eviction",
529 hash_str.c_str());
530
1/2
✓ Branch 1 taken 1456280 times.
✗ Branch 2 not taken.
1456280 candidates.push_back(
531
1/2
✓ Branch 1 taken 1456280 times.
✗ Branch 2 not taken.
1456280 EvictCandidate(shash::MkFromHexPtr(shash::HexPtr(hash_str)),
532 1456280 sqlite3_column_int64(stmt_lru_, 1),
533
2/4
✓ Branch 1 taken 1456280 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1456280 times.
✗ Branch 5 not taken.
1456280 sqlite3_column_int64(stmt_lru_, 2)));
534 1456280 i++;
535 }
536
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 1596 times.
1624 if (candidates.empty()) {
537
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug, "no more entries to evict");
538 28 break;
539 }
540
541 1596 const unsigned N = candidates.size();
542
543 1596 open_files_.clear();
544
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1596 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3192 open_files_ = (cleanup_unused_first_) ? CollectAllOpenHashes()
545 1596 : std::vector<shash::Short>();
546
547
2/2
✓ Branch 0 taken 1400252 times.
✓ Branch 1 taken 1400 times.
1401652 for (i = 0; i < N; ++i) {
548 // That's a critical condition. We must not delete a not yet inserted
549 // pinned file as it is already reserved (but will be inserted later).
550 // Instead, set the pin bit in the db to not run into an endless loop
551
1/2
✓ Branch 2 taken 1400252 times.
✗ Branch 3 not taken.
1400252 const bool is_pinned = pinned_chunks_.find(candidates[i].hash)
552 2800504 != pinned_chunks_.end();
553
554 // Avoid evicting open files hopping there are enough more recently used
555 // files to satisfy the cleanup request
556 /*
557 const bool is_open = std::find_if(
558 open_files_.begin(), open_files_.end(),
559 [&candidates, &i](const auto &elem) -> bool
560 { return elem.Collide(candidates[i].hash);
561 })
562 != open_files_.end();
563 */
564 1400252 bool is_open = false;
565
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1400252 times.
1400252 for (auto it = open_files_.begin(); it != open_files_.end(); ++it) {
566 ✗ if (it->Collide(candidates[i].hash)) {
567 ✗ is_open = true;
568 ✗ break;
569 }
570 }
571
572
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1400224 times.
1400252 if (is_pinned) {
573
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 SkipEviction(candidates[i]);
574 28 continue;
575 }
576
577
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1400224 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1400224 if (cleanup_unused_first_ and is_open) {
578 ✗ SkipEviction(candidates[i]);
579 ✗ lru_ordered_open.push_back(candidates[i]);
580 ✗ continue;
581 }
582
583
2/4
✓ Branch 1 taken 1400224 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1400224 times.
✗ Branch 5 not taken.
2800448 trash.push_back(cache_dir_ + "/"
584
2/4
✓ Branch 2 taken 1400224 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1400224 times.
✗ Branch 6 not taken.
4200672 + candidates[i].hash.MakePathWithoutSuffix());
585 1400224 gauge_ -= candidates[i].size;
586 1400224 max_acseq = candidates[i].acseq;
587
1/2
✓ Branch 2 taken 1400224 times.
✗ Branch 3 not taken.
1400224 LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
588
1/2
✓ Branch 2 taken 1400224 times.
✗ Branch 3 not taken.
2800448 candidates[i].hash.ToString().c_str(), gauge_);
589
590
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 1400028 times.
1400224 if (gauge_ <= leave_size)
591 196 break;
592 }
593
6/6
✓ Branch 1 taken 1596 times.
✓ Branch 2 taken 28 times.
✓ Branch 4 taken 1596 times.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 1400 times.
✓ Branch 7 taken 196 times.
3248 } while (gauge_ > leave_size);
594
595
1/2
✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
224 if (max_acseq != -1) {
596
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 sqlite3_bind_int64(stmt_rm_batch_, 1, max_acseq);
597
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 result = (sqlite3_step(stmt_rm_batch_) == SQLITE_DONE);
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 assert(result);
599
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 sqlite3_reset(stmt_rm_batch_);
600
601
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 result = (sqlite3_step(stmt_unblock_) == SQLITE_DONE);
602
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 sqlite3_reset(stmt_unblock_);
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 assert(result);
604 }
605
606
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 224 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 224 times.
224 while (!lru_ordered_open.empty() and gauge_ > leave_size) {
607 // cleanup files in use
608 ✗ auto &candidate = lru_ordered_open[0];
609 ✗ trash.push_back(cache_dir_ + "/" + candidate.hash.MakePathWithoutSuffix());
610 ✗ gauge_ -= candidate.size;
611 ✗ max_acseq = candidate.acseq;
612 ✗ LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
613 ✗ candidate.hash.ToString().c_str(), gauge_);
614 ✗ lru_ordered_open.erase(lru_ordered_open.begin());
615 }
616
617
2/4
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 224 times.
224 if (!EmptyTrash(trash))
618 ✗ return false;
619
620
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 196 times.
224 if (gauge_ > leave_size) {
621
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
622 "request to clean until %" PRIu64 ", "
623 "but effective gauge is %" PRIu64,
624 leave_size, gauge_);
625 28 return false;
626 }
627 196 return true;
628 224 }
629
630 224 bool PosixQuotaManager::EmptyTrash(const std::vector<std::string> &trash) {
631
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 224 times.
224 if (trash.empty())
632 ✗ return true;
633
634
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 56 times.
224 if (async_delete_) {
635 // Double fork avoids zombie, forked removal process must not flush file
636 // buffers
637 pid_t pid;
638 int statloc;
639
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 168 times.
168 if ((pid = fork()) == 0) {
640 // TODO(jblomer): eviciting files in the cache should perhaps become a
641 // thread. This would also allow to block the chunks and prevent the
642 // race with re-insertion. Then again, a thread can block umount.
643 #ifndef DEBUGMSG
644 ✗ CloseAllFildes(std::set<int>());
645 #endif
646 ✗ if (fork() == 0) {
647 ✗ for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
648 ✗ LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
649 ✗ unlink(trash[i].c_str());
650 }
651 ✗ _exit(0);
652 }
653 ✗ _exit(0);
654 } else {
655
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 if (pid > 0)
656
1/2
✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
168 waitpid(pid, &statloc, 0);
657 else
658 ✗ return false;
659 }
660 } else { // !async_delete_
661
2/2
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 56 times.
140 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
662 84 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
663 84 unlink(trash[i].c_str());
664 }
665 }
666 224 return true;
667 }
668
669
670 2801330 void PosixQuotaManager::DoInsert(const shash::Any &hash,
671 const uint64_t size,
672 const string &description,
673 const CommandType command_type) {
674
1/2
✓ Branch 1 taken 2801330 times.
✗ Branch 2 not taken.
2801330 const string hash_str = hash.ToString();
675
1/2
✓ Branch 3 taken 2801330 times.
✗ Branch 4 not taken.
2801330 LogCvmfs(kLogQuota, kLogDebug, "insert into lru %s, path %s, method %d",
676 hash_str.c_str(), description.c_str(), command_type);
677 2801330 const unsigned desc_length = (description.length() > kMaxDescription)
678 ? kMaxDescription
679
1/2
✓ Branch 0 taken 2801330 times.
✗ Branch 1 not taken.
2801330 : description.length();
680
681 LruCommand *cmd = reinterpret_cast<LruCommand *>(
682 2801330 alloca(sizeof(LruCommand) + desc_length));
683 2801330 new (cmd) LruCommand;
684 2801330 cmd->command_type = command_type;
685 2801330 cmd->SetSize(size);
686
1/2
✓ Branch 1 taken 2801330 times.
✗ Branch 2 not taken.
2801330 cmd->StoreHash(hash);
687 2801330 cmd->desc_length = desc_length;
688 2801330 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &description[0],
689 desc_length);
690
1/2
✓ Branch 1 taken 2801330 times.
✗ Branch 2 not taken.
2801330 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
691 2801330 }
692
693
694 1036 vector<string> PosixQuotaManager::DoList(const CommandType list_command) {
695 1036 vector<string> result;
696
697 int pipe_list[2];
698
1/2
✓ Branch 1 taken 1036 times.
✗ Branch 2 not taken.
1036 MakeReturnPipe(pipe_list);
699 char description_buffer[kMaxDescription];
700
701 1036 LruCommand cmd;
702 1036 cmd.command_type = list_command;
703 1036 cmd.return_pipe = pipe_list[1];
704
1/2
✓ Branch 1 taken 1036 times.
✗ Branch 2 not taken.
1036 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
705
706 int length;
707 do {
708
1/2
✓ Branch 1 taken 2802184 times.
✗ Branch 2 not taken.
2802184 ManagedReadHalfPipe(pipe_list[0], &length, sizeof(length));
709
2/2
✓ Branch 0 taken 2801148 times.
✓ Branch 1 taken 1036 times.
2802184 if (length > 0) {
710
1/2
✓ Branch 1 taken 2801148 times.
✗ Branch 2 not taken.
2801148 ReadPipe(pipe_list[0], description_buffer, length);
711
2/4
✓ Branch 2 taken 2801148 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2801148 times.
✗ Branch 6 not taken.
2801148 result.push_back(string(description_buffer, length));
712 }
713
2/2
✓ Branch 0 taken 2801148 times.
✓ Branch 1 taken 1036 times.
2802184 } while (length >= 0);
714
715
1/2
✓ Branch 1 taken 1036 times.
✗ Branch 2 not taken.
1036 CloseReturnPipe(pipe_list);
716 2072 return result;
717 }
718
719
720 1611 uint64_t PosixQuotaManager::GetCapacity() {
721
1/2
✓ Branch 0 taken 1611 times.
✗ Branch 1 not taken.
1611 if (limit_ != (uint64_t)(-1))
722 1611 return limit_;
723
724 // Unrestricted cache, look at free space on cache dir fs
725 struct statfs info;
726 ✗ if (statfs(".", &info) == 0) {
727 ✗ return info.f_bavail * info.f_bsize;
728 } else {
729 ✗ LogCvmfs(kLogQuota, kLogSyslogErr | kLogDebug,
730 ✗ "failed to query file system info of cache (%d)", errno);
731 ✗ return limit_;
732 }
733 }
734
735
736 ✗ void PosixQuotaManager::GetLimits(uint64_t *limit,
737 uint64_t *cleanup_threshold) {
738 int pipe_limits[2];
739 ✗ MakeReturnPipe(pipe_limits);
740
741 ✗ LruCommand cmd;
742 ✗ cmd.command_type = kLimits;
743 ✗ cmd.return_pipe = pipe_limits[1];
744 ✗ WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
745 ✗ ManagedReadHalfPipe(pipe_limits[0], limit, sizeof(*limit));
746 ✗ ReadPipe(pipe_limits[0], cleanup_threshold, sizeof(*cleanup_threshold));
747 ✗ CloseReturnPipe(pipe_limits);
748 }
749
750
751 /**
752 * Since we only cleanup until cleanup_threshold, we can only add
753 * files smaller than limit-cleanup_threshold.
754 */
755 275 uint64_t PosixQuotaManager::GetMaxFileSize() {
756 275 return limit_ - cleanup_threshold_;
757 }
758
759
760 28 pid_t PosixQuotaManager::GetPid() {
761
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
28 if (!shared_ || !spawned_) {
762 28 return getpid();
763 }
764 ✗ if (cachemgr_pid_) {
765 ✗ return cachemgr_pid_;
766 }
767
768 pid_t result;
769 int pipe_pid[2];
770 ✗ MakeReturnPipe(pipe_pid);
771
772 ✗ LruCommand cmd;
773 ✗ cmd.command_type = kPid;
774 ✗ cmd.return_pipe = pipe_pid[1];
775 ✗ WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
776 ✗ ReadHalfPipe(pipe_pid[0], &result, sizeof(result));
777 ✗ CloseReturnPipe(pipe_pid);
778 ✗ return result;
779 }
780
781
782 28 uint32_t PosixQuotaManager::GetProtocolRevision() {
783 int pipe_revision[2];
784
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 MakeReturnPipe(pipe_revision);
785
786 28 LruCommand cmd;
787 28 cmd.command_type = kGetProtocolRevision;
788 28 cmd.return_pipe = pipe_revision[1];
789
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
790
791 uint32_t revision;
792
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 ManagedReadHalfPipe(pipe_revision[0], &revision, sizeof(revision));
793
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 CloseReturnPipe(pipe_revision);
794 28 return revision;
795 }
796
797 1611 void PosixQuotaManager::SetCleanupPolicy(bool cleanup_unused_first) {
798
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1611 times.
1611 if (protocol_revision_ < 3)
799 ✗ return;
800
801
3/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1583 times.
✓ Branch 3 taken 1611 times.
✗ Branch 4 not taken.
1611 LogCvmfs(
802 kLogQuota, kLogDebug, "Set cleanup policy to %s",
803 (cleanup_unused_first) ? "cleanup unused files first." : "vanilla lru.");
804
805
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1583 times.
1611 char policy = (cleanup_unused_first) ? 'S' : 'R'; // S: smart, R: regular;
806
807 LruCommand *cmd = reinterpret_cast<LruCommand *>(
808 1611 alloca(sizeof(LruCommand) + sizeof(policy)));
809 1611 new (cmd) LruCommand;
810 1611 cmd->command_type = kSetCleanupPolicy;
811 1611 cmd->desc_length = sizeof(policy);
812 1611 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &policy,
813 sizeof(policy));
814
1/2
✓ Branch 1 taken 1611 times.
✗ Branch 2 not taken.
1611 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + sizeof(policy));
815 }
816
817 28 void PosixQuotaManager::RegisterMountpoint(const std::string &mountpoint) {
818
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (protocol_revision_ < 3)
819 ✗ return;
820
821 28 LogCvmfs(kLogQuota, kLogDebug, "Register Mountpoint %s", mountpoint.c_str());
822
823 28 const unsigned desc_length = (mountpoint.size() > kMaxDescription)
824 ? kMaxDescription
825
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 : mountpoint.size();
826 LruCommand *cmd = reinterpret_cast<LruCommand *>(
827 28 alloca(sizeof(LruCommand) + desc_length));
828 28 new (cmd) LruCommand;
829 28 cmd->command_type = kRegisterMountpoint;
830 28 cmd->desc_length = desc_length;
831 28 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), mountpoint.data(),
832 desc_length);
833 28 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
834 }
835
836 28 std::string PosixQuotaManager::ReadPipeString(int fd, size_t size) {
837
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (size == 0)
838 ✗ return "";
839
840
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 std::vector<char> buf(size);
841
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 ManagedReadHalfPipe(fd, buf.data(), size);
842
1/2
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
28 return std::string(buf.data(), size);
843 28 }
844
845 ✗ std::string PosixQuotaManager::GetMountpoints() {
846 ✗ if (protocol_revision_ < 3)
847 ✗ return "";
848
849 int pipe_mp[2];
850 ✗ MakeReturnPipe(pipe_mp);
851
852 ✗ LruCommand cmd;
853 ✗ cmd.command_type = kGetMountpoints;
854 ✗ cmd.return_pipe = pipe_mp[1];
855 ✗ WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
856 ✗ size_t mp_str_size = 0;
857 ✗ ManagedReadHalfPipe(pipe_mp[0], &mp_str_size, sizeof(size_t));
858 ✗ const std::string result = ReadPipeString(pipe_mp[0], mp_str_size);
859 ✗ CloseReturnPipe(pipe_mp);
860 ✗ return result;
861 }
862
863 ✗ std::string PosixQuotaManager::GetGroupHashes() {
864 ✗ if (protocol_revision_ < 3)
865 ✗ return "";
866
867 int pipe_gh[2];
868 ✗ MakeReturnPipe(pipe_gh);
869
870 ✗ LruCommand cmd;
871 ✗ cmd.command_type = kGetGroupHashes;
872 ✗ cmd.return_pipe = pipe_gh[1];
873 ✗ WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
874 ✗ size_t mp_str_size = 0;
875 ✗ ManagedReadHalfPipe(pipe_gh[0], &mp_str_size, sizeof(size_t));
876 ✗ const std::string result = ReadPipeString(pipe_gh[0], mp_str_size);
877 ✗ CloseReturnPipe(pipe_gh);
878 ✗ return result;
879 }
880
881 /**
882 * Queries the shared local hard disk quota manager.
883 */
884 504 void PosixQuotaManager::GetSharedStatus(uint64_t *gauge, uint64_t *pinned) {
885 int pipe_status[2];
886
1/2
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
504 MakeReturnPipe(pipe_status);
887
888 504 LruCommand cmd;
889 504 cmd.command_type = kStatus;
890 504 cmd.return_pipe = pipe_status[1];
891
1/2
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
504 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
892
1/2
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
504 ManagedReadHalfPipe(pipe_status[0], gauge, sizeof(*gauge));
893
1/2
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
504 ReadPipe(pipe_status[0], pinned, sizeof(*pinned));
894
1/2
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
504 CloseReturnPipe(pipe_status);
895 504 }
896
897 28 bool PosixQuotaManager::SetSharedLimit(uint64_t limit) {
898 int pipe_set_limit[2];
899 bool result;
900
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 MakeReturnPipe(pipe_set_limit);
901
902 28 LruCommand cmd;
903 28 cmd.command_type = kSetLimit;
904 28 cmd.size = limit;
905 28 cmd.return_pipe = pipe_set_limit[1];
906
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
907
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 ReadHalfPipe(pipe_set_limit[0], &result, sizeof(result));
908
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 CloseReturnPipe(pipe_set_limit);
909 28 return result;
910 }
911
912
913 28 bool PosixQuotaManager::SetLimit(uint64_t size) {
914
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (!spawned_) {
915 ✗ limit_ = size;
916 ✗ cleanup_threshold_ = size / 2;
917 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
918 "Quota limit set to %lu / threshold %lu", limit_,
919 cleanup_threshold_);
920 ✗ return true;
921 }
922 28 return SetSharedLimit(size);
923 }
924
925 3614 uint64_t PosixQuotaManager::GetSize() {
926
2/2
✓ Branch 0 taken 3166 times.
✓ Branch 1 taken 448 times.
3614 if (!spawned_)
927 3166 return gauge_;
928 uint64_t gauge, size_pinned;
929
1/2
✓ Branch 1 taken 448 times.
✗ Branch 2 not taken.
448 GetSharedStatus(&gauge, &size_pinned);
930 448 return gauge;
931 }
932
933
934 56 uint64_t PosixQuotaManager::GetSizePinned() {
935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 if (!spawned_)
936 ✗ return pinned_;
937 uint64_t gauge, size_pinned;
938
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 GetSharedStatus(&gauge, &size_pinned);
939 56 return size_pinned;
940 }
941
942
943 112 uint64_t PosixQuotaManager::GetCleanupRate(uint64_t period_s) {
944
2/4
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 112 times.
112 if (!spawned_ || (protocol_revision_ < 2))
945 ✗ return 0;
946 uint64_t cleanup_rate;
947
948 int pipe_cleanup_rate[2];
949
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 MakeReturnPipe(pipe_cleanup_rate);
950 112 LruCommand cmd;
951 112 cmd.command_type = kCleanupRate;
952 112 cmd.size = period_s;
953 112 cmd.return_pipe = pipe_cleanup_rate[1];
954
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
955
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 ManagedReadHalfPipe(pipe_cleanup_rate[0], &cleanup_rate,
956 sizeof(cleanup_rate));
957
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 CloseReturnPipe(pipe_cleanup_rate);
958
959 112 return cleanup_rate;
960 }
961
962
963 3403 bool PosixQuotaManager::InitDatabase(const bool rebuild_database) {
964 3403 string sql;
965 sqlite3_stmt *stmt;
966
967
2/4
✓ Branch 1 taken 3403 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3403 times.
✗ Branch 5 not taken.
3403 fd_lock_cachedb_ = LockFile(workspace_dir_ + "/lock_cachedb");
968
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 3375 times.
3403 if (fd_lock_cachedb_ < 0) {
969
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug, "failed to create cachedb lock");
970 28 return false;
971 }
972
973 3375 bool retry = false;
974
1/2
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
3375 const string db_file = cache_dir_ + "/cachedb";
975
2/2
✓ Branch 0 taken 3216 times.
✓ Branch 1 taken 159 times.
3375 if (rebuild_database) {
976
1/2
✓ Branch 2 taken 159 times.
✗ Branch 3 not taken.
159 LogCvmfs(kLogQuota, kLogDebug, "rebuild database, unlinking existing (%s)",
977 db_file.c_str());
978 159 unlink(db_file.c_str());
979
1/2
✓ Branch 1 taken 159 times.
✗ Branch 2 not taken.
159 unlink((db_file + "-journal").c_str());
980 }
981
982 3216 init_recover:
983
1/2
✓ Branch 2 taken 3375 times.
✗ Branch 3 not taken.
3375 int err = sqlite3_open(db_file.c_str(), &database_);
984
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3375 times.
3375 if (err != SQLITE_OK) {
985 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not open cache database (%d)", err);
986 ✗ goto init_database_fail;
987 }
988 // TODO(reneme): make this a `QuotaDatabase : public sqlite::Database`
989 sql = "PRAGMA synchronous=0; PRAGMA locking_mode=EXCLUSIVE; "
990 "PRAGMA auto_vacuum=1; "
991 "CREATE TABLE IF NOT EXISTS cache_catalog (sha1 TEXT, size INTEGER, "
992 " acseq INTEGER, path TEXT, type INTEGER, pinned INTEGER, "
993 "CONSTRAINT pk_cache_catalog PRIMARY KEY (sha1)); "
994 "CREATE UNIQUE INDEX IF NOT EXISTS idx_cache_catalog_acseq "
995 " ON cache_catalog (acseq); "
996 "CREATE TEMP TABLE fscache (sha1 TEXT, size INTEGER, actime INTEGER, "
997 "CONSTRAINT pk_fscache PRIMARY KEY (sha1)); "
998 "CREATE INDEX idx_fscache_actime ON fscache (actime); "
999 "CREATE TABLE IF NOT EXISTS properties (key TEXT, value TEXT, "
1000
1/2
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
3375 " CONSTRAINT pk_properties PRIMARY KEY(key));";
1001
1/2
✓ Branch 2 taken 3375 times.
✗ Branch 3 not taken.
3375 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3375 times.
3375 if (err != SQLITE_OK) {
1003 ✗ if (!retry) {
1004 ✗ retry = true;
1005 ✗ sqlite3_close(database_);
1006 ✗ unlink(db_file.c_str());
1007 ✗ unlink((db_file + "-journal").c_str());
1008 ✗ LogCvmfs(kLogQuota, kLogSyslogWarn,
1009 "LRU database corrupted, re-building");
1010 ✗ goto init_recover;
1011 }
1012 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
1013 sql.c_str());
1014 ✗ goto init_database_fail;
1015 }
1016
1017 // If this an old cache catalog,
1018 // add and initialize new columns to cache_catalog
1019 sql = "ALTER TABLE cache_catalog ADD type INTEGER; "
1020
1/2
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
3375 "ALTER TABLE cache_catalog ADD pinned INTEGER";
1021
1/2
✓ Branch 2 taken 3375 times.
✗ Branch 3 not taken.
3375 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3375 times.
3375 if (err == SQLITE_OK) {
1023 ✗ sql = "UPDATE cache_catalog SET type=" + StringifyInt(kFileRegular) + ";";
1024 ✗ err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1025 ✗ if (err != SQLITE_OK) {
1026 ✗ LogCvmfs(kLogQuota, kLogDebug,
1027 "could not init cache database (failed: %s)", sql.c_str());
1028 ✗ goto init_database_fail;
1029 }
1030 }
1031
1032 // Set pinned back
1033
1/2
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
3375 sql = "UPDATE cache_catalog SET pinned=0;";
1034
1/2
✓ Branch 2 taken 3375 times.
✗ Branch 3 not taken.
3375 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1035
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3375 times.
3375 if (err != SQLITE_OK) {
1036 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
1037 sql.c_str());
1038 ✗ goto init_database_fail;
1039 }
1040
1041 // Set schema version
1042 sql = "INSERT OR REPLACE INTO properties (key, value) "
1043
1/2
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
3375 "VALUES ('schema', '1.0')";
1044
1/2
✓ Branch 2 taken 3375 times.
✗ Branch 3 not taken.
3375 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1045
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3375 times.
3375 if (err != SQLITE_OK) {
1046 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
1047 sql.c_str());
1048 ✗ goto init_database_fail;
1049 }
1050
1051 // If cache catalog is empty, recreate from file system
1052
1/2
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
3375 sql = "SELECT count(*) FROM cache_catalog;";
1053
1/2
✓ Branch 2 taken 3375 times.
✗ Branch 3 not taken.
3375 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1054
2/4
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3375 times.
✗ Branch 4 not taken.
3375 if (sqlite3_step(stmt) == SQLITE_ROW) {
1055
6/8
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 112 times.
✓ Branch 4 taken 3263 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 112 times.
✓ Branch 7 taken 3263 times.
✓ Branch 8 taken 112 times.
3375 if ((sqlite3_column_int64(stmt, 0)) == 0 || rebuild_database) {
1056
1/2
✓ Branch 1 taken 3263 times.
✗ Branch 2 not taken.
3263 LogCvmfs(kLogCvmfs, kLogDebug,
1057 "CernVM-FS: building lru cache database...");
1058
3/4
✓ Branch 1 taken 3263 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
✓ Branch 4 taken 3179 times.
3263 if (!RebuildDatabase()) {
1059
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 LogCvmfs(kLogQuota, kLogDebug,
1060 "could not build cache database from file system");
1061
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 sqlite3_finalize(stmt);
1062 84 goto init_database_fail;
1063 }
1064 }
1065
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_finalize(stmt);
1066 } else {
1067 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not select on cache catalog");
1068 ✗ sqlite3_finalize(stmt);
1069 ✗ goto init_database_fail;
1070 }
1071
1072 // How many bytes do we already have in cache?
1073
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sql = "SELECT sum(size) FROM cache_catalog;";
1074
1/2
✓ Branch 2 taken 3291 times.
✗ Branch 3 not taken.
3291 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1075
2/4
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3291 times.
✗ Branch 4 not taken.
3291 if (sqlite3_step(stmt) == SQLITE_ROW) {
1076
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 gauge_ = sqlite3_column_int64(stmt, 0);
1077 } else {
1078 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not determine cache size");
1079 ✗ sqlite3_finalize(stmt);
1080 ✗ goto init_database_fail;
1081 }
1082
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_finalize(stmt);
1083
1084 // Highest seq-no?
1085
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sql = "SELECT coalesce(max(acseq & (~(1<<63))), 0) FROM cache_catalog;";
1086
1/2
✓ Branch 2 taken 3291 times.
✗ Branch 3 not taken.
3291 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1087
2/4
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3291 times.
✗ Branch 4 not taken.
3291 if (sqlite3_step(stmt) == SQLITE_ROW) {
1088
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 seq_ = sqlite3_column_int64(stmt, 0) + 1;
1089 } else {
1090 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not determine highest seq-no");
1091 ✗ sqlite3_finalize(stmt);
1092 ✗ goto init_database_fail;
1093 }
1094
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_finalize(stmt);
1095
1096 // Prepare touch, new, remove statements
1097
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1098 "UPDATE cache_catalog SET acseq=:seq | (acseq&(1<<63)) "
1099 "WHERE sha1=:sha1;",
1100 -1, &stmt_touch_, NULL);
1101
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1102 "UPDATE cache_catalog SET pinned=0 "
1103 "WHERE sha1=:sha1;",
1104 -1, &stmt_unpin_, NULL);
1105
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1106 "UPDATE cache_catalog SET pinned=2 "
1107 "WHERE sha1=:sha1;",
1108 -1, &stmt_block_, NULL);
1109
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1110 "UPDATE cache_catalog SET pinned=1 "
1111 "WHERE pinned=2;",
1112 -1, &stmt_unblock_, NULL);
1113
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1114 "INSERT OR REPLACE INTO cache_catalog "
1115 "(sha1, size, acseq, path, type, pinned) "
1116 "VALUES (:sha1, :s, :seq, :p, :t, :pin);",
1117 -1, &stmt_new_, NULL);
1118
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1119 "SELECT size, pinned FROM cache_catalog WHERE sha1=:sha1;",
1120 -1, &stmt_size_, NULL);
1121
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_, "DELETE FROM cache_catalog WHERE sha1=:sha1;",
1122 -1, &stmt_rm_, NULL);
1123
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1124 "DELETE FROM cache_catalog WHERE acseq<=:a AND pinned<>2;",
1125 -1, &stmt_rm_batch_, NULL);
1126
1/2
✓ Branch 2 taken 3291 times.
✗ Branch 3 not taken.
3291 sqlite3_prepare_v2(database_,
1127
1/2
✓ Branch 2 taken 3291 times.
✗ Branch 3 not taken.
6582 (std::string("SELECT sha1, size, acseq FROM cache_catalog "
1128 "WHERE pinned<>2 AND acseq>=:a "
1129 "ORDER BY acseq ASC "
1130 "LIMIT ")
1131
3/6
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3291 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3291 times.
✗ Branch 8 not taken.
13164 + StringifyInt(kEvictBatchSize) + ";")
1132 .c_str(),
1133 -1, &stmt_lru_, NULL);
1134
1/2
✓ Branch 2 taken 3291 times.
✗ Branch 3 not taken.
3291 sqlite3_prepare_v2(database_,
1135 ("SELECT path FROM cache_catalog WHERE type="
1136
3/6
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3291 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3291 times.
✗ Branch 8 not taken.
6582 + StringifyInt(kFileRegular) + ";")
1137 .c_str(),
1138 -1, &stmt_list_, NULL);
1139
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1140 "SELECT path FROM cache_catalog WHERE pinned<>0;", -1,
1141 &stmt_list_pinned_, NULL);
1142
1/2
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
3291 sqlite3_prepare_v2(database_,
1143 "SELECT path FROM cache_catalog WHERE acseq < 0;", -1,
1144 &stmt_list_volatile_, NULL);
1145
1/2
✓ Branch 2 taken 3291 times.
✗ Branch 3 not taken.
3291 sqlite3_prepare_v2(database_,
1146 ("SELECT path FROM cache_catalog WHERE type="
1147
3/6
✓ Branch 1 taken 3291 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3291 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3291 times.
✗ Branch 8 not taken.
6582 + StringifyInt(kFileCatalog) + ";")
1148 .c_str(),
1149 -1, &stmt_list_catalogs_, NULL);
1150 3291 return true;
1151
1152 84 init_database_fail:
1153
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 sqlite3_close(database_);
1154 84 database_ = NULL;
1155
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 UnlockFile(fd_lock_cachedb_);
1156 84 return false;
1157 3403 }
1158
1159
1160 /**
1161 * Inserts a new file into cache catalog. This file gets a new,
1162 * highest sequence number. Does cache cleanup if necessary.
1163 */
1164 2800854 void PosixQuotaManager::Insert(const shash::Any &any_hash,
1165 const uint64_t size,
1166 const string &description) {
1167 2800854 DoInsert(any_hash, size, description, kInsert);
1168 2800854 }
1169
1170
1171 /**
1172 * Inserts a new file into cache catalog. This file is marked as volatile
1173 * and gets a new highest sequence number with the first bit set. Cache cleanup
1174 * treats these files with priority.
1175 */
1176 112 void PosixQuotaManager::InsertVolatile(const shash::Any &any_hash,
1177 const uint64_t size,
1178 const string &description) {
1179 112 DoInsert(any_hash, size, description, kInsertVolatile);
1180 112 }
1181
1182
1183 /**
1184 * Lists all path names from the cache db.
1185 */
1186 644 vector<string> PosixQuotaManager::List() { return DoList(kList); }
1187
1188
1189 /**
1190 * Lists all pinned files from the cache db.
1191 */
1192 224 vector<string> PosixQuotaManager::ListPinned() { return DoList(kListPinned); }
1193
1194
1195 /**
1196 * Lists all sqlite catalog files from the cache db.
1197 */
1198 84 vector<string> PosixQuotaManager::ListCatalogs() {
1199 84 return DoList(kListCatalogs);
1200 }
1201
1202
1203 /**
1204 * Lists only files flagged as volatile (priority removal)
1205 */
1206 84 vector<string> PosixQuotaManager::ListVolatile() {
1207 84 return DoList(kListVolatile);
1208 }
1209
1210
1211 /**
1212 * Entry point for the shared cache manager process
1213 */
1214 ✗ int PosixQuotaManager::MainCacheManager(int argc, char **argv) {
1215 ✗ LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1216 int retval;
1217
1218 ✗ PosixQuotaManager shared_manager(0, 0, "");
1219 ✗ shared_manager.shared_ = true;
1220 ✗ shared_manager.spawned_ = true;
1221 ✗ shared_manager.pinned_ = 0;
1222
1223 // Process command line arguments
1224 ✗ ParseDirectories(string(argv[2]),
1225 &shared_manager.cache_dir_,
1226 &shared_manager.workspace_dir_);
1227 ✗ const int pipe_boot = String2Int64(argv[3]);
1228 ✗ const int pipe_handshake = String2Int64(argv[4]);
1229 ✗ shared_manager.limit_ = String2Int64(argv[5]);
1230 ✗ shared_manager.cleanup_threshold_ = String2Int64(argv[6]);
1231 ✗ const int foreground = String2Int64(argv[7]);
1232 ✗ const int syslog_level = String2Int64(argv[8]);
1233 ✗ const int syslog_facility = String2Int64(argv[9]);
1234 ✗ vector<string> logfiles = SplitString(argv[10], ':');
1235
1236 ✗ SetLogSyslogLevel(syslog_level);
1237 ✗ SetLogSyslogFacility(syslog_facility);
1238 ✗ if ((logfiles.size() > 0) && (logfiles[0] != ""))
1239 ✗ SetLogDebugFile(logfiles[0] + ".cachemgr");
1240 ✗ if (logfiles.size() > 1)
1241 ✗ SetLogMicroSyslog(logfiles[1]);
1242
1243 ✗ if (!foreground)
1244 ✗ Daemonize();
1245
1246 ✗ if ((geteuid() != 0) && SetuidCapabilityPermitted()) {
1247 // Permanently drop credentials
1248 ✗ const std::vector<cap_value_t> nocaps;
1249 ✗ if (!ClearPermittedCapabilities(nocaps, nocaps))
1250 ✗ PANIC(kLogStderr | kLogSyslogErr,
1251 "Failed to clear quota manager capabilities");
1252 // Leave this process ptraceable
1253 ✗ if (!platform_set_dumpable())
1254 ✗ PANIC(kLogStderr | kLogSyslogErr,
1255 "Failed to make quota manager process ptraceable");
1256 // but without core dumps
1257 ✗ if (!SetLimitCore(0))
1258 ✗ PANIC(kLogStderr | kLogSyslogErr,
1259 "Failed to disable quota manager core dumps");
1260 }
1261
1262 const std::unique_ptr<Watchdog> watchdog(
1263 ✗ Watchdog::Create(NULL, false /* needs_read_environ */));
1264 ✗ assert(watchdog.get() != nullptr);
1265 ✗ watchdog->Spawn("./stacktrace.cachemgr");
1266
1267 // Initialize pipe, open non-blocking as cvmfs is not yet connected
1268 ✗ const int fd_lockfile_fifo = LockFile(shared_manager.workspace_dir_
1269 ✗ + "/lock_cachemgr.fifo");
1270 ✗ if (fd_lockfile_fifo < 0) {
1271 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1272 "could not open lock file "
1273 "%s (%d)",
1274 ✗ (shared_manager.workspace_dir_ + "/lock_cachemgr.fifo").c_str(),
1275 ✗ errno);
1276 ✗ return 1;
1277 }
1278 ✗ const string crash_guard = shared_manager.cache_dir_ + "/cachemgr.running";
1279 ✗ const bool rebuild = FileExists(crash_guard);
1280 ✗ retval = open(crash_guard.c_str(), O_RDONLY | O_CREAT, 0600);
1281 ✗ if (retval < 0) {
1282 ✗ LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1283 "failed to create shared cache manager crash guard");
1284 ✗ UnlockFile(fd_lockfile_fifo);
1285 ✗ return 1;
1286 }
1287 ✗ close(retval);
1288
1289 // Redirect SQlite temp directory to cache (global variable)
1290 ✗ const string tmp_dir = shared_manager.workspace_dir_;
1291 ✗ sqlite3_temp_directory = static_cast<char *>(
1292 ✗ sqlite3_malloc(tmp_dir.length() + 1));
1293 ✗ snprintf(sqlite3_temp_directory, tmp_dir.length() + 1, "%s", tmp_dir.c_str());
1294
1295 // Cleanup leftover named pipes
1296 ✗ shared_manager.CleanupPipes();
1297
1298 ✗ if (!shared_manager.InitDatabase(rebuild)) {
1299 ✗ UnlockFile(fd_lockfile_fifo);
1300 ✗ return 1;
1301 }
1302 ✗ shared_manager.CheckFreeSpace();
1303
1304 // Save protocol revision to file. If the file is not found, it indicates
1305 // to the client that the cache manager is from times before the protocol
1306 // was versioned.
1307 const string protocol_revision_path = shared_manager.workspace_dir_
1308 ✗ + "/cachemgr.protocol";
1309 ✗ retval = open(protocol_revision_path.c_str(), O_WRONLY | O_CREAT, 0600);
1310 ✗ if (retval < 0) {
1311 ✗ LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1312 ✗ "failed to open protocol revision file (%d)", errno);
1313 ✗ UnlockFile(fd_lockfile_fifo);
1314 ✗ return 1;
1315 }
1316 ✗ const string revision = StringifyInt(kProtocolRevision);
1317 ✗ const int written = write(retval, revision.data(), revision.length());
1318 ✗ close(retval);
1319 ✗ if ((written < 0) || static_cast<unsigned>(written) != revision.length()) {
1320 ✗ LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1321 ✗ "failed to write protocol revision (%d)", errno);
1322 ✗ UnlockFile(fd_lockfile_fifo);
1323 ✗ return 1;
1324 }
1325
1326 ✗ const string fifo_path = shared_manager.workspace_dir_ + "/cachemgr";
1327 ✗ shared_manager.pipe_lru_[0] = open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK);
1328 ✗ if (shared_manager.pipe_lru_[0] < 0) {
1329 ✗ LogCvmfs(kLogQuota, kLogDebug, "failed to listen on FIFO %s (%d)",
1330 ✗ fifo_path.c_str(), errno);
1331 ✗ UnlockFile(fd_lockfile_fifo);
1332 ✗ return 1;
1333 }
1334 ✗ Nonblock2Block(shared_manager.pipe_lru_[0]);
1335 ✗ LogCvmfs(kLogQuota, kLogDebug, "shared cache manager listening");
1336
1337 ✗ char buf = 'C';
1338 ✗ WritePipe(pipe_boot, &buf, 1);
1339 ✗ close(pipe_boot);
1340
1341 ✗ ReadPipe(pipe_handshake, &buf, 1);
1342 ✗ close(pipe_handshake);
1343 ✗ LogCvmfs(kLogQuota, kLogDebug, "shared cache manager handshake done");
1344
1345 // Ensure that broken pipes from clients do not kill the cache manager
1346 ✗ signal(SIGPIPE, SIG_IGN);
1347 // Don't let Ctrl-C ungracefully kill interactive session
1348 ✗ signal(SIGINT, SIG_IGN);
1349
1350 ✗ shared_manager.MainCommandServer(&shared_manager);
1351 ✗ unlink(fifo_path.c_str());
1352 ✗ unlink(protocol_revision_path.c_str());
1353 ✗ shared_manager.CloseDatabase();
1354 ✗ unlink(crash_guard.c_str());
1355 ✗ UnlockFile(fd_lockfile_fifo);
1356
1357 ✗ if (sqlite3_temp_directory) {
1358 ✗ sqlite3_free(sqlite3_temp_directory);
1359 ✗ sqlite3_temp_directory = NULL;
1360 }
1361
1362 ✗ return 0;
1363 }
1364
1365
1366 952 void *PosixQuotaManager::MainCommandServer(void *data) {
1367 952 PosixQuotaManager *quota_mgr = static_cast<PosixQuotaManager *>(data);
1368
1369
1/2
✓ Branch 1 taken 952 times.
✗ Branch 2 not taken.
952 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1370
1/2
✓ Branch 1 taken 952 times.
✗ Branch 2 not taken.
952 sqlite3_soft_heap_limit(quota_mgr->kSqliteMemPerThread);
1371
1372
2/2
✓ Branch 1 taken 30464 times.
✓ Branch 2 taken 952 times.
31416 LruCommand command_buffer[kCommandBufferSize];
1373 char description_buffer[kCommandBufferSize * kMaxDescription];
1374 952 unsigned num_commands = 0;
1375
1376
1/2
✓ Branch 1 taken 4204760 times.
✗ Branch 2 not taken.
4204760 while (read(quota_mgr->pipe_lru_[0], &command_buffer[num_commands],
1377 sizeof(command_buffer[0]))
1378
2/2
✓ Branch 0 taken 4203808 times.
✓ Branch 1 taken 952 times.
4204760 == sizeof(command_buffer[0])) {
1379 4203808 const CommandType command_type = command_buffer[num_commands].command_type;
1380
1/2
✓ Branch 1 taken 4203808 times.
✗ Branch 2 not taken.
4203808 LogCvmfs(kLogQuota, kLogDebug, "received command %d", command_type);
1381 4203808 const uint64_t size = command_buffer[num_commands].GetSize();
1382
1383 // Inserts and pins come with a description (usually a path)
1384
4/4
✓ Branch 0 taken 1403276 times.
✓ Branch 1 taken 2800532 times.
✓ Branch 2 taken 1403164 times.
✓ Branch 3 taken 112 times.
4203808 if ((command_type == kInsert) || (command_type == kInsertVolatile)
1385
4/4
✓ Branch 0 taken 1403108 times.
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 1402800 times.
✓ Branch 3 taken 308 times.
1403164 || (command_type == kPin) || (command_type == kPinRegular)
1386
2/2
✓ Branch 0 taken 1402772 times.
✓ Branch 1 taken 28 times.
1402800 || (command_type == kRegisterMountpoint)
1387
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1402744 times.
1402772 || (command_type == kSetCleanupPolicy)) {
1388 2801064 const int desc_length = command_buffer[num_commands].desc_length;
1389 2801064 ReadPipe(quota_mgr->pipe_lru_[0],
1390
1/2
✓ Branch 1 taken 2801064 times.
✗ Branch 2 not taken.
2801064 &description_buffer[kMaxDescription * num_commands],
1391 desc_length);
1392 }
1393
1394 // The protocol revision is returned immediately
1395
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 4203780 times.
4203808 if (command_type == kGetProtocolRevision) {
1396
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const int return_pipe = quota_mgr->BindReturnPipe(
1397 command_buffer[num_commands].return_pipe);
1398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (return_pipe < 0)
1399 ✗ continue;
1400
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 WritePipe(return_pipe, &quota_mgr->kProtocolRevision,
1401 sizeof(quota_mgr->kProtocolRevision));
1402
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 quota_mgr->UnbindReturnPipe(return_pipe);
1403 28 continue;
1404 28 }
1405
1406 // Register a new mountpoint
1407
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 4203752 times.
4203780 if (command_type == kRegisterMountpoint) {
1408 const std::string mountpoint(
1409 28 &description_buffer[num_commands * kMaxDescription],
1410
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 command_buffer[num_commands].desc_length);
1411
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 quota_mgr->mountpoints_.push_back(mountpoint);
1412
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1413 "Mountpoint %s registered in the group", mountpoint.c_str());
1414 28 continue;
1415 28 }
1416
1417 // Set Cleanup Policy
1418
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 4203724 times.
4203752 if (command_type == kSetCleanupPolicy) {
1419 28 quota_mgr->cleanup_unused_first_ = (description_buffer[num_commands
1420 28 * kMaxDescription]
1421 == 'S')
1422 28 ? true
1423 : false;
1424 28 continue;
1425 }
1426 // Mountpoints are returned immediately
1427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4203724 times.
4203724 if (command_type == kGetMountpoints) {
1428 ✗ const int return_pipe = quota_mgr->BindReturnPipe(
1429 command_buffer[num_commands].return_pipe);
1430 ✗ if (return_pipe < 0)
1431 ✗ continue;
1432
1433 ✗ std::string mps;
1434 ✗ for (auto it = quota_mgr->mountpoints_.begin();
1435 ✗ it != quota_mgr->mountpoints_.end();
1436 ✗ ++it) {
1437 ✗ mps += *it + "\n";
1438 }
1439 ✗ size_t mp_size = mps.size();
1440 ✗ WritePipe(return_pipe, &mp_size, sizeof(size_t));
1441 ✗ WritePipe(return_pipe, mps.c_str(), mp_size);
1442 ✗ quota_mgr->UnbindReturnPipe(return_pipe);
1443 ✗ continue;
1444 }
1445
1446 // Group hashes are returned immediately
1447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4203724 times.
4203724 if (command_type == kGetGroupHashes) {
1448 ✗ const int return_pipe = quota_mgr->BindReturnPipe(
1449 command_buffer[num_commands].return_pipe);
1450 ✗ if (return_pipe < 0)
1451 ✗ continue;
1452
1453 ✗ std::vector<shash::Short> gh = quota_mgr->CollectAllOpenHashes();
1454 ✗ std::string result;
1455 ✗ for (auto it = gh.begin(); it != gh.end(); ++it) {
1456 ✗ result += (*it).ToString() + "\n";
1457 }
1458 ✗ size_t result_size = result.size();
1459 ✗ WritePipe(return_pipe, &result_size, sizeof(size_t));
1460 ✗ WritePipe(return_pipe, result.c_str(), result_size);
1461 ✗ quota_mgr->UnbindReturnPipe(return_pipe);
1462 ✗ continue;
1463 }
1464
1465 // The cleanup rate is returned immediately
1466
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 4203612 times.
4203724 if (command_type == kCleanupRate) {
1467
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 const int return_pipe = quota_mgr->BindReturnPipe(
1468 command_buffer[num_commands].return_pipe);
1469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (return_pipe < 0)
1470 ✗ continue;
1471 const uint64_t
1472 112 period_s = size; // use the size field to transmit the period
1473
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 uint64_t rate = quota_mgr->cleanup_recorder_.GetNoTicks(period_s);
1474
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 WritePipe(return_pipe, &rate, sizeof(rate));
1475
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 quota_mgr->UnbindReturnPipe(return_pipe);
1476 112 continue;
1477 112 }
1478
1479
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 4203584 times.
4203612 if (command_type == kSetLimit) {
1480
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const int return_pipe = quota_mgr->BindReturnPipe(
1481 command_buffer[num_commands].return_pipe);
1482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (return_pipe < 0)
1483 ✗ continue;
1484 28 quota_mgr->limit_ = size; // use the size field to transmit the size
1485 28 quota_mgr->cleanup_threshold_ = size / 2;
1486
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1487 "Quota limit set to %lu / threshold %lu", quota_mgr->limit_,
1488 quota_mgr->cleanup_threshold_);
1489 28 bool ret = true;
1490
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 WritePipe(return_pipe, &ret, sizeof(ret));
1491
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 quota_mgr->UnbindReturnPipe(return_pipe);
1492 28 continue;
1493 28 }
1494
1495 // Reservations are handled immediately and "out of band"
1496
2/2
✓ Branch 0 taken 392 times.
✓ Branch 1 taken 4203192 times.
4203584 if (command_type == kReserve) {
1497 392 bool success = true;
1498
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 const int return_pipe = quota_mgr->BindReturnPipe(
1499 command_buffer[num_commands].return_pipe);
1500
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if (return_pipe < 0)
1501 ✗ continue;
1502
1503
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1504
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 const string hash_str(hash.ToString());
1505
1/2
✓ Branch 2 taken 392 times.
✗ Branch 3 not taken.
392 LogCvmfs(kLogQuota, kLogDebug, "reserve %lu bytes for %s", size,
1506 hash_str.c_str());
1507
1508
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 if (quota_mgr->pinned_chunks_.find(hash)
1509
2/2
✓ Branch 2 taken 336 times.
✓ Branch 3 taken 56 times.
784 == quota_mgr->pinned_chunks_.end()) {
1510
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 308 times.
336 if ((quota_mgr->pinned_ + size) > quota_mgr->cleanup_threshold_) {
1511
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 LogCvmfs(kLogQuota, kLogDebug,
1512 "failed to insert %s (pinned), no space", hash_str.c_str());
1513 28 success = false;
1514 } else {
1515
1/2
✓ Branch 1 taken 308 times.
✗ Branch 2 not taken.
308 quota_mgr->pinned_chunks_[hash] = size;
1516 308 quota_mgr->pinned_ += size;
1517
1/2
✓ Branch 1 taken 308 times.
✗ Branch 2 not taken.
308 quota_mgr->CheckHighPinWatermark();
1518 }
1519 }
1520
1521
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 WritePipe(return_pipe, &success, sizeof(success));
1522
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 quota_mgr->UnbindReturnPipe(return_pipe);
1523 392 continue;
1524 392 }
1525
1526 // Back channels are also handled out of band
1527
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 4203080 times.
4203192 if (command_type == kRegisterBackChannel) {
1528
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 const int return_pipe = quota_mgr->BindReturnPipe(
1529 command_buffer[num_commands].return_pipe);
1530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (return_pipe < 0)
1531 ✗ continue;
1532
1533
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 quota_mgr->UnlinkReturnPipe(command_buffer[num_commands].return_pipe);
1534
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 Block2Nonblock(return_pipe); // back channels are opportunistic
1535
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 shash::Md5 hash;
1536 112 memcpy(hash.digest, command_buffer[num_commands].digest,
1537 112 shash::kDigestSizes[shash::kMd5]);
1538
1539 112 quota_mgr->LockBackChannels();
1540 const map<shash::Md5, int>::const_iterator
1541
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 iter = quota_mgr->back_channels_.find(hash);
1542
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 112 times.
112 if (iter != quota_mgr->back_channels_.end()) {
1543 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1544 ✗ "closing left-over back channel %s", hash.ToString().c_str());
1545 ✗ close(iter->second);
1546 }
1547
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 quota_mgr->back_channels_[hash] = return_pipe;
1548 112 quota_mgr->UnlockBackChannels();
1549
1550 112 char success = 'S';
1551
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 WritePipe(return_pipe, &success, sizeof(success));
1552
1/2
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
112 LogCvmfs(kLogQuota, kLogDebug, "register back channel %s on fd %d",
1553
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
224 hash.ToString().c_str(), return_pipe);
1554
1555 112 continue;
1556 112 }
1557
1558
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 4203024 times.
4203080 if (command_type == kUnregisterBackChannel) {
1559
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 shash::Md5 hash;
1560 56 memcpy(hash.digest, command_buffer[num_commands].digest,
1561 56 shash::kDigestSizes[shash::kMd5]);
1562
1563 56 quota_mgr->LockBackChannels();
1564 const map<shash::Md5, int>::iterator iter = quota_mgr->back_channels_
1565
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 .find(hash);
1566
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 if (iter != quota_mgr->back_channels_.end()) {
1567
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 LogCvmfs(kLogQuota, kLogDebug, "closing back channel %s",
1568
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
112 hash.ToString().c_str());
1569
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 close(iter->second);
1570
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 quota_mgr->back_channels_.erase(iter);
1571 } else {
1572 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1573 ✗ "did not find back channel %s", hash.ToString().c_str());
1574 }
1575 56 quota_mgr->UnlockBackChannels();
1576
1577 56 continue;
1578 56 }
1579
1580 // Unpinnings are also handled immediately with respect to the pinned gauge
1581
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 4202968 times.
4203024 if (command_type == kUnpin) {
1582
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1583
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 const string hash_str(hash.ToString());
1584
1585 const map<shash::Any, uint64_t>::iterator iter = quota_mgr->pinned_chunks_
1586
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 .find(hash);
1587
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 if (iter != quota_mgr->pinned_chunks_.end()) {
1588 56 quota_mgr->pinned_ -= iter->second;
1589
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 quota_mgr->pinned_chunks_.erase(iter);
1590 // It can happen that files get pinned that were removed from the cache
1591 // (see cache.cc). We fix this at this point, where we remove such
1592 // entries from the cache database.
1593
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
112 if (!FileExists(quota_mgr->cache_dir_ + "/"
1594
4/6
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 28 times.
✓ Branch 10 taken 28 times.
168 + hash.MakePathWithoutSuffix())) {
1595
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 LogCvmfs(kLogQuota, kLogDebug,
1596 "remove orphaned pinned hash %s from cache database",
1597 hash_str.c_str());
1598
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1599 28 hash_str.length(), SQLITE_STATIC);
1600 int retval;
1601
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
28 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1602
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1603 28 0);
1604
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1605 28 hash_str.length(), SQLITE_STATIC);
1606
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 retval = sqlite3_step(quota_mgr->stmt_rm_);
1607
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
28 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1608 28 quota_mgr->gauge_ -= size;
1609 } else {
1610 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1611 "failed to delete %s (%d)", hash_str.c_str(), retval);
1612 }
1613
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_reset(quota_mgr->stmt_rm_);
1614 }
1615
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_reset(quota_mgr->stmt_size_);
1616 }
1617 } else {
1618 ✗ LogCvmfs(kLogQuota, kLogDebug, "this chunk was not pinned");
1619 }
1620 56 }
1621
1622 // Immediate commands trigger flushing of the buffer
1623 4203024 const bool immediate_command = (command_type == kCleanup)
1624
2/2
✓ Branch 0 taken 4202128 times.
✓ Branch 1 taken 644 times.
4202772 || (command_type == kList)
1625
2/2
✓ Branch 0 taken 4201904 times.
✓ Branch 1 taken 224 times.
4202128 || (command_type == kListPinned)
1626
2/2
✓ Branch 0 taken 4201820 times.
✓ Branch 1 taken 84 times.
4201904 || (command_type == kListCatalogs)
1627
2/2
✓ Branch 0 taken 4201736 times.
✓ Branch 1 taken 84 times.
4201820 || (command_type == kListVolatile)
1628
2/2
✓ Branch 0 taken 4201652 times.
✓ Branch 1 taken 84 times.
4201736 || (command_type == kRemove)
1629
2/2
✓ Branch 0 taken 4201148 times.
✓ Branch 1 taken 504 times.
4201652 || (command_type == kStatus)
1630
1/2
✓ Branch 0 taken 4201148 times.
✗ Branch 1 not taken.
4201148 || (command_type == kLimits)
1631
3/4
✓ Branch 0 taken 4202772 times.
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4201148 times.
8405796 || (command_type == kPid);
1632
2/2
✓ Branch 0 taken 4201148 times.
✓ Branch 1 taken 1876 times.
4203024 if (!immediate_command)
1633 4201148 num_commands++;
1634
1635
4/4
✓ Branch 0 taken 4071788 times.
✓ Branch 1 taken 131236 times.
✓ Branch 2 taken 1876 times.
✓ Branch 3 taken 4069912 times.
4203024 if ((num_commands == kCommandBufferSize) || immediate_command) {
1636
1/2
✓ Branch 1 taken 133112 times.
✗ Branch 2 not taken.
133112 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1637 description_buffer);
1638
2/2
✓ Branch 0 taken 131236 times.
✓ Branch 1 taken 1876 times.
133112 if (!immediate_command)
1639 131236 num_commands = 0;
1640 }
1641
1642
2/2
✓ Branch 0 taken 1876 times.
✓ Branch 1 taken 4201148 times.
4203024 if (immediate_command) {
1643 // Process cleanup, listings
1644
1/2
✓ Branch 1 taken 1876 times.
✗ Branch 2 not taken.
1876 const int return_pipe = quota_mgr->BindReturnPipe(
1645 command_buffer[num_commands].return_pipe);
1646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1876 times.
1876 if (return_pipe < 0) {
1647 ✗ num_commands = 0;
1648 ✗ continue;
1649 }
1650
1651 int retval;
1652 1876 sqlite3_stmt *this_stmt_list = NULL;
1653
7/10
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 252 times.
✓ Branch 2 taken 644 times.
✓ Branch 3 taken 224 times.
✓ Branch 4 taken 84 times.
✓ Branch 5 taken 84 times.
✓ Branch 6 taken 504 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1876 switch (command_type) {
1654 84 case kRemove: {
1655
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1656
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 const string hash_str = hash.ToString();
1657
1/2
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 LogCvmfs(kLogQuota, kLogDebug, "manually removing %s",
1658 hash_str.c_str());
1659 84 bool success = false;
1660
1661
1/2
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1662 84 hash_str.length(), SQLITE_STATIC);
1663 int retval;
1664
3/4
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
✓ Branch 4 taken 28 times.
84 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1665
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1666 56 0);
1667
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 const uint64_t is_pinned = sqlite3_column_int64(
1668 56 quota_mgr->stmt_size_, 1);
1669
1670
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1671 56 hash_str.length(), SQLITE_STATIC);
1672
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 retval = sqlite3_step(quota_mgr->stmt_rm_);
1673
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
56 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1674 56 success = true;
1675 56 quota_mgr->gauge_ -= size;
1676
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 if (is_pinned) {
1677
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 quota_mgr->pinned_chunks_.erase(hash);
1678 28 quota_mgr->pinned_ -= size;
1679 }
1680 } else {
1681 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1682 "failed to delete %s (%d)", hash_str.c_str(), retval);
1683 }
1684
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 sqlite3_reset(quota_mgr->stmt_rm_);
1685 } else {
1686 // File does not exist
1687 28 success = true;
1688 }
1689
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 sqlite3_reset(quota_mgr->stmt_size_);
1690
1691
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 WritePipe(return_pipe, &success, sizeof(success));
1692 84 break;
1693 84 }
1694 252 case kCleanup:
1695
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 retval = quota_mgr->DoCleanup(size);
1696
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 WritePipe(return_pipe, &retval, sizeof(retval));
1697 252 break;
1698 644 case kList:
1699
1/2
✓ Branch 0 taken 644 times.
✗ Branch 1 not taken.
644 if (!this_stmt_list)
1700 644 this_stmt_list = quota_mgr->stmt_list_;
1701 case kListPinned:
1702
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 644 times.
868 if (!this_stmt_list)
1703 224 this_stmt_list = quota_mgr->stmt_list_pinned_;
1704 case kListCatalogs:
1705
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 868 times.
952 if (!this_stmt_list)
1706 84 this_stmt_list = quota_mgr->stmt_list_catalogs_;
1707 case kListVolatile:
1708
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 952 times.
1036 if (!this_stmt_list)
1709 84 this_stmt_list = quota_mgr->stmt_list_volatile_;
1710
1711 // Pipe back the list, one by one
1712 int length;
1713
3/4
✓ Branch 1 taken 2802184 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2801148 times.
✓ Branch 4 taken 1036 times.
2802184 while (sqlite3_step(this_stmt_list) == SQLITE_ROW) {
1714
1/2
✓ Branch 2 taken 2801148 times.
✗ Branch 3 not taken.
2801148 string path = "(NULL)";
1715
2/4
✓ Branch 1 taken 2801148 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2801148 times.
✗ Branch 4 not taken.
2801148 if (sqlite3_column_type(this_stmt_list, 0) != SQLITE_NULL) {
1716 5602296 path = string(reinterpret_cast<const char *>(
1717
2/4
✓ Branch 1 taken 2801148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2801148 times.
✗ Branch 5 not taken.
2801148 sqlite3_column_text(this_stmt_list, 0)));
1718 }
1719 2801148 length = path.length();
1720
1/2
✓ Branch 1 taken 2801148 times.
✗ Branch 2 not taken.
2801148 WritePipe(return_pipe, &length, sizeof(length));
1721
1/2
✓ Branch 0 taken 2801148 times.
✗ Branch 1 not taken.
2801148 if (length > 0)
1722
2/4
✓ Branch 1 taken 2801148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2801148 times.
✗ Branch 5 not taken.
2801148 WritePipe(return_pipe, &path[0], length);
1723 2801148 }
1724 1036 length = -1;
1725
1/2
✓ Branch 1 taken 1036 times.
✗ Branch 2 not taken.
1036 WritePipe(return_pipe, &length, sizeof(length));
1726
1/2
✓ Branch 1 taken 1036 times.
✗ Branch 2 not taken.
1036 sqlite3_reset(this_stmt_list);
1727 1036 break;
1728 504 case kStatus:
1729
1/2
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
504 WritePipe(return_pipe, &quota_mgr->gauge_, sizeof(quota_mgr->gauge_));
1730
1/2
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
504 WritePipe(return_pipe, &quota_mgr->pinned_,
1731 sizeof(quota_mgr->pinned_));
1732 504 break;
1733 ✗ case kLimits:
1734 ✗ WritePipe(return_pipe, &quota_mgr->limit_, sizeof(quota_mgr->limit_));
1735 ✗ WritePipe(return_pipe, &quota_mgr->cleanup_threshold_,
1736 sizeof(quota_mgr->cleanup_threshold_));
1737 ✗ break;
1738 ✗ case kPid: {
1739 ✗ pid_t pid = getpid();
1740 ✗ WritePipe(return_pipe, &pid, sizeof(pid));
1741 ✗ break;
1742 }
1743 ✗ default:
1744 ✗ PANIC(NULL); // other types are handled by the bunch processor
1745 }
1746
1/2
✓ Branch 1 taken 1876 times.
✗ Branch 2 not taken.
1876 quota_mgr->UnbindReturnPipe(return_pipe);
1747 1876 num_commands = 0;
1748 }
1749 }
1750
1751
1/2
✓ Branch 1 taken 952 times.
✗ Branch 2 not taken.
952 LogCvmfs(kLogQuota, kLogDebug, "stopping cache manager (%d)", errno);
1752
1/2
✓ Branch 1 taken 952 times.
✗ Branch 2 not taken.
952 close(quota_mgr->pipe_lru_[0]);
1753
1/2
✓ Branch 1 taken 952 times.
✗ Branch 2 not taken.
952 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1754 description_buffer);
1755
1756 // Unpin
1757 952 command_buffer[0].command_type = kTouch;
1758 952 for (map<shash::Any, uint64_t>::const_iterator
1759 952 i = quota_mgr->pinned_chunks_.begin(),
1760 952 iEnd = quota_mgr->pinned_chunks_.end();
1761
2/2
✓ Branch 1 taken 280 times.
✓ Branch 2 taken 952 times.
1232 i != iEnd;
1762 280 ++i) {
1763
1/2
✓ Branch 2 taken 280 times.
✗ Branch 3 not taken.
280 command_buffer[0].StoreHash(i->first);
1764
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 quota_mgr->ProcessCommandBunch(1, command_buffer, description_buffer);
1765 }
1766
1767 952 return NULL;
1768 }
1769
1770
1771 2632 void PosixQuotaManager::MakeReturnPipe(int pipe[2]) {
1772
2/2
✓ Branch 0 taken 2548 times.
✓ Branch 1 taken 84 times.
2632 if (!shared_) {
1773 2548 MakePipe(pipe);
1774 2548 return;
1775 }
1776
1777 // Create FIFO in cache directory, store path name (number) in pipe write end
1778 84 int i = 0;
1779 int retval;
1780 do {
1781
2/4
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 112 times.
✗ Branch 6 not taken.
112 retval = mkfifo((workspace_dir_ + "/pipe" + StringifyInt(i)).c_str(), 0600);
1782 112 pipe[1] = i;
1783 112 i++;
1784
3/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
112 } while ((retval == -1) && (errno == EEXIST));
1785
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
84 assert(retval == 0);
1786
1787 // Connect reader's end
1788
3/6
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 84 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 84 times.
✗ Branch 10 not taken.
84 pipe[0] = open((workspace_dir_ + "/pipe" + StringifyInt(pipe[1])).c_str(),
1789 O_RDONLY | O_NONBLOCK);
1790
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
84 assert(pipe[0] >= 0);
1791 84 Nonblock2Block(pipe[0]);
1792 }
1793
1794
1795 3375 void PosixQuotaManager::ParseDirectories(const std::string cache_workspace,
1796 std::string *cache_dir,
1797 std::string *workspace_dir) {
1798
1/2
✓ Branch 1 taken 3375 times.
✗ Branch 2 not taken.
3375 vector<string> dir_tokens(SplitString(cache_workspace, ':'));
1799
2/3
✓ Branch 1 taken 3319 times.
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
3375 switch (dir_tokens.size()) {
1800 3319 case 1:
1801
2/4
✓ Branch 2 taken 3319 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3319 times.
✗ Branch 6 not taken.
3319 *cache_dir = *workspace_dir = dir_tokens[0];
1802 3319 break;
1803 56 case 2:
1804
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 *cache_dir = dir_tokens[0];
1805
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 *workspace_dir = dir_tokens[1];
1806 56 break;
1807 ✗ default:
1808 ✗ PANIC(NULL);
1809 }
1810 3375 }
1811
1812 28 void PosixQuotaManager::SkipEviction(const EvictCandidate &candidate) {
1813 28 bool res = true;
1814
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 std::string hash_str = candidate.hash.ToString();
1815
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 LogCvmfs(kLogQuota, kLogDebug, "Exclude %s from eviction", hash_str.c_str());
1816
2/4
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
28 sqlite3_bind_text(stmt_block_, 1, &hash_str[0], hash_str.length(),
1817 SQLITE_STATIC);
1818
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 res = (sqlite3_step(stmt_block_) == SQLITE_DONE);
1819
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_reset(stmt_block_);
1820
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 assert(res);
1821 28 }
1822
1823 /**
1824 * Immediately inserts a new pinned catalog. Does cache cleanup if necessary.
1825 *
1826 * \return True on success, false otherwise
1827 */
1828 1480 bool PosixQuotaManager::Pin(const shash::Any &hash,
1829 const uint64_t size,
1830 const string &description,
1831 const bool is_catalog) {
1832
3/4
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 1424 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
1480 assert((size > 0) || !is_catalog);
1833
1834
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 const string hash_str = hash.ToString();
1835
1/2
✓ Branch 3 taken 1480 times.
✗ Branch 4 not taken.
1480 LogCvmfs(kLogQuota, kLogDebug, "pin into lru %s, path %s", hash_str.c_str(),
1836 description.c_str());
1837
1838 // Has to run when not yet spawned (cvmfs initialization)
1839
2/2
✓ Branch 0 taken 1088 times.
✓ Branch 1 taken 392 times.
1480 if (!spawned_) {
1840 // Code duplication here
1841
3/4
✓ Branch 2 taken 1088 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 633 times.
✓ Branch 6 taken 455 times.
1088 if (pinned_chunks_.find(hash) == pinned_chunks_.end()) {
1842
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 577 times.
633 if (pinned_ + size > cleanup_threshold_) {
1843
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 LogCvmfs(kLogQuota, kLogDebug, "failed to insert %s (pinned), no space",
1844 hash_str.c_str());
1845 56 return false;
1846 } else {
1847
1/2
✓ Branch 1 taken 577 times.
✗ Branch 2 not taken.
577 pinned_chunks_[hash] = size;
1848 577 pinned_ += size;
1849
1/2
✓ Branch 1 taken 577 times.
✗ Branch 2 not taken.
577 CheckHighPinWatermark();
1850 }
1851 }
1852
1/2
✓ Branch 1 taken 1032 times.
✗ Branch 2 not taken.
1032 const bool exists = Contains(hash_str);
1853
4/4
✓ Branch 0 taken 577 times.
✓ Branch 1 taken 455 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 549 times.
1032 if (!exists && (gauge_ + size > limit_)) {
1854
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %lu",
1855 gauge_, size);
1856
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const int retval = DoCleanup(cleanup_threshold_);
1857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 assert(retval != 0);
1858 }
1859
1/2
✓ Branch 3 taken 1032 times.
✗ Branch 4 not taken.
1032 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1860 SQLITE_STATIC);
1861
1/2
✓ Branch 1 taken 1032 times.
✗ Branch 2 not taken.
1032 sqlite3_bind_int64(stmt_new_, 2, size);
1862
1/2
✓ Branch 1 taken 1032 times.
✗ Branch 2 not taken.
1032 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1863
1/2
✓ Branch 3 taken 1032 times.
✗ Branch 4 not taken.
1032 sqlite3_bind_text(stmt_new_, 4, &description[0], description.length(),
1864 SQLITE_STATIC);
1865
3/4
✓ Branch 0 taken 948 times.
✓ Branch 1 taken 84 times.
✓ Branch 3 taken 1032 times.
✗ Branch 4 not taken.
1032 sqlite3_bind_int64(stmt_new_, 5, is_catalog ? kFileCatalog : kFileRegular);
1866
1/2
✓ Branch 1 taken 1032 times.
✗ Branch 2 not taken.
1032 sqlite3_bind_int64(stmt_new_, 6, 1);
1867
1/2
✓ Branch 1 taken 1032 times.
✗ Branch 2 not taken.
1032 const int retval = sqlite3_step(stmt_new_);
1868
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1032 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1032 assert((retval == SQLITE_DONE) || (retval == SQLITE_OK));
1869
1/2
✓ Branch 1 taken 1032 times.
✗ Branch 2 not taken.
1032 sqlite3_reset(stmt_new_);
1870
2/2
✓ Branch 0 taken 577 times.
✓ Branch 1 taken 455 times.
1032 if (!exists)
1871 577 gauge_ += size;
1872 1032 return true;
1873 }
1874
1875 int pipe_reserve[2];
1876
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 MakeReturnPipe(pipe_reserve);
1877
1878 392 LruCommand cmd;
1879 392 cmd.command_type = kReserve;
1880 392 cmd.SetSize(size);
1881
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 cmd.StoreHash(hash);
1882 392 cmd.return_pipe = pipe_reserve[1];
1883
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1884 bool result;
1885
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 ManagedReadHalfPipe(pipe_reserve[0], &result, sizeof(result));
1886
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 CloseReturnPipe(pipe_reserve);
1887
1888
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 364 times.
392 if (!result)
1889 28 return false;
1890
3/4
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 308 times.
✓ Branch 3 taken 364 times.
✗ Branch 4 not taken.
364 DoInsert(hash, size, description, is_catalog ? kPin : kPinRegular);
1891
1892 364 return true;
1893 1480 }
1894
1895
1896 3319 PosixQuotaManager::PosixQuotaManager(const uint64_t limit,
1897 const uint64_t cleanup_threshold,
1898 3319 const string &cache_workspace)
1899 3319 : shared_(false)
1900 3319 , spawned_(false)
1901 3319 , limit_(limit)
1902 3319 , cleanup_threshold_(cleanup_threshold)
1903 3319 , gauge_(0)
1904 3319 , pinned_(0)
1905 3319 , seq_(0)
1906 3319 , cache_dir_() // initialized in body
1907 3319 , workspace_dir_() // initialized in body
1908 3319 , fd_lock_cachedb_(-1)
1909 3319 , async_delete_(true)
1910 3319 , cachemgr_pid_(0)
1911 3319 , database_(NULL)
1912 3319 , stmt_touch_(NULL)
1913 3319 , stmt_unpin_(NULL)
1914 3319 , stmt_block_(NULL)
1915 3319 , stmt_unblock_(NULL)
1916 3319 , stmt_new_(NULL)
1917 3319 , stmt_lru_(NULL)
1918 3319 , stmt_size_(NULL)
1919 3319 , stmt_rm_(NULL)
1920 3319 , stmt_rm_batch_(NULL)
1921 3319 , stmt_list_(NULL)
1922 3319 , stmt_list_pinned_(NULL)
1923 3319 , stmt_list_catalogs_(NULL)
1924 3319 , stmt_list_volatile_(NULL)
1925 3319 , initialized_(false)
1926 6638 , cleanup_unused_first_(false) {
1927
2/4
✓ Branch 1 taken 3319 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3319 times.
✗ Branch 5 not taken.
3319 ParseDirectories(cache_workspace, &cache_dir_, &workspace_dir_);
1928 3319 pipe_lru_[0] = pipe_lru_[1] = -1;
1929
1/2
✓ Branch 1 taken 3319 times.
✗ Branch 2 not taken.
3319 cleanup_recorder_.AddRecorder(1, 90); // last 1.5 min with second resolution
1930 // last 1.5 h with minute resolution
1931
1/2
✓ Branch 1 taken 3319 times.
✗ Branch 2 not taken.
3319 cleanup_recorder_.AddRecorder(60, 90 * 60);
1932 // last 18 hours with 20 min resolution
1933
1/2
✓ Branch 1 taken 3319 times.
✗ Branch 2 not taken.
3319 cleanup_recorder_.AddRecorder(20 * 60, 60 * 60 * 18);
1934 // last 4 days with hour resolution
1935
1/2
✓ Branch 1 taken 3319 times.
✗ Branch 2 not taken.
3319 cleanup_recorder_.AddRecorder(60 * 60, 60 * 60 * 24 * 4);
1936
1937 3319 lock_open_files_ = reinterpret_cast<pthread_mutex_t *>(
1938 3319 smalloc(sizeof(pthread_mutex_t)));
1939 3319 }
1940
1941
1942 13272 PosixQuotaManager::~PosixQuotaManager() {
1943 6636 free(lock_open_files_);
1944
1945
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 3234 times.
6636 if (!initialized_)
1946 168 return;
1947
1948
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3234 times.
6468 if (shared_) {
1949 // Most of cleanup is done elsewhen by shared cache manager
1950 ✗ close(pipe_lru_[1]);
1951 ✗ return;
1952 }
1953
1954
2/2
✓ Branch 0 taken 952 times.
✓ Branch 1 taken 2282 times.
6468 if (spawned_) {
1955 1904 char fin = 0;
1956 1904 WritePipe(pipe_lru_[1], &fin, 1);
1957 1904 close(pipe_lru_[1]);
1958 1904 pthread_join(thread_lru_, NULL);
1959 } else {
1960 4564 ClosePipe(pipe_lru_);
1961 }
1962
1963 6468 CloseDatabase();
1964
14/14
✓ Branch 1 taken 3234 times.
✓ Branch 2 taken 84 times.
✓ Branch 4 taken 3234 times.
✓ Branch 5 taken 84 times.
✓ Branch 7 taken 3234 times.
✓ Branch 8 taken 84 times.
✓ Branch 10 taken 3234 times.
✓ Branch 11 taken 84 times.
✓ Branch 13 taken 3234 times.
✓ Branch 14 taken 84 times.
✓ Branch 16 taken 3234 times.
✓ Branch 17 taken 84 times.
✓ Branch 19 taken 3234 times.
✓ Branch 20 taken 84 times.
14280 }
1965
1966
1967 134344 void PosixQuotaManager::ProcessCommandBunch(const unsigned num,
1968 const LruCommand *commands,
1969 const char *descriptions) {
1970 134344 int retval = sqlite3_exec(database_, "BEGIN", NULL, NULL, NULL);
1971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134344 times.
134344 assert(retval == SQLITE_OK);
1972
1973
2/2
✓ Branch 0 taken 4201428 times.
✓ Branch 1 taken 134344 times.
4335772 for (unsigned i = 0; i < num; ++i) {
1974
1/2
✓ Branch 1 taken 4201428 times.
✗ Branch 2 not taken.
4201428 const shash::Any hash = commands[i].RetrieveHash();
1975
1/2
✓ Branch 1 taken 4201428 times.
✗ Branch 2 not taken.
4201428 const string hash_str = hash.ToString();
1976 4201428 const unsigned size = commands[i].GetSize();
1977
1/2
✓ Branch 1 taken 4201428 times.
✗ Branch 2 not taken.
4201428 LogCvmfs(kLogQuota, kLogDebug, "processing %s (%d)", hash_str.c_str(),
1978 4201428 commands[i].command_type);
1979
1980 bool exists;
1981
3/4
✓ Branch 0 taken 1400364 times.
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 2801008 times.
✗ Branch 3 not taken.
4201428 switch (commands[i].command_type) {
1982 1400364 case kTouch:
1983
1/2
✓ Branch 1 taken 1400364 times.
✗ Branch 2 not taken.
1400364 sqlite3_bind_int64(stmt_touch_, 1, seq_++);
1984
1/2
✓ Branch 3 taken 1400364 times.
✗ Branch 4 not taken.
1400364 sqlite3_bind_text(stmt_touch_, 2, &hash_str[0], hash_str.length(),
1985 SQLITE_STATIC);
1986
1/2
✓ Branch 1 taken 1400364 times.
✗ Branch 2 not taken.
1400364 retval = sqlite3_step(stmt_touch_);
1987
1/2
✓ Branch 1 taken 1400364 times.
✗ Branch 2 not taken.
1400364 LogCvmfs(kLogQuota, kLogDebug, "touching %s (%ld): %d",
1988 1400364 hash_str.c_str(), seq_ - 1, retval);
1989
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1400364 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1400364 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1990 ✗ PANIC(kLogSyslogErr, "failed to update %s in cachedb, error %d",
1991 hash_str.c_str(), retval);
1992 }
1993
1/2
✓ Branch 1 taken 1400364 times.
✗ Branch 2 not taken.
1400364 sqlite3_reset(stmt_touch_);
1994 1400364 break;
1995 56 case kUnpin:
1996
1/2
✓ Branch 3 taken 56 times.
✗ Branch 4 not taken.
56 sqlite3_bind_text(stmt_unpin_, 1, &hash_str[0], hash_str.length(),
1997 SQLITE_STATIC);
1998
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 retval = sqlite3_step(stmt_unpin_);
1999
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 LogCvmfs(kLogQuota, kLogDebug, "unpinning %s: %d", hash_str.c_str(),
2000 retval);
2001
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
56 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
2002 ✗ PANIC(kLogSyslogErr, "failed to unpin %s in cachedb, error %d",
2003 hash_str.c_str(), retval);
2004 }
2005
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 sqlite3_reset(stmt_unpin_);
2006 56 break;
2007 2801008 case kPin:
2008 case kPinRegular:
2009 case kInsert:
2010 case kInsertVolatile:
2011 // It could already be in, check
2012
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 exists = Contains(hash_str);
2013
2014 // Cleanup, move to trash and unlink
2015
3/4
✓ Branch 0 taken 2800868 times.
✓ Branch 1 taken 140 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2800868 times.
2801008 if (!exists && (gauge_ + size > limit_)) {
2016 ✗ LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %u",
2017 gauge_, size);
2018 ✗ retval = DoCleanup(cleanup_threshold_);
2019 ✗ assert(retval != 0);
2020 }
2021
2022 // Insert or replace
2023
1/2
✓ Branch 3 taken 2801008 times.
✗ Branch 4 not taken.
2801008 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
2024 SQLITE_STATIC);
2025
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 sqlite3_bind_int64(stmt_new_, 2, size);
2026
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 2800896 times.
2801008 if (commands[i].command_type == kInsertVolatile) {
2027
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 sqlite3_bind_int64(stmt_new_, 3, (seq_++) | kVolatileFlag);
2028 } else {
2029
1/2
✓ Branch 1 taken 2800896 times.
✗ Branch 2 not taken.
2800896 sqlite3_bind_int64(stmt_new_, 3, seq_++);
2030 }
2031 2801008 sqlite3_bind_text(stmt_new_, 4, &descriptions[i * kMaxDescription],
2032
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 commands[i].desc_length, SQLITE_STATIC);
2033
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 sqlite3_bind_int64(
2034 stmt_new_, 5,
2035
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 2800952 times.
2801008 (commands[i].command_type == kPin) ? kFileCatalog : kFileRegular);
2036
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 sqlite3_bind_int64(stmt_new_, 6,
2037
2/2
✓ Branch 0 taken 2800952 times.
✓ Branch 1 taken 56 times.
2801008 ((commands[i].command_type == kPin)
2038
2/2
✓ Branch 0 taken 308 times.
✓ Branch 1 taken 2800644 times.
2800952 || (commands[i].command_type == kPinRegular))
2039 ? 1
2040 : 0);
2041
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 retval = sqlite3_step(stmt_new_);
2042
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 LogCvmfs(kLogQuota, kLogDebug, "insert or replace %s, method %d: %d",
2043 2801008 hash_str.c_str(), commands[i].command_type, retval);
2044
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2801008 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
2045 ✗ PANIC(kLogSyslogErr, "failed to insert %s in cachedb, error %d",
2046 hash_str.c_str(), retval);
2047 }
2048
1/2
✓ Branch 1 taken 2801008 times.
✗ Branch 2 not taken.
2801008 sqlite3_reset(stmt_new_);
2049
2050
2/2
✓ Branch 0 taken 2800868 times.
✓ Branch 1 taken 140 times.
2801008 if (!exists)
2051 2800868 gauge_ += size;
2052 2801008 break;
2053 ✗ default:
2054 // other types should have been taken care of by event loop
2055 ✗ PANIC(NULL);
2056 }
2057 4201428 }
2058
2059 134344 retval = sqlite3_exec(database_, "COMMIT", NULL, NULL, NULL);
2060
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134344 times.
134344 if (retval != SQLITE_OK) {
2061 ✗ PANIC(kLogSyslogErr, "failed to commit to cachedb, error %d", retval);
2062 }
2063 134344 }
2064
2065
2066 3263 bool PosixQuotaManager::RebuildDatabase() {
2067 3263 bool result = false;
2068 3263 string sql;
2069 3263 sqlite3_stmt *stmt_select = NULL;
2070 3263 sqlite3_stmt *stmt_insert = NULL;
2071 int sqlerr;
2072 3263 int seq = 0;
2073 char hex[4];
2074 struct stat info;
2075 platform_dirent64 *d;
2076 3263 DIR *dirp = NULL;
2077 3263 string path;
2078
2079
1/2
✓ Branch 1 taken 3263 times.
✗ Branch 2 not taken.
3263 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug, "re-building cache database");
2080
2081 // Empty cache catalog and fscache
2082
1/2
✓ Branch 1 taken 3263 times.
✗ Branch 2 not taken.
3263 sql = "DELETE FROM cache_catalog; DELETE FROM fscache;";
2083
1/2
✓ Branch 2 taken 3263 times.
✗ Branch 3 not taken.
3263 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3263 times.
3263 if (sqlerr != SQLITE_OK) {
2085 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not clear cache database");
2086 ✗ goto build_return;
2087 }
2088
2089 3263 gauge_ = 0;
2090
2091 // Insert files from cache sub-directories 00 - ff
2092 // TODO(jblomer): fs_traversal
2093
1/2
✓ Branch 1 taken 3263 times.
✗ Branch 2 not taken.
3263 sqlite3_prepare_v2(database_,
2094 "INSERT INTO fscache (sha1, size, actime) "
2095 "VALUES (:sha1, :s, :t);",
2096 -1, &stmt_insert, NULL);
2097
2098
2/2
✓ Branch 0 taken 813908 times.
✓ Branch 1 taken 3179 times.
817087 for (int i = 0; i <= 0xff; i++) {
2099 813908 snprintf(hex, sizeof(hex), "%02x", i);
2100
3/6
✓ Branch 2 taken 813908 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 813908 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 813908 times.
✗ Branch 9 not taken.
813908 path = cache_dir_ + "/" + string(hex);
2101
3/4
✓ Branch 2 taken 813908 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 84 times.
✓ Branch 5 taken 813824 times.
813908 if ((dirp = opendir(path.c_str())) == NULL) {
2102
1/2
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
2103 "failed to open directory %s (tmpwatch interfering?)",
2104 path.c_str());
2105 84 goto build_return;
2106 }
2107
3/4
✓ Branch 1 taken 2441528 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1627704 times.
✓ Branch 4 taken 813824 times.
2441528 while ((d = platform_readdir(dirp)) != NULL) {
2108
3/6
✓ Branch 2 taken 1627704 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1627704 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1627704 times.
✗ Branch 9 not taken.
3255408 const string file_path = path + "/" + string(d->d_name);
2109
1/2
✓ Branch 2 taken 1627704 times.
✗ Branch 3 not taken.
1627704 if (stat(file_path.c_str(), &info) == 0) {
2110
2/2
✓ Branch 0 taken 1627648 times.
✓ Branch 1 taken 56 times.
1627704 if (!S_ISREG(info.st_mode))
2111 1627676 continue;
2112
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 if (info.st_size == 0) {
2113
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
2114 "removing empty file %s during automatic cache db rebuild",
2115 file_path.c_str());
2116 28 unlink(file_path.c_str());
2117 28 continue;
2118 }
2119
2120
3/6
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 28 times.
✗ Branch 10 not taken.
56 string hash = string(hex) + string(d->d_name);
2121
1/2
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
28 sqlite3_bind_text(stmt_insert, 1, hash.data(), hash.length(),
2122 SQLITE_STATIC);
2123
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_bind_int64(stmt_insert, 2, info.st_size);
2124
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_bind_int64(stmt_insert, 3, info.st_atime);
2125
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
28 if (sqlite3_step(stmt_insert) != SQLITE_DONE) {
2126 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not insert into temp table");
2127 ✗ goto build_return;
2128 }
2129
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_reset(stmt_insert);
2130
2131 28 gauge_ += info.st_size;
2132
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 } else {
2133 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not stat %s", file_path.c_str());
2134 }
2135
2/3
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 1627676 times.
✗ Branch 3 not taken.
1627704 }
2136
1/2
✓ Branch 1 taken 813824 times.
✗ Branch 2 not taken.
813824 closedir(dirp);
2137 813824 dirp = NULL;
2138 }
2139
1/2
✓ Branch 1 taken 3179 times.
✗ Branch 2 not taken.
3179 sqlite3_finalize(stmt_insert);
2140 3179 stmt_insert = NULL;
2141
2142 // Transfer from temp table in cache catalog
2143
1/2
✓ Branch 1 taken 3179 times.
✗ Branch 2 not taken.
3179 sqlite3_prepare_v2(database_,
2144 "SELECT sha1, size FROM fscache ORDER BY actime;", -1,
2145 &stmt_select, NULL);
2146
1/2
✓ Branch 1 taken 3179 times.
✗ Branch 2 not taken.
3179 sqlite3_prepare_v2(
2147 database_,
2148 "INSERT INTO cache_catalog (sha1, size, acseq, path, type, pinned) "
2149 "VALUES (:sha1, :s, :seq, 'unknown (automatic rebuild)', :t, 0);",
2150 -1, &stmt_insert, NULL);
2151
3/4
✓ Branch 1 taken 3207 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 3179 times.
3207 while (sqlite3_step(stmt_select) == SQLITE_ROW) {
2152 const string hash = string(
2153
2/4
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
28 reinterpret_cast<const char *>(sqlite3_column_text(stmt_select, 0)));
2154
1/2
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
28 sqlite3_bind_text(stmt_insert, 1, &hash[0], hash.length(), SQLITE_STATIC);
2155
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 sqlite3_bind_int64(stmt_insert, 2, sqlite3_column_int64(stmt_select, 1));
2156
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_bind_int64(stmt_insert, 3, seq++);
2157 // Might also be a catalog (information is lost)
2158
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_bind_int64(stmt_insert, 4, kFileRegular);
2159
2160
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const int retval = sqlite3_step(stmt_insert);
2161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (retval != SQLITE_DONE) {
2162 // If the file system hosting the cache is full, we'll likely notice here
2163 ✗ LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
2164 "could not insert into cache catalog (%d - %s)", retval,
2165 sqlite3_errstr(retval));
2166 ✗ goto build_return;
2167 }
2168
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 sqlite3_reset(stmt_insert);
2169
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 }
2170
2171 // Delete temporary table
2172
1/2
✓ Branch 1 taken 3179 times.
✗ Branch 2 not taken.
3179 sql = "DELETE FROM fscache;";
2173
1/2
✓ Branch 2 taken 3179 times.
✗ Branch 3 not taken.
3179 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3179 times.
3179 if (sqlerr != SQLITE_OK) {
2175 ✗ LogCvmfs(kLogQuota, kLogDebug, "could not clear temporary table (%d)",
2176 sqlerr);
2177 ✗ goto build_return;
2178 }
2179
2180 3179 seq_ = seq;
2181 3179 result = true;
2182
1/2
✓ Branch 1 taken 3179 times.
✗ Branch 2 not taken.
3179 LogCvmfs(kLogQuota, kLogDebug,
2183 "rebuilding finished, sequence %" PRIu64 ", gauge %" PRIu64, seq_,
2184 gauge_);
2185
2186 3263 build_return:
2187
1/2
✓ Branch 0 taken 3263 times.
✗ Branch 1 not taken.
3263 if (stmt_insert)
2188
1/2
✓ Branch 1 taken 3263 times.
✗ Branch 2 not taken.
3263 sqlite3_finalize(stmt_insert);
2189
2/2
✓ Branch 0 taken 3179 times.
✓ Branch 1 taken 84 times.
3263 if (stmt_select)
2190
1/2
✓ Branch 1 taken 3179 times.
✗ Branch 2 not taken.
3179 sqlite3_finalize(stmt_select);
2191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3263 times.
3263 if (dirp)
2192 ✗ closedir(dirp);
2193 3263 return result;
2194 3263 }
2195
2196
2197 /**
2198 * Register a channel that allows the cache manager to trigger action to its
2199 * clients. Currently used for releasing pinned catalogs.
2200 */
2201 112 void PosixQuotaManager::RegisterBackChannel(int back_channel[2],
2202 const string &channel_id) {
2203
1/2
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
112 if (protocol_revision_ >= 1) {
2204
1/2
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
112 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2205
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 MakeReturnPipe(back_channel);
2206
2207 112 LruCommand cmd;
2208 112 cmd.command_type = kRegisterBackChannel;
2209 112 cmd.return_pipe = back_channel[1];
2210 // Not StoreHash(). This is an MD5 hash.
2211 112 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2212
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2213
2214 char success;
2215
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 ManagedReadHalfPipe(back_channel[0], &success, sizeof(success));
2216 // At this point, the named FIFO is unlinked, so don't use CloseReturnPipe
2217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (success != 'S') {
2218 ✗ PANIC(kLogDebug | kLogSyslogErr,
2219 "failed to register quota back channel (%c)", success);
2220 }
2221 } else {
2222 // Dummy pipe to return valid file descriptors
2223 ✗ MakePipe(back_channel);
2224 }
2225 112 }
2226
2227
2228 /**
2229 * Removes a chunk from cache, if it exists.
2230 */
2231 84 void PosixQuotaManager::Remove(const shash::Any &hash) {
2232
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 const string hash_str = hash.ToString();
2233
2234 int pipe_remove[2];
2235
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 MakeReturnPipe(pipe_remove);
2236
2237 84 LruCommand cmd;
2238 84 cmd.command_type = kRemove;
2239 84 cmd.return_pipe = pipe_remove[1];
2240
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 cmd.StoreHash(hash);
2241
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2242
2243 bool success;
2244
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 ManagedReadHalfPipe(pipe_remove[0], &success, sizeof(success));
2245
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 CloseReturnPipe(pipe_remove);
2246
2247
3/6
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 84 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 84 times.
✗ Branch 8 not taken.
84 unlink((cache_dir_ + "/" + hash.MakePathWithoutSuffix()).c_str());
2248 84 }
2249
2250
2251 1008 void PosixQuotaManager::Spawn() {
2252
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 952 times.
1008 if (spawned_)
2253 56 return;
2254
2255 952 if (pthread_create(&thread_lru_, NULL, MainCommandServer,
2256 static_cast<void *>(this))
2257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 952 times.
952 != 0) {
2258 ✗ PANIC(kLogDebug, "could not create lru thread");
2259 }
2260
2261 952 spawned_ = true;
2262 }
2263
2264
2265 /**
2266 * Updates the sequence number of the file specified by the hash.
2267 */
2268 1400652 void PosixQuotaManager::Touch(const shash::Any &hash) {
2269 1400652 LruCommand cmd;
2270 1400652 cmd.command_type = kTouch;
2271
1/2
✓ Branch 1 taken 1400652 times.
✗ Branch 2 not taken.
1400652 cmd.StoreHash(hash);
2272
1/2
✓ Branch 1 taken 1400652 times.
✗ Branch 2 not taken.
1400652 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2273 1400652 }
2274
2275
2276 2464 void PosixQuotaManager::UnbindReturnPipe(int pipe_wronly) {
2277
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2436 times.
2464 if (shared_)
2278 28 close(pipe_wronly);
2279 2464 }
2280
2281
2282 196 void PosixQuotaManager::UnlinkReturnPipe(int pipe_wronly) {
2283
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 112 times.
196 if (shared_)
2284
2/4
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 84 times.
✗ Branch 6 not taken.
84 unlink((workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str());
2285 196 }
2286
2287
2288 801 void PosixQuotaManager::Unpin(const shash::Any &hash) {
2289
2/4
✓ Branch 1 taken 801 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 801 times.
✗ Branch 6 not taken.
801 LogCvmfs(kLogQuota, kLogDebug, "Unpin %s", hash.ToString().c_str());
2290
2291 801 LruCommand cmd;
2292 801 cmd.command_type = kUnpin;
2293
1/2
✓ Branch 1 taken 801 times.
✗ Branch 2 not taken.
801 cmd.StoreHash(hash);
2294
1/2
✓ Branch 1 taken 801 times.
✗ Branch 2 not taken.
801 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2295 801 }
2296
2297
2298 56 void PosixQuotaManager::UnregisterBackChannel(int back_channel[2],
2299 const string &channel_id) {
2300
1/2
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
56 if (protocol_revision_ >= 1) {
2301
1/2
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
56 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2302
2303 56 LruCommand cmd;
2304 56 cmd.command_type = kUnregisterBackChannel;
2305 // Not StoreHash(). This is an MD5 hash.
2306 56 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2307
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2308
2309 // Writer's end will be closed by cache manager, FIFO is already unlinked
2310
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 close(back_channel[0]);
2311 } else {
2312 ✗ ClosePipe(back_channel);
2313 }
2314 56 }
2315
2316 2803696 void PosixQuotaManager::ManagedReadHalfPipe(int fd, void *buf, size_t nbyte) {
2317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2803696 times.
2803696 const unsigned timeout_ms = cachemgr_pid_ ? 1000 : 0;
2318 2803696 bool result = false;
2319 do {
2320 2803696 result = ReadHalfPipe(fd, buf, nbyte, timeout_ms);
2321 // try only as long as the cachemgr is still alive
2322
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2803696 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2803696 times.
2803696 } while (!result && getpgid(cachemgr_pid_) >= 0);
2323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2803696 times.
2803696 if (!result) {
2324 ✗ PANIC(kLogStderr,
2325 "Error: quota manager could not read from cachemanager pipe");
2326 }
2327 2803696 }
2328
2329 ✗ void *PosixQuotaManager::CollectMountpointsHashes(void *data) {
2330 #ifndef __APPLE__
2331 ✗ pthread_setname_np(pthread_self(), "hash_collector");
2332 ✗ auto *handler = static_cast<CollectorHandler *>(data);
2333
2334 ✗ const std::string mountpoint = handler->mp[handler->i];
2335 ✗ ssize_t n = getxattr(mountpoint.c_str(), "user.list_open_hashes", nullptr, 0);
2336 ✗ if (n < 0) {
2337 ✗ pthread_exit(nullptr);
2338 }
2339 ✗ std::vector<char> buf((size_t)n);
2340 ✗ n = getxattr(mountpoint.c_str(), "user.list_open_hashes", buf.data(),
2341 buf.size());
2342 ✗ if (n < 0) {
2343 ✗ pthread_exit(nullptr);
2344 }
2345
2346 ✗ std::vector<std::string> hash_strs;
2347 ✗ std::string hash_str;
2348 ✗ for (const char c : buf) {
2349 ✗ if (c == '\n') {
2350 ✗ hash_strs.push_back(hash_str);
2351 ✗ hash_str.clear();
2352 } else {
2353 ✗ hash_str += c;
2354 }
2355 }
2356 ✗ const MutexLockGuard lock_guard(handler->l);
2357 ✗ for (auto hash_str : hash_strs) {
2358 ✗ handler->of.push_back(
2359 ✗ shash::Short(shash::MkFromHexPtr(shash::HexPtr(hash_str))));
2360 }
2361 #endif
2362 ✗ pthread_exit(nullptr);
2363 }
2364
2365 ✗ std::vector<shash::Short> PosixQuotaManager::CollectAllOpenHashes() {
2366 ✗ std::vector<CollectorHandler *> handlers;
2367 ✗ std::vector<pthread_t *> threads;
2368 ✗ open_files_.clear();
2369 #ifndef __APPLE__
2370 ✗ auto &&a_after_b = [](const struct timespec a, const struct timespec b) {
2371 ✗ return (a.tv_sec > b.tv_sec) ? true : false;
2372 };
2373
2374 ✗ for (size_t i = 0; i < mountpoints_.size(); ++i) {
2375 ✗ handlers.push_back(
2376 ✗ new CollectorHandler{open_files_, mountpoints_, lock_open_files_, i});
2377 ✗ threads.push_back(new pthread_t);
2378 }
2379
2380 ✗ const int retval = pthread_mutex_init(lock_open_files_, NULL);
2381 ✗ assert(retval == 0);
2382
2383 ✗ for (size_t i = 0; i < mountpoints_.size(); ++i) {
2384 ✗ pthread_create(threads[i], nullptr, CollectMountpointsHashes, handlers[i]);
2385 }
2386
2387 ✗ std::vector<bool> joined(handlers.size(), false);
2388 struct timespec reference, current;
2389 ✗ clock_gettime(CLOCK_REALTIME, &reference);
2390 ✗ clock_gettime(CLOCK_REALTIME, &current);
2391 ✗ reference.tv_sec += 10; // Give 10sec for hash collection
2392 ✗ size_t i = 0;
2393 ✗ while (
2394 ✗ (not std::all_of(joined.begin(), joined.end(), [](bool b) { return b; }))
2395 ✗ and a_after_b(reference, current)) {
2396 // as long as there are still threads that haven't joined yet
2397 // and for 10 seconds
2398 ✗ if (not joined[i]) {
2399 ✗ const int s = pthread_tryjoin_np(*threads[i], NULL);
2400 ✗ if (s == 0) {
2401 ✗ joined[i] = true;
2402 }
2403 }
2404 ✗ ++i;
2405 ✗ i = i % handlers.size();
2406 ✗ clock_gettime(CLOCK_REALTIME, &current);
2407 }
2408
2409 ✗ for (size_t i = 0; i < handlers.size(); ++i) {
2410 ✗ delete handlers[i];
2411 }
2412
2413 ✗ pthread_mutex_destroy(lock_open_files_);
2414 #endif
2415 ✗ return open_files_;
2416 }
2417