GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.cc
Date: 2026-08-30 02:40:36
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 3572 int PosixQuotaManager::BindReturnPipe(int pipe_wronly) {
67
2/2
✓ Branch 0 taken 3496 times.
✓ Branch 1 taken 76 times.
3572 if (!shared_)
68 3496 return pipe_wronly;
69
70 // Connect writer's end
71
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 const int result = open(
72
2/4
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 76 times.
✗ Branch 6 not taken.
152 (workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str(),
73 O_WRONLY | O_NONBLOCK);
74
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 38 times.
76 if (result >= 0) {
75 38 Nonblock2Block(result);
76 } else {
77 38 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
78 38 "failed to bind return pipe (%d)", errno);
79 }
80 76 return result;
81 }
82
83
84 1004 void PosixQuotaManager::CheckHighPinWatermark() {
85 1004 const uint64_t watermark = kHighPinWatermark * cleanup_threshold_ / 100;
86
3/4
✓ Branch 0 taken 1004 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 114 times.
✓ Branch 3 taken 890 times.
1004 if ((cleanup_threshold_ > 0) && (pinned_ > watermark)) {
87 114 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
88 "high watermark of pinned files (%" PRIu64 "M > %" PRIu64 "M)",
89 114 pinned_ / (1024 * 1024), watermark / (1024 * 1024));
90
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 BroadcastBackchannels("R"); // clients: please release pinned catalogs
91 }
92 1004 }
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 342 bool PosixQuotaManager::Cleanup(const uint64_t leave_size) {
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 342 times.
342 if (!spawned_)
129 return DoCleanup(leave_size);
130
131 bool result;
132 int pipe_cleanup[2];
133
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 MakeReturnPipe(pipe_cleanup);
134
135 342 LruCommand cmd;
136 342 cmd.command_type = kCleanup;
137 342 cmd.size = leave_size;
138 342 cmd.return_pipe = pipe_cleanup[1];
139
140
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
141
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 ManagedReadHalfPipe(pipe_cleanup[0], &result, sizeof(result));
142
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 CloseReturnPipe(pipe_cleanup);
143
144 342 return result;
145 }
146
147
148 3977 void PosixQuotaManager::CloseDatabase() {
149
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_list_catalogs_)
150 3977 sqlite3_finalize(stmt_list_catalogs_);
151
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_list_pinned_)
152 3977 sqlite3_finalize(stmt_list_pinned_);
153
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_list_volatile_)
154 3977 sqlite3_finalize(stmt_list_volatile_);
155
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_list_)
156 3977 sqlite3_finalize(stmt_list_);
157
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_lru_)
158 3977 sqlite3_finalize(stmt_lru_);
159
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_rm_)
160 3977 sqlite3_finalize(stmt_rm_);
161
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_rm_batch_)
162 3977 sqlite3_finalize(stmt_rm_batch_);
163
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_size_)
164 3977 sqlite3_finalize(stmt_size_);
165
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_touch_)
166 3977 sqlite3_finalize(stmt_touch_);
167
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_unpin_)
168 3977 sqlite3_finalize(stmt_unpin_);
169
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_block_)
170 3977 sqlite3_finalize(stmt_block_);
171
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_unblock_)
172 3977 sqlite3_finalize(stmt_unblock_);
173
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (stmt_new_)
174 3977 sqlite3_finalize(stmt_new_);
175
1/2
✓ Branch 0 taken 3977 times.
✗ Branch 1 not taken.
3977 if (database_)
176 3977 sqlite3_close(database_);
177 3977 UnlockFile(fd_lock_cachedb_);
178
179 3977 stmt_list_catalogs_ = NULL;
180 3977 stmt_list_pinned_ = NULL;
181 3977 stmt_list_volatile_ = NULL;
182 3977 stmt_list_ = NULL;
183 3977 stmt_rm_ = NULL;
184 3977 stmt_rm_batch_ = NULL;
185 3977 stmt_size_ = NULL;
186 3977 stmt_touch_ = NULL;
187 3977 stmt_unpin_ = NULL;
188 3977 stmt_block_ = NULL;
189 3977 stmt_unblock_ = NULL;
190 3977 stmt_new_ = NULL;
191 3977 database_ = NULL;
192
193 3977 pinned_chunks_.clear();
194 3977 }
195
196
197 3382 void PosixQuotaManager::CloseReturnPipe(int pipe[2]) {
198
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 3306 times.
3382 if (shared_) {
199 76 close(pipe[0]);
200 76 UnlinkReturnPipe(pipe[1]);
201 } else {
202 3306 ClosePipe(pipe);
203 }
204 3382 }
205
206
207 3802526 bool PosixQuotaManager::Contains(const string &hash_str) {
208 3802526 bool result = false;
209
210 3802526 sqlite3_bind_text(stmt_size_, 1, &hash_str[0], hash_str.length(),
211 SQLITE_STATIC);
212
2/2
✓ Branch 1 taken 724 times.
✓ Branch 2 taken 3801802 times.
3802526 if (sqlite3_step(stmt_size_) == SQLITE_ROW)
213 724 result = true;
214 3802526 sqlite3_reset(stmt_size_);
215 3802526 LogCvmfs(kLogQuota, kLogDebug, "contains %s returns %d", hash_str.c_str(),
216 result);
217
218 3802526 return result;
219 }
220
221
222 3902 void PosixQuotaManager::CheckFreeSpace() {
223
3/4
✓ Branch 0 taken 3902 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✓ Branch 3 taken 3864 times.
3902 if ((limit_ == 0) || (gauge_ >= limit_))
224 38 return;
225
226 struct statvfs vfs_info;
227
1/2
✓ Branch 1 taken 3864 times.
✗ Branch 2 not taken.
3864 const int retval = statvfs((cache_dir_ + "/cachedb").c_str(), &vfs_info);
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3864 times.
3864 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 3864 const int64_t free_space_byte = vfs_info.f_bavail * vfs_info.f_bsize;
235
1/2
✓ Branch 1 taken 3864 times.
✗ Branch 2 not taken.
3864 LogCvmfs(kLogQuota, kLogDebug, "free space: %" PRId64 " MB",
236 free_space_byte / (1024 * 1024));
237
238 3864 const int64_t required_byte = limit_ - gauge_;
239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3864 times.
3864 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 4016 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 76 times.
✓ Branch 1 taken 3940 times.
4016 if (cleanup_threshold >= limit) {
253 76 LogCvmfs(kLogQuota, kLogDebug,
254 "invalid parameters: limit %" PRIu64 ", "
255 "cleanup_threshold %" PRIu64,
256 limit, cleanup_threshold);
257 76 return NULL;
258 }
259
260 PosixQuotaManager *quota_manager = new PosixQuotaManager(
261
1/2
✓ Branch 2 taken 3940 times.
✗ Branch 3 not taken.
3940 limit, cleanup_threshold, cache_workspace);
262
263 // Initialize cache catalog
264
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 3902 times.
3940 if (!quota_manager->InitDatabase(rebuild_database)) {
265
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 delete quota_manager;
266 38 return NULL;
267 }
268 3902 quota_manager->CheckFreeSpace();
269 3902 MakePipe(quota_manager->pipe_lru_);
270
271 3902 quota_manager->protocol_revision_ = kProtocolRevision;
272 3902 quota_manager->initialized_ = true;
273 3902 return quota_manager;
274 }
275
276
277 /**
278 * Connects to a running shared local quota manager. Creates one if necessary.
279 */
280 76 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 76 string cache_dir;
287 76 string workspace_dir;
288
2/4
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
76 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 76 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
76 const int fd_lockfile = LockFile(workspace_dir + "/lock_cachemgr");
294
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 38 times.
76 if (fd_lockfile < 0) {
295
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
296
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
76 (workspace_dir + "/lock_cachemgr").c_str(), errno);
297 38 return NULL;
298 }
299
300 PosixQuotaManager *quota_mgr = new PosixQuotaManager(limit, cleanup_threshold,
301
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 cache_workspace);
302 38 quota_mgr->shared_ = true;
303 38 quota_mgr->spawned_ = true;
304
305 // Try to connect to pipe
306
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const string fifo_path = workspace_dir + "/cachemgr";
307
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug, "trying to connect to existing pipe");
308
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 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 38 const int connect_error = errno;
356
357 // Lock file: let existing cache manager finish first
358
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 const int fd_lockfile_fifo = LockFile(workspace_dir + "/lock_cachemgr.fifo");
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 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 38 times.
✗ Branch 2 not taken.
38 UnlockFile(fd_lockfile_fifo);
367
368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 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 38 int retval = mkfifo(fifo_path.c_str(), 0600);
375
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 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 38 times.
✗ Branch 2 not taken.
38 MakePipe(pipe_boot);
387
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 MakePipe(pipe_handshake);
388
389 38 vector<string> command_line;
390
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 command_line.push_back(exe_path);
391
2/4
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
38 command_line.push_back("__cachemgr__");
392
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 command_line.push_back(cache_workspace);
393
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 command_line.push_back(StringifyInt(pipe_boot[1]));
394
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 command_line.push_back(StringifyInt(pipe_handshake[0]));
395
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 command_line.push_back(StringifyInt(limit));
396
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 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 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 command_line.push_back(StringifyInt(true)); // foreground
400
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
38 command_line.push_back(StringifyInt(GetLogSyslogLevel()));
401
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
38 command_line.push_back(StringifyInt(GetLogSyslogFacility()));
402
5/14
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 38 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 38 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
38 command_line.push_back(GetLogDebugFile() + ":" + GetLogMicroSyslog());
403
404 38 set<int> preserve_filedes;
405
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 preserve_filedes.insert(0);
406
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 preserve_filedes.insert(1);
407
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 preserve_filedes.insert(2);
408
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 preserve_filedes.insert(pipe_boot[1]);
409
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 preserve_filedes.insert(pipe_handshake[0]);
410
411
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if (foreground) {
412
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 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 38 times.
38 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 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug,
428 "new cache manager pid: %d protocol revision %d", new_cachemgr_pid,
429 QuotaManager::kProtocolRevision);
430 38 quota_mgr->SetCacheMgrPid(new_cachemgr_pid);
431
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
38 const int fd_lockfile_rw = open((workspace_dir + "/lock_cachemgr").c_str(),
432 O_RDWR | O_TRUNC, 0600);
433 38 const unsigned magic_number = PosixQuotaManager::kLockFileMagicNumber;
434
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const bool result_mn = SafeWrite(fd_lockfile_rw, &magic_number,
435 sizeof(magic_number));
436
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const bool result = SafeWrite(fd_lockfile_rw, &new_cachemgr_pid,
437 sizeof(new_cachemgr_pid));
438
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
38 if (!result || !result_mn) {
439 PANIC(kLogSyslogErr, "could not write cache manager pid to lockfile");
440 }
441
442
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 close(fd_lockfile_rw);
443 // Wait for cache manager to be ready
444
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 close(pipe_boot[1]);
445
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 close(pipe_handshake[0]);
446 char buf;
447
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
38 if (read(pipe_boot[0], &buf, 1) != 1) {
448
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 UnlockFile(fd_lockfile);
449
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 close(pipe_boot[0]);
450
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 close(pipe_handshake[1]);
451
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 delete quota_mgr;
452
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
453 "cache manager did not start");
454 38 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 76 }
494
495
496 380 bool PosixQuotaManager::DoCleanup(const uint64_t leave_size) {
497
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 304 times.
380 if (gauge_ <= leave_size)
498 76 return true;
499
500 // TODO(jblomer) transaction
501
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
502 "clean up cache until at most %lu KB is used", leave_size / 1024);
503
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 LogCvmfs(kLogQuota, kLogDebug, "gauge %" PRIu64, gauge_);
504
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 cleanup_recorder_.Tick();
505
506 bool result;
507 304 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 304 int64_t max_acseq = -1;
513 304 std::vector<EvictCandidate> lru_ordered_open;
514
515 do {
516
1/2
✓ Branch 1 taken 2204 times.
✗ Branch 2 not taken.
2204 sqlite3_reset(stmt_lru_);
517
3/4
✓ Branch 0 taken 304 times.
✓ Branch 1 taken 1900 times.
✓ Branch 3 taken 2204 times.
✗ Branch 4 not taken.
2508 sqlite3_bind_int64(stmt_lru_, 1,
518 304 (max_acseq == -1) ? std::numeric_limits<int64_t>::min()
519 : (max_acseq + 1));
520
521 2204 std::vector<EvictCandidate> candidates;
522
1/2
✓ Branch 1 taken 2204 times.
✗ Branch 2 not taken.
2204 candidates.reserve(kEvictBatchSize);
523 2204 string hash_str;
524 2204 unsigned i = 0;
525
3/4
✓ Branch 1 taken 1978584 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1976380 times.
✓ Branch 4 taken 2204 times.
1978584 while (sqlite3_step(stmt_lru_) == SQLITE_ROW) {
526 hash_str = reinterpret_cast<const char *>(
527
2/4
✓ Branch 1 taken 1976380 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1976380 times.
✗ Branch 5 not taken.
1976380 sqlite3_column_text(stmt_lru_, 0));
528
1/2
✓ Branch 2 taken 1976380 times.
✗ Branch 3 not taken.
1976380 LogCvmfs(kLogQuota, kLogDebug, "add %s to candidates for eviction",
529 hash_str.c_str());
530
1/2
✓ Branch 1 taken 1976380 times.
✗ Branch 2 not taken.
1976380 candidates.push_back(
531
1/2
✓ Branch 1 taken 1976380 times.
✗ Branch 2 not taken.
1976380 EvictCandidate(shash::MkFromHexPtr(shash::HexPtr(hash_str)),
532 1976380 sqlite3_column_int64(stmt_lru_, 1),
533
2/4
✓ Branch 1 taken 1976380 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1976380 times.
✗ Branch 5 not taken.
1976380 sqlite3_column_int64(stmt_lru_, 2)));
534 1976380 i++;
535 }
536
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 2166 times.
2204 if (candidates.empty()) {
537
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug, "no more entries to evict");
538 38 break;
539 }
540
541 2166 const unsigned N = candidates.size();
542
543 2166 open_files_.clear();
544
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2166 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
4332 open_files_ = (cleanup_unused_first_) ? CollectAllOpenHashes()
545 2166 : std::vector<shash::Short>();
546
547
2/2
✓ Branch 0 taken 1900342 times.
✓ Branch 1 taken 1900 times.
1902242 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 1900342 times.
✗ Branch 3 not taken.
1900342 const bool is_pinned = pinned_chunks_.find(candidates[i].hash)
552 3800684 != 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 1900342 bool is_open = false;
565
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1900342 times.
1900342 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 38 times.
✓ Branch 1 taken 1900304 times.
1900342 if (is_pinned) {
573
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 SkipEviction(candidates[i]);
574 38 continue;
575 }
576
577
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1900304 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1900304 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 1900304 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1900304 times.
✗ Branch 5 not taken.
3800608 trash.push_back(cache_dir_ + "/"
584
2/4
✓ Branch 2 taken 1900304 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1900304 times.
✗ Branch 6 not taken.
5700912 + candidates[i].hash.MakePathWithoutSuffix());
585 1900304 gauge_ -= candidates[i].size;
586 1900304 max_acseq = candidates[i].acseq;
587
1/2
✓ Branch 2 taken 1900304 times.
✗ Branch 3 not taken.
1900304 LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
588
1/2
✓ Branch 2 taken 1900304 times.
✗ Branch 3 not taken.
3800608 candidates[i].hash.ToString().c_str(), gauge_);
589
590
2/2
✓ Branch 0 taken 266 times.
✓ Branch 1 taken 1900038 times.
1900304 if (gauge_ <= leave_size)
591 266 break;
592 }
593
6/6
✓ Branch 1 taken 2166 times.
✓ Branch 2 taken 38 times.
✓ Branch 4 taken 2166 times.
✓ Branch 5 taken 38 times.
✓ Branch 6 taken 1900 times.
✓ Branch 7 taken 266 times.
4408 } while (gauge_ > leave_size);
594
595
1/2
✓ Branch 0 taken 304 times.
✗ Branch 1 not taken.
304 if (max_acseq != -1) {
596
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 sqlite3_bind_int64(stmt_rm_batch_, 1, max_acseq);
597
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 result = (sqlite3_step(stmt_rm_batch_) == SQLITE_DONE);
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 304 times.
304 assert(result);
599
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 sqlite3_reset(stmt_rm_batch_);
600
601
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 result = (sqlite3_step(stmt_unblock_) == SQLITE_DONE);
602
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 sqlite3_reset(stmt_unblock_);
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 304 times.
304 assert(result);
604 }
605
606
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 304 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 304 times.
304 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 304 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 304 times.
304 if (!EmptyTrash(trash))
618 return false;
619
620
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 266 times.
304 if (gauge_ > leave_size) {
621
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
622 "request to clean until %" PRIu64 ", "
623 "but effective gauge is %" PRIu64,
624 leave_size, gauge_);
625 38 return false;
626 }
627 266 return true;
628 304 }
629
630 304 bool PosixQuotaManager::EmptyTrash(const std::vector<std::string> &trash) {
631
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 304 times.
304 if (trash.empty())
632 return true;
633
634
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 76 times.
304 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 228 times.
228 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 228 times.
✗ Branch 1 not taken.
228 if (pid > 0)
656
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 waitpid(pid, &statloc, 0);
657 else
658 return false;
659 }
660 } else { // !async_delete_
661
2/2
✓ Branch 1 taken 114 times.
✓ Branch 2 taken 76 times.
190 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
662 114 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
663 114 unlink(trash[i].c_str());
664 }
665 }
666 304 return true;
667 }
668
669
670 3801683 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 3801683 times.
✗ Branch 2 not taken.
3801683 const string hash_str = hash.ToString();
675
1/2
✓ Branch 3 taken 3801683 times.
✗ Branch 4 not taken.
3801683 LogCvmfs(kLogQuota, kLogDebug, "insert into lru %s, path %s, method %d",
676 hash_str.c_str(), description.c_str(), command_type);
677 3801683 const unsigned desc_length = (description.length() > kMaxDescription)
678 ? kMaxDescription
679
1/2
✓ Branch 0 taken 3801683 times.
✗ Branch 1 not taken.
3801683 : description.length();
680
681 LruCommand *cmd = reinterpret_cast<LruCommand *>(
682 3801683 alloca(sizeof(LruCommand) + desc_length));
683 3801683 new (cmd) LruCommand;
684 3801683 cmd->command_type = command_type;
685 3801683 cmd->SetSize(size);
686
1/2
✓ Branch 1 taken 3801683 times.
✗ Branch 2 not taken.
3801683 cmd->StoreHash(hash);
687 3801683 cmd->desc_length = desc_length;
688 3801683 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &description[0],
689 desc_length);
690
1/2
✓ Branch 1 taken 3801683 times.
✗ Branch 2 not taken.
3801683 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
691 3801683 }
692
693
694 1406 vector<string> PosixQuotaManager::DoList(const CommandType list_command) {
695 1406 vector<string> result;
696
697 int pipe_list[2];
698
1/2
✓ Branch 1 taken 1406 times.
✗ Branch 2 not taken.
1406 MakeReturnPipe(pipe_list);
699 char description_buffer[kMaxDescription];
700
701 1406 LruCommand cmd;
702 1406 cmd.command_type = list_command;
703 1406 cmd.return_pipe = pipe_list[1];
704
1/2
✓ Branch 1 taken 1406 times.
✗ Branch 2 not taken.
1406 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
705
706 int length;
707 do {
708
1/2
✓ Branch 1 taken 3802964 times.
✗ Branch 2 not taken.
3802964 ManagedReadHalfPipe(pipe_list[0], &length, sizeof(length));
709
2/2
✓ Branch 0 taken 3801558 times.
✓ Branch 1 taken 1406 times.
3802964 if (length > 0) {
710
1/2
✓ Branch 1 taken 3801558 times.
✗ Branch 2 not taken.
3801558 ReadPipe(pipe_list[0], description_buffer, length);
711
2/4
✓ Branch 2 taken 3801558 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3801558 times.
✗ Branch 6 not taken.
3801558 result.push_back(string(description_buffer, length));
712 }
713
2/2
✓ Branch 0 taken 3801558 times.
✓ Branch 1 taken 1406 times.
3802964 } while (length >= 0);
714
715
1/2
✓ Branch 1 taken 1406 times.
✗ Branch 2 not taken.
1406 CloseReturnPipe(pipe_list);
716 2812 return result;
717 }
718
719
720 1698 uint64_t PosixQuotaManager::GetCapacity() {
721
1/2
✓ Branch 0 taken 1698 times.
✗ Branch 1 not taken.
1698 if (limit_ != (uint64_t)(-1))
722 1698 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 270 uint64_t PosixQuotaManager::GetMaxFileSize() {
756 270 return limit_ - cleanup_threshold_;
757 }
758
759
760 38 pid_t PosixQuotaManager::GetPid() {
761
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
38 if (!shared_ || !spawned_) {
762 38 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 38 uint32_t PosixQuotaManager::GetProtocolRevision() {
783 int pipe_revision[2];
784
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 MakeReturnPipe(pipe_revision);
785
786 38 LruCommand cmd;
787 38 cmd.command_type = kGetProtocolRevision;
788 38 cmd.return_pipe = pipe_revision[1];
789
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
790
791 uint32_t revision;
792
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 ManagedReadHalfPipe(pipe_revision[0], &revision, sizeof(revision));
793
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 CloseReturnPipe(pipe_revision);
794 38 return revision;
795 }
796
797 1698 void PosixQuotaManager::SetCleanupPolicy(bool cleanup_unused_first) {
798
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1698 times.
1698 if (protocol_revision_ < 3)
799 return;
800
801
3/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 1660 times.
✓ Branch 3 taken 1698 times.
✗ Branch 4 not taken.
1698 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 38 times.
✓ Branch 1 taken 1660 times.
1698 char policy = (cleanup_unused_first) ? 'S' : 'R'; // S: smart, R: regular;
806
807 LruCommand *cmd = reinterpret_cast<LruCommand *>(
808 1698 alloca(sizeof(LruCommand) + sizeof(policy)));
809 1698 new (cmd) LruCommand;
810 1698 cmd->command_type = kSetCleanupPolicy;
811 1698 cmd->desc_length = sizeof(policy);
812 1698 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &policy,
813 sizeof(policy));
814
1/2
✓ Branch 1 taken 1698 times.
✗ Branch 2 not taken.
1698 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + sizeof(policy));
815 }
816
817 38 void PosixQuotaManager::RegisterMountpoint(const std::string &mountpoint) {
818
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (protocol_revision_ < 3)
819 return;
820
821 38 LogCvmfs(kLogQuota, kLogDebug, "Register Mountpoint %s", mountpoint.c_str());
822
823 38 const unsigned desc_length = (mountpoint.size() > kMaxDescription)
824 ? kMaxDescription
825
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 : mountpoint.size();
826 LruCommand *cmd = reinterpret_cast<LruCommand *>(
827 38 alloca(sizeof(LruCommand) + desc_length));
828 38 new (cmd) LruCommand;
829 38 cmd->command_type = kRegisterMountpoint;
830 38 cmd->desc_length = desc_length;
831 38 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), mountpoint.data(),
832 desc_length);
833 38 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
834 }
835
836 38 std::string PosixQuotaManager::ReadPipeString(int fd, size_t size) {
837
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (size == 0)
838 return "";
839
840
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 std::vector<char> buf(size);
841
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 ManagedReadHalfPipe(fd, buf.data(), size);
842
1/2
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
38 return std::string(buf.data(), size);
843 38 }
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 684 void PosixQuotaManager::GetSharedStatus(uint64_t *gauge, uint64_t *pinned) {
885 int pipe_status[2];
886
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 MakeReturnPipe(pipe_status);
887
888 684 LruCommand cmd;
889 684 cmd.command_type = kStatus;
890 684 cmd.return_pipe = pipe_status[1];
891
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
892
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 ManagedReadHalfPipe(pipe_status[0], gauge, sizeof(*gauge));
893
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 ReadPipe(pipe_status[0], pinned, sizeof(*pinned));
894
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 CloseReturnPipe(pipe_status);
895 684 }
896
897 38 bool PosixQuotaManager::SetSharedLimit(uint64_t limit) {
898 int pipe_set_limit[2];
899 bool result;
900
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 MakeReturnPipe(pipe_set_limit);
901
902 38 LruCommand cmd;
903 38 cmd.command_type = kSetLimit;
904 38 cmd.size = limit;
905 38 cmd.return_pipe = pipe_set_limit[1];
906
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
907
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 ReadHalfPipe(pipe_set_limit[0], &result, sizeof(result));
908
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 CloseReturnPipe(pipe_set_limit);
909 38 return result;
910 }
911
912
913 38 bool PosixQuotaManager::SetLimit(uint64_t size) {
914
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 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 38 return SetSharedLimit(size);
923 }
924
925 3928 uint64_t PosixQuotaManager::GetSize() {
926
2/2
✓ Branch 0 taken 3320 times.
✓ Branch 1 taken 608 times.
3928 if (!spawned_)
927 3320 return gauge_;
928 uint64_t gauge, size_pinned;
929
1/2
✓ Branch 1 taken 608 times.
✗ Branch 2 not taken.
608 GetSharedStatus(&gauge, &size_pinned);
930 608 return gauge;
931 }
932
933
934 76 uint64_t PosixQuotaManager::GetSizePinned() {
935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (!spawned_)
936 return pinned_;
937 uint64_t gauge, size_pinned;
938
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 GetSharedStatus(&gauge, &size_pinned);
939 76 return size_pinned;
940 }
941
942
943 152 uint64_t PosixQuotaManager::GetCleanupRate(uint64_t period_s) {
944
2/4
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 152 times.
152 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 152 times.
✗ Branch 2 not taken.
152 MakeReturnPipe(pipe_cleanup_rate);
950 152 LruCommand cmd;
951 152 cmd.command_type = kCleanupRate;
952 152 cmd.size = period_s;
953 152 cmd.return_pipe = pipe_cleanup_rate[1];
954
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
955
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 ManagedReadHalfPipe(pipe_cleanup_rate[0], &cleanup_rate,
956 sizeof(cleanup_rate));
957
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 CloseReturnPipe(pipe_cleanup_rate);
958
959 152 return cleanup_rate;
960 }
961
962
963 4130 bool PosixQuotaManager::InitDatabase(const bool rebuild_database) {
964 4130 string sql;
965 sqlite3_stmt *stmt;
966
967
2/4
✓ Branch 1 taken 4130 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4130 times.
✗ Branch 5 not taken.
4130 fd_lock_cachedb_ = LockFile(workspace_dir_ + "/lock_cachedb");
968
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 4092 times.
4130 if (fd_lock_cachedb_ < 0) {
969
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug, "failed to create cachedb lock");
970 38 return false;
971 }
972
973 4092 bool retry = false;
974
1/2
✓ Branch 1 taken 4092 times.
✗ Branch 2 not taken.
4092 const string db_file = cache_dir_ + "/cachedb";
975
2/2
✓ Branch 0 taken 3895 times.
✓ Branch 1 taken 197 times.
4092 if (rebuild_database) {
976
1/2
✓ Branch 2 taken 197 times.
✗ Branch 3 not taken.
197 LogCvmfs(kLogQuota, kLogDebug, "rebuild database, unlinking existing (%s)",
977 db_file.c_str());
978 197 unlink(db_file.c_str());
979
1/2
✓ Branch 1 taken 197 times.
✗ Branch 2 not taken.
197 unlink((db_file + "-journal").c_str());
980 }
981
982 3895 init_recover:
983
1/2
✓ Branch 2 taken 4092 times.
✗ Branch 3 not taken.
4092 int err = sqlite3_open(db_file.c_str(), &database_);
984
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4092 times.
4092 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 4092 times.
✗ Branch 2 not taken.
4092 " CONSTRAINT pk_properties PRIMARY KEY(key));";
1001
1/2
✓ Branch 2 taken 4092 times.
✗ Branch 3 not taken.
4092 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4092 times.
4092 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 4092 times.
✗ Branch 2 not taken.
4092 "ALTER TABLE cache_catalog ADD pinned INTEGER";
1021
1/2
✓ Branch 2 taken 4092 times.
✗ Branch 3 not taken.
4092 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4092 times.
4092 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 4092 times.
✗ Branch 2 not taken.
4092 sql = "UPDATE cache_catalog SET pinned=0;";
1034
1/2
✓ Branch 2 taken 4092 times.
✗ Branch 3 not taken.
4092 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1035
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4092 times.
4092 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 4092 times.
✗ Branch 2 not taken.
4092 "VALUES ('schema', '1.0')";
1044
1/2
✓ Branch 2 taken 4092 times.
✗ Branch 3 not taken.
4092 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1045
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4092 times.
4092 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 4092 times.
✗ Branch 2 not taken.
4092 sql = "SELECT count(*) FROM cache_catalog;";
1053
1/2
✓ Branch 2 taken 4092 times.
✗ Branch 3 not taken.
4092 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1054
2/4
✓ Branch 1 taken 4092 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4092 times.
✗ Branch 4 not taken.
4092 if (sqlite3_step(stmt) == SQLITE_ROW) {
1055
6/8
✓ Branch 1 taken 4092 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 152 times.
✓ Branch 4 taken 3940 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 152 times.
✓ Branch 7 taken 3940 times.
✓ Branch 8 taken 152 times.
4092 if ((sqlite3_column_int64(stmt, 0)) == 0 || rebuild_database) {
1056
1/2
✓ Branch 1 taken 3940 times.
✗ Branch 2 not taken.
3940 LogCvmfs(kLogCvmfs, kLogDebug,
1057 "CernVM-FS: building lru cache database...");
1058
3/4
✓ Branch 1 taken 3940 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 114 times.
✓ Branch 4 taken 3826 times.
3940 if (!RebuildDatabase()) {
1059
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 LogCvmfs(kLogQuota, kLogDebug,
1060 "could not build cache database from file system");
1061
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 sqlite3_finalize(stmt);
1062 114 goto init_database_fail;
1063 }
1064 }
1065
1/2
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 sql = "SELECT sum(size) FROM cache_catalog;";
1074
1/2
✓ Branch 2 taken 3978 times.
✗ Branch 3 not taken.
3978 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1075
2/4
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3978 times.
✗ Branch 4 not taken.
3978 if (sqlite3_step(stmt) == SQLITE_ROW) {
1076
1/2
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 sqlite3_finalize(stmt);
1083
1084 // Highest seq-no?
1085
1/2
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
3978 sql = "SELECT coalesce(max(acseq & (~(1<<63))), 0) FROM cache_catalog;";
1086
1/2
✓ Branch 2 taken 3978 times.
✗ Branch 3 not taken.
3978 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1087
2/4
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3978 times.
✗ Branch 4 not taken.
3978 if (sqlite3_step(stmt) == SQLITE_ROW) {
1088
1/2
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 sqlite3_finalize(stmt);
1095
1096 // Prepare touch, new, remove statements
1097
1/2
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 sqlite3_prepare_v2(database_, "DELETE FROM cache_catalog WHERE sha1=:sha1;",
1122 -1, &stmt_rm_, NULL);
1123
1/2
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 3 not taken.
3978 sqlite3_prepare_v2(database_,
1127
1/2
✓ Branch 2 taken 3978 times.
✗ Branch 3 not taken.
7956 (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 3978 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3978 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3978 times.
✗ Branch 8 not taken.
15912 + StringifyInt(kEvictBatchSize) + ";")
1132 .c_str(),
1133 -1, &stmt_lru_, NULL);
1134
1/2
✓ Branch 2 taken 3978 times.
✗ Branch 3 not taken.
3978 sqlite3_prepare_v2(database_,
1135 ("SELECT path FROM cache_catalog WHERE type="
1136
3/6
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3978 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3978 times.
✗ Branch 8 not taken.
7956 + StringifyInt(kFileRegular) + ";")
1137 .c_str(),
1138 -1, &stmt_list_, NULL);
1139
1/2
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 2 not taken.
3978 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 3978 times.
✗ Branch 3 not taken.
3978 sqlite3_prepare_v2(database_,
1146 ("SELECT path FROM cache_catalog WHERE type="
1147
3/6
✓ Branch 1 taken 3978 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3978 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3978 times.
✗ Branch 8 not taken.
7956 + StringifyInt(kFileCatalog) + ";")
1148 .c_str(),
1149 -1, &stmt_list_catalogs_, NULL);
1150 3978 return true;
1151
1152 114 init_database_fail:
1153
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 sqlite3_close(database_);
1154 114 database_ = NULL;
1155
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 UnlockFile(fd_lock_cachedb_);
1156 114 return false;
1157 4130 }
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 3801037 void PosixQuotaManager::Insert(const shash::Any &any_hash,
1165 const uint64_t size,
1166 const string &description) {
1167 3801037 DoInsert(any_hash, size, description, kInsert);
1168 3801037 }
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 152 void PosixQuotaManager::InsertVolatile(const shash::Any &any_hash,
1177 const uint64_t size,
1178 const string &description) {
1179 152 DoInsert(any_hash, size, description, kInsertVolatile);
1180 152 }
1181
1182
1183 /**
1184 * Lists all path names from the cache db.
1185 */
1186 874 vector<string> PosixQuotaManager::List() { return DoList(kList); }
1187
1188
1189 /**
1190 * Lists all pinned files from the cache db.
1191 */
1192 304 vector<string> PosixQuotaManager::ListPinned() { return DoList(kListPinned); }
1193
1194
1195 /**
1196 * Lists all sqlite catalog files from the cache db.
1197 */
1198 114 vector<string> PosixQuotaManager::ListCatalogs() {
1199 114 return DoList(kListCatalogs);
1200 }
1201
1202
1203 /**
1204 * Lists only files flagged as volatile (priority removal)
1205 */
1206 114 vector<string> PosixQuotaManager::ListVolatile() {
1207 114 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 1292 void *PosixQuotaManager::MainCommandServer(void *data) {
1367 1292 PosixQuotaManager *quota_mgr = static_cast<PosixQuotaManager *>(data);
1368
1369
1/2
✓ Branch 1 taken 1292 times.
✗ Branch 2 not taken.
1292 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1370
1/2
✓ Branch 1 taken 1292 times.
✗ Branch 2 not taken.
1292 sqlite3_soft_heap_limit(quota_mgr->kSqliteMemPerThread);
1371
1372
2/2
✓ Branch 1 taken 41344 times.
✓ Branch 2 taken 1292 times.
42636 LruCommand command_buffer[kCommandBufferSize];
1373 char description_buffer[kCommandBufferSize * kMaxDescription];
1374 1292 unsigned num_commands = 0;
1375
1376
1/2
✓ Branch 1 taken 5706460 times.
✗ Branch 2 not taken.
5706460 while (read(quota_mgr->pipe_lru_[0], &command_buffer[num_commands],
1377 sizeof(command_buffer[0]))
1378
2/2
✓ Branch 0 taken 5705168 times.
✓ Branch 1 taken 1292 times.
5706460 == sizeof(command_buffer[0])) {
1379 5705168 const CommandType command_type = command_buffer[num_commands].command_type;
1380
1/2
✓ Branch 1 taken 5705168 times.
✗ Branch 2 not taken.
5705168 LogCvmfs(kLogQuota, kLogDebug, "received command %d", command_type);
1381 5705168 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 1904446 times.
✓ Branch 1 taken 3800722 times.
✓ Branch 2 taken 1904294 times.
✓ Branch 3 taken 152 times.
5705168 if ((command_type == kInsert) || (command_type == kInsertVolatile)
1385
4/4
✓ Branch 0 taken 1904218 times.
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 1903800 times.
✓ Branch 3 taken 418 times.
1904294 || (command_type == kPin) || (command_type == kPinRegular)
1386
2/2
✓ Branch 0 taken 1903762 times.
✓ Branch 1 taken 38 times.
1903800 || (command_type == kRegisterMountpoint)
1387
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 1903724 times.
1903762 || (command_type == kSetCleanupPolicy)) {
1388 3801444 const int desc_length = command_buffer[num_commands].desc_length;
1389 3801444 ReadPipe(quota_mgr->pipe_lru_[0],
1390
1/2
✓ Branch 1 taken 3801444 times.
✗ Branch 2 not taken.
3801444 &description_buffer[kMaxDescription * num_commands],
1391 desc_length);
1392 }
1393
1394 // The protocol revision is returned immediately
1395
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 5705130 times.
5705168 if (command_type == kGetProtocolRevision) {
1396
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const int return_pipe = quota_mgr->BindReturnPipe(
1397 command_buffer[num_commands].return_pipe);
1398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (return_pipe < 0)
1399 continue;
1400
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 WritePipe(return_pipe, &quota_mgr->kProtocolRevision,
1401 sizeof(quota_mgr->kProtocolRevision));
1402
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 quota_mgr->UnbindReturnPipe(return_pipe);
1403 38 continue;
1404 38 }
1405
1406 // Register a new mountpoint
1407
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 5705092 times.
5705130 if (command_type == kRegisterMountpoint) {
1408 const std::string mountpoint(
1409 38 &description_buffer[num_commands * kMaxDescription],
1410
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 command_buffer[num_commands].desc_length);
1411
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 quota_mgr->mountpoints_.push_back(mountpoint);
1412
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1413 "Mountpoint %s registered in the group", mountpoint.c_str());
1414 38 continue;
1415 38 }
1416
1417 // Set Cleanup Policy
1418
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 5705054 times.
5705092 if (command_type == kSetCleanupPolicy) {
1419 38 quota_mgr->cleanup_unused_first_ = (description_buffer[num_commands
1420 38 * kMaxDescription]
1421 == 'S')
1422 38 ? true
1423 : false;
1424 38 continue;
1425 }
1426 // Mountpoints are returned immediately
1427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5705054 times.
5705054 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 5705054 times.
5705054 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 152 times.
✓ Branch 1 taken 5704902 times.
5705054 if (command_type == kCleanupRate) {
1467
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 const int return_pipe = quota_mgr->BindReturnPipe(
1468 command_buffer[num_commands].return_pipe);
1469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 if (return_pipe < 0)
1470 continue;
1471 const uint64_t
1472 152 period_s = size; // use the size field to transmit the period
1473
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 uint64_t rate = quota_mgr->cleanup_recorder_.GetNoTicks(period_s);
1474
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 WritePipe(return_pipe, &rate, sizeof(rate));
1475
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 quota_mgr->UnbindReturnPipe(return_pipe);
1476 152 continue;
1477 152 }
1478
1479
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 5704864 times.
5704902 if (command_type == kSetLimit) {
1480
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const int return_pipe = quota_mgr->BindReturnPipe(
1481 command_buffer[num_commands].return_pipe);
1482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (return_pipe < 0)
1483 continue;
1484 38 quota_mgr->limit_ = size; // use the size field to transmit the size
1485 38 quota_mgr->cleanup_threshold_ = size / 2;
1486
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1487 "Quota limit set to %lu / threshold %lu", quota_mgr->limit_,
1488 quota_mgr->cleanup_threshold_);
1489 38 bool ret = true;
1490
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 WritePipe(return_pipe, &ret, sizeof(ret));
1491
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 quota_mgr->UnbindReturnPipe(return_pipe);
1492 38 continue;
1493 38 }
1494
1495 // Reservations are handled immediately and "out of band"
1496
2/2
✓ Branch 0 taken 532 times.
✓ Branch 1 taken 5704332 times.
5704864 if (command_type == kReserve) {
1497 532 bool success = true;
1498
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 const int return_pipe = quota_mgr->BindReturnPipe(
1499 command_buffer[num_commands].return_pipe);
1500
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 532 times.
532 if (return_pipe < 0)
1501 continue;
1502
1503
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1504
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 const string hash_str(hash.ToString());
1505
1/2
✓ Branch 2 taken 532 times.
✗ Branch 3 not taken.
532 LogCvmfs(kLogQuota, kLogDebug, "reserve %lu bytes for %s", size,
1506 hash_str.c_str());
1507
1508
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 if (quota_mgr->pinned_chunks_.find(hash)
1509
2/2
✓ Branch 2 taken 456 times.
✓ Branch 3 taken 76 times.
1064 == quota_mgr->pinned_chunks_.end()) {
1510
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 418 times.
456 if ((quota_mgr->pinned_ + size) > quota_mgr->cleanup_threshold_) {
1511
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 LogCvmfs(kLogQuota, kLogDebug,
1512 "failed to insert %s (pinned), no space", hash_str.c_str());
1513 38 success = false;
1514 } else {
1515
1/2
✓ Branch 1 taken 418 times.
✗ Branch 2 not taken.
418 quota_mgr->pinned_chunks_[hash] = size;
1516 418 quota_mgr->pinned_ += size;
1517
1/2
✓ Branch 1 taken 418 times.
✗ Branch 2 not taken.
418 quota_mgr->CheckHighPinWatermark();
1518 }
1519 }
1520
1521
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 WritePipe(return_pipe, &success, sizeof(success));
1522
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 quota_mgr->UnbindReturnPipe(return_pipe);
1523 532 continue;
1524 532 }
1525
1526 // Back channels are also handled out of band
1527
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 5704180 times.
5704332 if (command_type == kRegisterBackChannel) {
1528
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 const int return_pipe = quota_mgr->BindReturnPipe(
1529 command_buffer[num_commands].return_pipe);
1530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 if (return_pipe < 0)
1531 continue;
1532
1533
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 quota_mgr->UnlinkReturnPipe(command_buffer[num_commands].return_pipe);
1534
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 Block2Nonblock(return_pipe); // back channels are opportunistic
1535
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 shash::Md5 hash;
1536 152 memcpy(hash.digest, command_buffer[num_commands].digest,
1537 152 shash::kDigestSizes[shash::kMd5]);
1538
1539 152 quota_mgr->LockBackChannels();
1540 const map<shash::Md5, int>::const_iterator
1541
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 iter = quota_mgr->back_channels_.find(hash);
1542
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 152 times.
152 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 152 times.
✗ Branch 2 not taken.
152 quota_mgr->back_channels_[hash] = return_pipe;
1548 152 quota_mgr->UnlockBackChannels();
1549
1550 152 char success = 'S';
1551
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 WritePipe(return_pipe, &success, sizeof(success));
1552
1/2
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
152 LogCvmfs(kLogQuota, kLogDebug, "register back channel %s on fd %d",
1553
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
304 hash.ToString().c_str(), return_pipe);
1554
1555 152 continue;
1556 152 }
1557
1558
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 5704104 times.
5704180 if (command_type == kUnregisterBackChannel) {
1559
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 shash::Md5 hash;
1560 76 memcpy(hash.digest, command_buffer[num_commands].digest,
1561 76 shash::kDigestSizes[shash::kMd5]);
1562
1563 76 quota_mgr->LockBackChannels();
1564 const map<shash::Md5, int>::iterator iter = quota_mgr->back_channels_
1565
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 .find(hash);
1566
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 if (iter != quota_mgr->back_channels_.end()) {
1567
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 LogCvmfs(kLogQuota, kLogDebug, "closing back channel %s",
1568
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
152 hash.ToString().c_str());
1569
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 close(iter->second);
1570
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 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 76 quota_mgr->UnlockBackChannels();
1576
1577 76 continue;
1578 76 }
1579
1580 // Unpinnings are also handled immediately with respect to the pinned gauge
1581
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 5704028 times.
5704104 if (command_type == kUnpin) {
1582
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1583
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 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 76 times.
✗ Branch 2 not taken.
76 .find(hash);
1587
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 if (iter != quota_mgr->pinned_chunks_.end()) {
1588 76 quota_mgr->pinned_ -= iter->second;
1589
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 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 76 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
152 if (!FileExists(quota_mgr->cache_dir_ + "/"
1594
4/6
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 38 times.
✓ Branch 10 taken 38 times.
228 + hash.MakePathWithoutSuffix())) {
1595
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 LogCvmfs(kLogQuota, kLogDebug,
1596 "remove orphaned pinned hash %s from cache database",
1597 hash_str.c_str());
1598
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1599 38 hash_str.length(), SQLITE_STATIC);
1600 int retval;
1601
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
38 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1602
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1603 38 0);
1604
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1605 38 hash_str.length(), SQLITE_STATIC);
1606
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 retval = sqlite3_step(quota_mgr->stmt_rm_);
1607
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
38 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1608 38 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 38 times.
✗ Branch 2 not taken.
38 sqlite3_reset(quota_mgr->stmt_rm_);
1614 }
1615
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 sqlite3_reset(quota_mgr->stmt_size_);
1616 }
1617 } else {
1618 LogCvmfs(kLogQuota, kLogDebug, "this chunk was not pinned");
1619 }
1620 76 }
1621
1622 // Immediate commands trigger flushing of the buffer
1623 5704104 const bool immediate_command = (command_type == kCleanup)
1624
2/2
✓ Branch 0 taken 5702888 times.
✓ Branch 1 taken 874 times.
5703762 || (command_type == kList)
1625
2/2
✓ Branch 0 taken 5702584 times.
✓ Branch 1 taken 304 times.
5702888 || (command_type == kListPinned)
1626
2/2
✓ Branch 0 taken 5702470 times.
✓ Branch 1 taken 114 times.
5702584 || (command_type == kListCatalogs)
1627
2/2
✓ Branch 0 taken 5702356 times.
✓ Branch 1 taken 114 times.
5702470 || (command_type == kListVolatile)
1628
2/2
✓ Branch 0 taken 5702242 times.
✓ Branch 1 taken 114 times.
5702356 || (command_type == kRemove)
1629
2/2
✓ Branch 0 taken 5701558 times.
✓ Branch 1 taken 684 times.
5702242 || (command_type == kStatus)
1630
1/2
✓ Branch 0 taken 5701558 times.
✗ Branch 1 not taken.
5701558 || (command_type == kLimits)
1631
3/4
✓ Branch 0 taken 5703762 times.
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5701558 times.
11407866 || (command_type == kPid);
1632
2/2
✓ Branch 0 taken 5701558 times.
✓ Branch 1 taken 2546 times.
5704104 if (!immediate_command)
1633 5701558 num_commands++;
1634
1635
4/4
✓ Branch 0 taken 5525998 times.
✓ Branch 1 taken 178106 times.
✓ Branch 2 taken 2546 times.
✓ Branch 3 taken 5523452 times.
5704104 if ((num_commands == kCommandBufferSize) || immediate_command) {
1636
1/2
✓ Branch 1 taken 180652 times.
✗ Branch 2 not taken.
180652 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1637 description_buffer);
1638
2/2
✓ Branch 0 taken 178106 times.
✓ Branch 1 taken 2546 times.
180652 if (!immediate_command)
1639 178106 num_commands = 0;
1640 }
1641
1642
2/2
✓ Branch 0 taken 2546 times.
✓ Branch 1 taken 5701558 times.
5704104 if (immediate_command) {
1643 // Process cleanup, listings
1644
1/2
✓ Branch 1 taken 2546 times.
✗ Branch 2 not taken.
2546 const int return_pipe = quota_mgr->BindReturnPipe(
1645 command_buffer[num_commands].return_pipe);
1646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2546 times.
2546 if (return_pipe < 0) {
1647 num_commands = 0;
1648 continue;
1649 }
1650
1651 int retval;
1652 2546 sqlite3_stmt *this_stmt_list = NULL;
1653
7/10
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 342 times.
✓ Branch 2 taken 874 times.
✓ Branch 3 taken 304 times.
✓ Branch 4 taken 114 times.
✓ Branch 5 taken 114 times.
✓ Branch 6 taken 684 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2546 switch (command_type) {
1654 114 case kRemove: {
1655
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1656
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 const string hash_str = hash.ToString();
1657
1/2
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 LogCvmfs(kLogQuota, kLogDebug, "manually removing %s",
1658 hash_str.c_str());
1659 114 bool success = false;
1660
1661
1/2
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1662 114 hash_str.length(), SQLITE_STATIC);
1663 int retval;
1664
3/4
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
✓ Branch 4 taken 38 times.
114 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1665
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1666 76 0);
1667
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 const uint64_t is_pinned = sqlite3_column_int64(
1668 76 quota_mgr->stmt_size_, 1);
1669
1670
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1671 76 hash_str.length(), SQLITE_STATIC);
1672
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 retval = sqlite3_step(quota_mgr->stmt_rm_);
1673
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
76 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1674 76 success = true;
1675 76 quota_mgr->gauge_ -= size;
1676
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 38 times.
76 if (is_pinned) {
1677
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 quota_mgr->pinned_chunks_.erase(hash);
1678 38 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 76 times.
✗ Branch 2 not taken.
76 sqlite3_reset(quota_mgr->stmt_rm_);
1685 } else {
1686 // File does not exist
1687 38 success = true;
1688 }
1689
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 sqlite3_reset(quota_mgr->stmt_size_);
1690
1691
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 WritePipe(return_pipe, &success, sizeof(success));
1692 114 break;
1693 114 }
1694 342 case kCleanup:
1695
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 retval = quota_mgr->DoCleanup(size);
1696
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 WritePipe(return_pipe, &retval, sizeof(retval));
1697 342 break;
1698 874 case kList:
1699
1/2
✓ Branch 0 taken 874 times.
✗ Branch 1 not taken.
874 if (!this_stmt_list)
1700 874 this_stmt_list = quota_mgr->stmt_list_;
1701 case kListPinned:
1702
2/2
✓ Branch 0 taken 304 times.
✓ Branch 1 taken 874 times.
1178 if (!this_stmt_list)
1703 304 this_stmt_list = quota_mgr->stmt_list_pinned_;
1704 case kListCatalogs:
1705
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 1178 times.
1292 if (!this_stmt_list)
1706 114 this_stmt_list = quota_mgr->stmt_list_catalogs_;
1707 case kListVolatile:
1708
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 1292 times.
1406 if (!this_stmt_list)
1709 114 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 3802964 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3801558 times.
✓ Branch 4 taken 1406 times.
3802964 while (sqlite3_step(this_stmt_list) == SQLITE_ROW) {
1714
1/2
✓ Branch 2 taken 3801558 times.
✗ Branch 3 not taken.
3801558 string path = "(NULL)";
1715
2/4
✓ Branch 1 taken 3801558 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3801558 times.
✗ Branch 4 not taken.
3801558 if (sqlite3_column_type(this_stmt_list, 0) != SQLITE_NULL) {
1716 7603116 path = string(reinterpret_cast<const char *>(
1717
2/4
✓ Branch 1 taken 3801558 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3801558 times.
✗ Branch 5 not taken.
3801558 sqlite3_column_text(this_stmt_list, 0)));
1718 }
1719 3801558 length = path.length();
1720
1/2
✓ Branch 1 taken 3801558 times.
✗ Branch 2 not taken.
3801558 WritePipe(return_pipe, &length, sizeof(length));
1721
1/2
✓ Branch 0 taken 3801558 times.
✗ Branch 1 not taken.
3801558 if (length > 0)
1722
2/4
✓ Branch 1 taken 3801558 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3801558 times.
✗ Branch 5 not taken.
3801558 WritePipe(return_pipe, &path[0], length);
1723 3801558 }
1724 1406 length = -1;
1725
1/2
✓ Branch 1 taken 1406 times.
✗ Branch 2 not taken.
1406 WritePipe(return_pipe, &length, sizeof(length));
1726
1/2
✓ Branch 1 taken 1406 times.
✗ Branch 2 not taken.
1406 sqlite3_reset(this_stmt_list);
1727 1406 break;
1728 684 case kStatus:
1729
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 WritePipe(return_pipe, &quota_mgr->gauge_, sizeof(quota_mgr->gauge_));
1730
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 WritePipe(return_pipe, &quota_mgr->pinned_,
1731 sizeof(quota_mgr->pinned_));
1732 684 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 2546 times.
✗ Branch 2 not taken.
2546 quota_mgr->UnbindReturnPipe(return_pipe);
1747 2546 num_commands = 0;
1748 }
1749 }
1750
1751
1/2
✓ Branch 1 taken 1292 times.
✗ Branch 2 not taken.
1292 LogCvmfs(kLogQuota, kLogDebug, "stopping cache manager (%d)", errno);
1752
1/2
✓ Branch 1 taken 1292 times.
✗ Branch 2 not taken.
1292 close(quota_mgr->pipe_lru_[0]);
1753
1/2
✓ Branch 1 taken 1292 times.
✗ Branch 2 not taken.
1292 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1754 description_buffer);
1755
1756 // Unpin
1757 1292 command_buffer[0].command_type = kTouch;
1758 1292 for (map<shash::Any, uint64_t>::const_iterator
1759 1292 i = quota_mgr->pinned_chunks_.begin(),
1760 1292 iEnd = quota_mgr->pinned_chunks_.end();
1761
2/2
✓ Branch 1 taken 380 times.
✓ Branch 2 taken 1292 times.
1672 i != iEnd;
1762 380 ++i) {
1763
1/2
✓ Branch 2 taken 380 times.
✗ Branch 3 not taken.
380 command_buffer[0].StoreHash(i->first);
1764
1/2
✓ Branch 1 taken 380 times.
✗ Branch 2 not taken.
380 quota_mgr->ProcessCommandBunch(1, command_buffer, description_buffer);
1765 }
1766
1767 1292 return NULL;
1768 }
1769
1770
1771 3572 void PosixQuotaManager::MakeReturnPipe(int pipe[2]) {
1772
2/2
✓ Branch 0 taken 3458 times.
✓ Branch 1 taken 114 times.
3572 if (!shared_) {
1773 3458 MakePipe(pipe);
1774 3458 return;
1775 }
1776
1777 // Create FIFO in cache directory, store path name (number) in pipe write end
1778 114 int i = 0;
1779 int retval;
1780 do {
1781
2/4
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 152 times.
✗ Branch 6 not taken.
152 retval = mkfifo((workspace_dir_ + "/pipe" + StringifyInt(i)).c_str(), 0600);
1782 152 pipe[1] = i;
1783 152 i++;
1784
3/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 114 times.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
152 } while ((retval == -1) && (errno == EEXIST));
1785
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
114 assert(retval == 0);
1786
1787 // Connect reader's end
1788
3/6
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 114 times.
✗ Branch 10 not taken.
114 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 114 times.
114 assert(pipe[0] >= 0);
1791 114 Nonblock2Block(pipe[0]);
1792 }
1793
1794
1795 4092 void PosixQuotaManager::ParseDirectories(const std::string cache_workspace,
1796 std::string *cache_dir,
1797 std::string *workspace_dir) {
1798
1/2
✓ Branch 1 taken 4092 times.
✗ Branch 2 not taken.
4092 vector<string> dir_tokens(SplitString(cache_workspace, ':'));
1799
2/3
✓ Branch 1 taken 4016 times.
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
4092 switch (dir_tokens.size()) {
1800 4016 case 1:
1801
2/4
✓ Branch 2 taken 4016 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4016 times.
✗ Branch 6 not taken.
4016 *cache_dir = *workspace_dir = dir_tokens[0];
1802 4016 break;
1803 76 case 2:
1804
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 *cache_dir = dir_tokens[0];
1805
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 *workspace_dir = dir_tokens[1];
1806 76 break;
1807 default:
1808 PANIC(NULL);
1809 }
1810 4092 }
1811
1812 38 void PosixQuotaManager::SkipEviction(const EvictCandidate &candidate) {
1813 38 bool res = true;
1814
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 std::string hash_str = candidate.hash.ToString();
1815
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 LogCvmfs(kLogQuota, kLogDebug, "Exclude %s from eviction", hash_str.c_str());
1816
2/4
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
38 sqlite3_bind_text(stmt_block_, 1, &hash_str[0], hash_str.length(),
1817 SQLITE_STATIC);
1818
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 res = (sqlite3_step(stmt_block_) == SQLITE_DONE);
1819
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 sqlite3_reset(stmt_block_);
1820
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(res);
1821 38 }
1822
1823 /**
1824 * Immediately inserts a new pinned catalog. Does cache cleanup if necessary.
1825 *
1826 * \return True on success, false otherwise
1827 */
1828 1652 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 76 times.
✓ Branch 1 taken 1576 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
1652 assert((size > 0) || !is_catalog);
1833
1834
1/2
✓ Branch 1 taken 1652 times.
✗ Branch 2 not taken.
1652 const string hash_str = hash.ToString();
1835
1/2
✓ Branch 3 taken 1652 times.
✗ Branch 4 not taken.
1652 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 1120 times.
✓ Branch 1 taken 532 times.
1652 if (!spawned_) {
1840 // Code duplication here
1841
3/4
✓ Branch 2 taken 1120 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 662 times.
✓ Branch 6 taken 458 times.
1120 if (pinned_chunks_.find(hash) == pinned_chunks_.end()) {
1842
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 586 times.
662 if (pinned_ + size > cleanup_threshold_) {
1843
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 LogCvmfs(kLogQuota, kLogDebug, "failed to insert %s (pinned), no space",
1844 hash_str.c_str());
1845 76 return false;
1846 } else {
1847
1/2
✓ Branch 1 taken 586 times.
✗ Branch 2 not taken.
586 pinned_chunks_[hash] = size;
1848 586 pinned_ += size;
1849
1/2
✓ Branch 1 taken 586 times.
✗ Branch 2 not taken.
586 CheckHighPinWatermark();
1850 }
1851 }
1852
1/2
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
1044 const bool exists = Contains(hash_str);
1853
4/4
✓ Branch 0 taken 586 times.
✓ Branch 1 taken 458 times.
✓ Branch 2 taken 38 times.
✓ Branch 3 taken 548 times.
1044 if (!exists && (gauge_ + size > limit_)) {
1854
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %lu",
1855 gauge_, size);
1856
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const int retval = DoCleanup(cleanup_threshold_);
1857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(retval != 0);
1858 }
1859
1/2
✓ Branch 3 taken 1044 times.
✗ Branch 4 not taken.
1044 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1860 SQLITE_STATIC);
1861
1/2
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
1044 sqlite3_bind_int64(stmt_new_, 2, size);
1862
1/2
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
1044 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1863
1/2
✓ Branch 3 taken 1044 times.
✗ Branch 4 not taken.
1044 sqlite3_bind_text(stmt_new_, 4, &description[0], description.length(),
1864 SQLITE_STATIC);
1865
3/4
✓ Branch 0 taken 930 times.
✓ Branch 1 taken 114 times.
✓ Branch 3 taken 1044 times.
✗ Branch 4 not taken.
1044 sqlite3_bind_int64(stmt_new_, 5, is_catalog ? kFileCatalog : kFileRegular);
1866
1/2
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
1044 sqlite3_bind_int64(stmt_new_, 6, 1);
1867
1/2
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
1044 const int retval = sqlite3_step(stmt_new_);
1868
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1044 assert((retval == SQLITE_DONE) || (retval == SQLITE_OK));
1869
1/2
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
1044 sqlite3_reset(stmt_new_);
1870
2/2
✓ Branch 0 taken 586 times.
✓ Branch 1 taken 458 times.
1044 if (!exists)
1871 586 gauge_ += size;
1872 1044 return true;
1873 }
1874
1875 int pipe_reserve[2];
1876
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 MakeReturnPipe(pipe_reserve);
1877
1878 532 LruCommand cmd;
1879 532 cmd.command_type = kReserve;
1880 532 cmd.SetSize(size);
1881
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 cmd.StoreHash(hash);
1882 532 cmd.return_pipe = pipe_reserve[1];
1883
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1884 bool result;
1885
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 ManagedReadHalfPipe(pipe_reserve[0], &result, sizeof(result));
1886
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 CloseReturnPipe(pipe_reserve);
1887
1888
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 494 times.
532 if (!result)
1889 38 return false;
1890
3/4
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 418 times.
✓ Branch 3 taken 494 times.
✗ Branch 4 not taken.
494 DoInsert(hash, size, description, is_catalog ? kPin : kPinRegular);
1891
1892 494 return true;
1893 1652 }
1894
1895
1896 4016 PosixQuotaManager::PosixQuotaManager(const uint64_t limit,
1897 const uint64_t cleanup_threshold,
1898 4016 const string &cache_workspace)
1899 4016 : shared_(false)
1900 4016 , spawned_(false)
1901 4016 , limit_(limit)
1902 4016 , cleanup_threshold_(cleanup_threshold)
1903 4016 , gauge_(0)
1904 4016 , pinned_(0)
1905 4016 , seq_(0)
1906 4016 , cache_dir_() // initialized in body
1907 4016 , workspace_dir_() // initialized in body
1908 4016 , fd_lock_cachedb_(-1)
1909 4016 , async_delete_(true)
1910 4016 , cachemgr_pid_(0)
1911 4016 , database_(NULL)
1912 4016 , stmt_touch_(NULL)
1913 4016 , stmt_unpin_(NULL)
1914 4016 , stmt_block_(NULL)
1915 4016 , stmt_unblock_(NULL)
1916 4016 , stmt_new_(NULL)
1917 4016 , stmt_lru_(NULL)
1918 4016 , stmt_size_(NULL)
1919 4016 , stmt_rm_(NULL)
1920 4016 , stmt_rm_batch_(NULL)
1921 4016 , stmt_list_(NULL)
1922 4016 , stmt_list_pinned_(NULL)
1923 4016 , stmt_list_catalogs_(NULL)
1924 4016 , stmt_list_volatile_(NULL)
1925 4016 , initialized_(false)
1926 8032 , cleanup_unused_first_(false) {
1927
2/4
✓ Branch 1 taken 4016 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4016 times.
✗ Branch 5 not taken.
4016 ParseDirectories(cache_workspace, &cache_dir_, &workspace_dir_);
1928 4016 pipe_lru_[0] = pipe_lru_[1] = -1;
1929
1/2
✓ Branch 1 taken 4016 times.
✗ Branch 2 not taken.
4016 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 4016 times.
✗ Branch 2 not taken.
4016 cleanup_recorder_.AddRecorder(60, 90 * 60);
1932 // last 18 hours with 20 min resolution
1933
1/2
✓ Branch 1 taken 4016 times.
✗ Branch 2 not taken.
4016 cleanup_recorder_.AddRecorder(20 * 60, 60 * 60 * 18);
1934 // last 4 days with hour resolution
1935
1/2
✓ Branch 1 taken 4016 times.
✗ Branch 2 not taken.
4016 cleanup_recorder_.AddRecorder(60 * 60, 60 * 60 * 24 * 4);
1936
1937 4016 lock_open_files_ = reinterpret_cast<pthread_mutex_t *>(
1938 4016 smalloc(sizeof(pthread_mutex_t)));
1939 4016 }
1940
1941
1942 16060 PosixQuotaManager::~PosixQuotaManager() {
1943 8030 free(lock_open_files_);
1944
1945
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 3901 times.
8030 if (!initialized_)
1946 228 return;
1947
1948
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3901 times.
7802 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 1292 times.
✓ Branch 1 taken 2609 times.
7802 if (spawned_) {
1955 2584 char fin = 0;
1956 2584 WritePipe(pipe_lru_[1], &fin, 1);
1957 2584 close(pipe_lru_[1]);
1958 2584 pthread_join(thread_lru_, NULL);
1959 } else {
1960 5218 ClosePipe(pipe_lru_);
1961 }
1962
1963 7802 CloseDatabase();
1964
14/14
✓ Branch 1 taken 3901 times.
✓ Branch 2 taken 114 times.
✓ Branch 4 taken 3901 times.
✓ Branch 5 taken 114 times.
✓ Branch 7 taken 3901 times.
✓ Branch 8 taken 114 times.
✓ Branch 10 taken 3901 times.
✓ Branch 11 taken 114 times.
✓ Branch 13 taken 3901 times.
✓ Branch 14 taken 114 times.
✓ Branch 16 taken 3901 times.
✓ Branch 17 taken 114 times.
✓ Branch 19 taken 3901 times.
✓ Branch 20 taken 114 times.
17428 }
1965
1966
1967 182324 void PosixQuotaManager::ProcessCommandBunch(const unsigned num,
1968 const LruCommand *commands,
1969 const char *descriptions) {
1970 182324 int retval = sqlite3_exec(database_, "BEGIN", NULL, NULL, NULL);
1971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 182324 times.
182324 assert(retval == SQLITE_OK);
1972
1973
2/2
✓ Branch 0 taken 5701938 times.
✓ Branch 1 taken 182324 times.
5884262 for (unsigned i = 0; i < num; ++i) {
1974
1/2
✓ Branch 1 taken 5701938 times.
✗ Branch 2 not taken.
5701938 const shash::Any hash = commands[i].RetrieveHash();
1975
1/2
✓ Branch 1 taken 5701938 times.
✗ Branch 2 not taken.
5701938 const string hash_str = hash.ToString();
1976 5701938 const unsigned size = commands[i].GetSize();
1977
1/2
✓ Branch 1 taken 5701938 times.
✗ Branch 2 not taken.
5701938 LogCvmfs(kLogQuota, kLogDebug, "processing %s (%d)", hash_str.c_str(),
1978 5701938 commands[i].command_type);
1979
1980 bool exists;
1981
3/4
✓ Branch 0 taken 1900494 times.
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 3801368 times.
✗ Branch 3 not taken.
5701938 switch (commands[i].command_type) {
1982 1900494 case kTouch:
1983
1/2
✓ Branch 1 taken 1900494 times.
✗ Branch 2 not taken.
1900494 sqlite3_bind_int64(stmt_touch_, 1, seq_++);
1984
1/2
✓ Branch 3 taken 1900494 times.
✗ Branch 4 not taken.
1900494 sqlite3_bind_text(stmt_touch_, 2, &hash_str[0], hash_str.length(),
1985 SQLITE_STATIC);
1986
1/2
✓ Branch 1 taken 1900494 times.
✗ Branch 2 not taken.
1900494 retval = sqlite3_step(stmt_touch_);
1987
1/2
✓ Branch 1 taken 1900494 times.
✗ Branch 2 not taken.
1900494 LogCvmfs(kLogQuota, kLogDebug, "touching %s (%ld): %d",
1988 1900494 hash_str.c_str(), seq_ - 1, retval);
1989
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1900494 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1900494 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 1900494 times.
✗ Branch 2 not taken.
1900494 sqlite3_reset(stmt_touch_);
1994 1900494 break;
1995 76 case kUnpin:
1996
1/2
✓ Branch 3 taken 76 times.
✗ Branch 4 not taken.
76 sqlite3_bind_text(stmt_unpin_, 1, &hash_str[0], hash_str.length(),
1997 SQLITE_STATIC);
1998
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 retval = sqlite3_step(stmt_unpin_);
1999
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 LogCvmfs(kLogQuota, kLogDebug, "unpinning %s: %d", hash_str.c_str(),
2000 retval);
2001
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
76 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 76 times.
✗ Branch 2 not taken.
76 sqlite3_reset(stmt_unpin_);
2006 76 break;
2007 3801368 case kPin:
2008 case kPinRegular:
2009 case kInsert:
2010 case kInsertVolatile:
2011 // It could already be in, check
2012
1/2
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
3801368 exists = Contains(hash_str);
2013
2014 // Cleanup, move to trash and unlink
2015
3/4
✓ Branch 0 taken 3801178 times.
✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3801178 times.
3801368 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 3801368 times.
✗ Branch 4 not taken.
3801368 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
2024 SQLITE_STATIC);
2025
1/2
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
3801368 sqlite3_bind_int64(stmt_new_, 2, size);
2026
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 3801216 times.
3801368 if (commands[i].command_type == kInsertVolatile) {
2027
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 sqlite3_bind_int64(stmt_new_, 3, (seq_++) | kVolatileFlag);
2028 } else {
2029
1/2
✓ Branch 1 taken 3801216 times.
✗ Branch 2 not taken.
3801216 sqlite3_bind_int64(stmt_new_, 3, seq_++);
2030 }
2031 3801368 sqlite3_bind_text(stmt_new_, 4, &descriptions[i * kMaxDescription],
2032
1/2
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
3801368 commands[i].desc_length, SQLITE_STATIC);
2033
1/2
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
3801368 sqlite3_bind_int64(
2034 stmt_new_, 5,
2035
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 3801292 times.
3801368 (commands[i].command_type == kPin) ? kFileCatalog : kFileRegular);
2036
1/2
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
3801368 sqlite3_bind_int64(stmt_new_, 6,
2037
2/2
✓ Branch 0 taken 3801292 times.
✓ Branch 1 taken 76 times.
3801368 ((commands[i].command_type == kPin)
2038
2/2
✓ Branch 0 taken 418 times.
✓ Branch 1 taken 3800874 times.
3801292 || (commands[i].command_type == kPinRegular))
2039 ? 1
2040 : 0);
2041
1/2
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
3801368 retval = sqlite3_step(stmt_new_);
2042
1/2
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
3801368 LogCvmfs(kLogQuota, kLogDebug, "insert or replace %s, method %d: %d",
2043 3801368 hash_str.c_str(), commands[i].command_type, retval);
2044
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3801368 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3801368 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 3801368 times.
✗ Branch 2 not taken.
3801368 sqlite3_reset(stmt_new_);
2049
2050
2/2
✓ Branch 0 taken 3801178 times.
✓ Branch 1 taken 190 times.
3801368 if (!exists)
2051 3801178 gauge_ += size;
2052 3801368 break;
2053 default:
2054 // other types should have been taken care of by event loop
2055 PANIC(NULL);
2056 }
2057 5701938 }
2058
2059 182324 retval = sqlite3_exec(database_, "COMMIT", NULL, NULL, NULL);
2060
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 182324 times.
182324 if (retval != SQLITE_OK) {
2061 PANIC(kLogSyslogErr, "failed to commit to cachedb, error %d", retval);
2062 }
2063 182324 }
2064
2065
2066 3940 bool PosixQuotaManager::RebuildDatabase() {
2067 3940 bool result = false;
2068 3940 string sql;
2069 3940 sqlite3_stmt *stmt_select = NULL;
2070 3940 sqlite3_stmt *stmt_insert = NULL;
2071 int sqlerr;
2072 3940 int seq = 0;
2073 char hex[4];
2074 struct stat info;
2075 platform_dirent64 *d;
2076 3940 DIR *dirp = NULL;
2077 3940 string path;
2078
2079
1/2
✓ Branch 1 taken 3940 times.
✗ Branch 2 not taken.
3940 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug, "re-building cache database");
2080
2081 // Empty cache catalog and fscache
2082
1/2
✓ Branch 1 taken 3940 times.
✗ Branch 2 not taken.
3940 sql = "DELETE FROM cache_catalog; DELETE FROM fscache;";
2083
1/2
✓ Branch 2 taken 3940 times.
✗ Branch 3 not taken.
3940 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3940 times.
3940 if (sqlerr != SQLITE_OK) {
2085 LogCvmfs(kLogQuota, kLogDebug, "could not clear cache database");
2086 goto build_return;
2087 }
2088
2089 3940 gauge_ = 0;
2090
2091 // Insert files from cache sub-directories 00 - ff
2092 // TODO(jblomer): fs_traversal
2093
1/2
✓ Branch 1 taken 3940 times.
✗ Branch 2 not taken.
3940 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 979570 times.
✓ Branch 1 taken 3826 times.
983396 for (int i = 0; i <= 0xff; i++) {
2099 979570 snprintf(hex, sizeof(hex), "%02x", i);
2100
3/6
✓ Branch 2 taken 979570 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 979570 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 979570 times.
✗ Branch 9 not taken.
979570 path = cache_dir_ + "/" + string(hex);
2101
3/4
✓ Branch 2 taken 979570 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 114 times.
✓ Branch 5 taken 979456 times.
979570 if ((dirp = opendir(path.c_str())) == NULL) {
2102
1/2
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
2103 "failed to open directory %s (tmpwatch interfering?)",
2104 path.c_str());
2105 114 goto build_return;
2106 }
2107
3/4
✓ Branch 1 taken 2938444 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1958988 times.
✓ Branch 4 taken 979456 times.
2938444 while ((d = platform_readdir(dirp)) != NULL) {
2108
3/6
✓ Branch 2 taken 1958988 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1958988 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1958988 times.
✗ Branch 9 not taken.
3917976 const string file_path = path + "/" + string(d->d_name);
2109
1/2
✓ Branch 2 taken 1958988 times.
✗ Branch 3 not taken.
1958988 if (stat(file_path.c_str(), &info) == 0) {
2110
2/2
✓ Branch 0 taken 1958912 times.
✓ Branch 1 taken 76 times.
1958988 if (!S_ISREG(info.st_mode))
2111 1958950 continue;
2112
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 38 times.
76 if (info.st_size == 0) {
2113
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
2114 "removing empty file %s during automatic cache db rebuild",
2115 file_path.c_str());
2116 38 unlink(file_path.c_str());
2117 38 continue;
2118 }
2119
2120
3/6
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 38 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 38 times.
✗ Branch 10 not taken.
76 string hash = string(hex) + string(d->d_name);
2121
1/2
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
38 sqlite3_bind_text(stmt_insert, 1, hash.data(), hash.length(),
2122 SQLITE_STATIC);
2123
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 sqlite3_bind_int64(stmt_insert, 2, info.st_size);
2124
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 sqlite3_bind_int64(stmt_insert, 3, info.st_atime);
2125
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 38 times.
38 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 38 times.
✗ Branch 2 not taken.
38 sqlite3_reset(stmt_insert);
2130
2131 38 gauge_ += info.st_size;
2132
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 } else {
2133 LogCvmfs(kLogQuota, kLogDebug, "could not stat %s", file_path.c_str());
2134 }
2135
2/3
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 1958950 times.
✗ Branch 3 not taken.
1958988 }
2136
1/2
✓ Branch 1 taken 979456 times.
✗ Branch 2 not taken.
979456 closedir(dirp);
2137 979456 dirp = NULL;
2138 }
2139
1/2
✓ Branch 1 taken 3826 times.
✗ Branch 2 not taken.
3826 sqlite3_finalize(stmt_insert);
2140 3826 stmt_insert = NULL;
2141
2142 // Transfer from temp table in cache catalog
2143
1/2
✓ Branch 1 taken 3826 times.
✗ Branch 2 not taken.
3826 sqlite3_prepare_v2(database_,
2144 "SELECT sha1, size FROM fscache ORDER BY actime;", -1,
2145 &stmt_select, NULL);
2146
1/2
✓ Branch 1 taken 3826 times.
✗ Branch 2 not taken.
3826 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 3864 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 3826 times.
3864 while (sqlite3_step(stmt_select) == SQLITE_ROW) {
2152 const string hash = string(
2153
2/4
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
38 reinterpret_cast<const char *>(sqlite3_column_text(stmt_select, 0)));
2154
1/2
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
38 sqlite3_bind_text(stmt_insert, 1, &hash[0], hash.length(), SQLITE_STATIC);
2155
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 sqlite3_bind_int64(stmt_insert, 2, sqlite3_column_int64(stmt_select, 1));
2156
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 sqlite3_bind_int64(stmt_insert, 3, seq++);
2157 // Might also be a catalog (information is lost)
2158
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 sqlite3_bind_int64(stmt_insert, 4, kFileRegular);
2159
2160
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const int retval = sqlite3_step(stmt_insert);
2161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 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 38 times.
✗ Branch 2 not taken.
38 sqlite3_reset(stmt_insert);
2169
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 }
2170
2171 // Delete temporary table
2172
1/2
✓ Branch 1 taken 3826 times.
✗ Branch 2 not taken.
3826 sql = "DELETE FROM fscache;";
2173
1/2
✓ Branch 2 taken 3826 times.
✗ Branch 3 not taken.
3826 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3826 times.
3826 if (sqlerr != SQLITE_OK) {
2175 LogCvmfs(kLogQuota, kLogDebug, "could not clear temporary table (%d)",
2176 sqlerr);
2177 goto build_return;
2178 }
2179
2180 3826 seq_ = seq;
2181 3826 result = true;
2182
1/2
✓ Branch 1 taken 3826 times.
✗ Branch 2 not taken.
3826 LogCvmfs(kLogQuota, kLogDebug,
2183 "rebuilding finished, sequence %" PRIu64 ", gauge %" PRIu64, seq_,
2184 gauge_);
2185
2186 3940 build_return:
2187
1/2
✓ Branch 0 taken 3940 times.
✗ Branch 1 not taken.
3940 if (stmt_insert)
2188
1/2
✓ Branch 1 taken 3940 times.
✗ Branch 2 not taken.
3940 sqlite3_finalize(stmt_insert);
2189
2/2
✓ Branch 0 taken 3826 times.
✓ Branch 1 taken 114 times.
3940 if (stmt_select)
2190
1/2
✓ Branch 1 taken 3826 times.
✗ Branch 2 not taken.
3826 sqlite3_finalize(stmt_select);
2191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3940 times.
3940 if (dirp)
2192 closedir(dirp);
2193 3940 return result;
2194 3940 }
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 152 void PosixQuotaManager::RegisterBackChannel(int back_channel[2],
2202 const string &channel_id) {
2203
1/2
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
152 if (protocol_revision_ >= 1) {
2204
1/2
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
152 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2205
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 MakeReturnPipe(back_channel);
2206
2207 152 LruCommand cmd;
2208 152 cmd.command_type = kRegisterBackChannel;
2209 152 cmd.return_pipe = back_channel[1];
2210 // Not StoreHash(). This is an MD5 hash.
2211 152 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2212
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2213
2214 char success;
2215
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 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 152 times.
152 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 152 }
2226
2227
2228 /**
2229 * Removes a chunk from cache, if it exists.
2230 */
2231 114 void PosixQuotaManager::Remove(const shash::Any &hash) {
2232
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 const string hash_str = hash.ToString();
2233
2234 int pipe_remove[2];
2235
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 MakeReturnPipe(pipe_remove);
2236
2237 114 LruCommand cmd;
2238 114 cmd.command_type = kRemove;
2239 114 cmd.return_pipe = pipe_remove[1];
2240
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 cmd.StoreHash(hash);
2241
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2242
2243 bool success;
2244
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 ManagedReadHalfPipe(pipe_remove[0], &success, sizeof(success));
2245
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 CloseReturnPipe(pipe_remove);
2246
2247
3/6
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 114 times.
✗ Branch 8 not taken.
114 unlink((cache_dir_ + "/" + hash.MakePathWithoutSuffix()).c_str());
2248 114 }
2249
2250
2251 1368 void PosixQuotaManager::Spawn() {
2252
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 1292 times.
1368 if (spawned_)
2253 76 return;
2254
2255 1292 if (pthread_create(&thread_lru_, NULL, MainCommandServer,
2256 static_cast<void *>(this))
2257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1292 times.
1292 != 0) {
2258 PANIC(kLogDebug, "could not create lru thread");
2259 }
2260
2261 1292 spawned_ = true;
2262 }
2263
2264
2265 /**
2266 * Updates the sequence number of the file specified by the hash.
2267 */
2268 1900669 void PosixQuotaManager::Touch(const shash::Any &hash) {
2269 1900669 LruCommand cmd;
2270 1900669 cmd.command_type = kTouch;
2271
1/2
✓ Branch 1 taken 1900669 times.
✗ Branch 2 not taken.
1900669 cmd.StoreHash(hash);
2272
1/2
✓ Branch 1 taken 1900669 times.
✗ Branch 2 not taken.
1900669 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2273 1900669 }
2274
2275
2276 3344 void PosixQuotaManager::UnbindReturnPipe(int pipe_wronly) {
2277
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 3306 times.
3344 if (shared_)
2278 38 close(pipe_wronly);
2279 3344 }
2280
2281
2282 266 void PosixQuotaManager::UnlinkReturnPipe(int pipe_wronly) {
2283
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 152 times.
266 if (shared_)
2284
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 unlink((workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str());
2285 266 }
2286
2287
2288 796 void PosixQuotaManager::Unpin(const shash::Any &hash) {
2289
2/4
✓ Branch 1 taken 796 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 796 times.
✗ Branch 6 not taken.
796 LogCvmfs(kLogQuota, kLogDebug, "Unpin %s", hash.ToString().c_str());
2290
2291 796 LruCommand cmd;
2292 796 cmd.command_type = kUnpin;
2293
1/2
✓ Branch 1 taken 796 times.
✗ Branch 2 not taken.
796 cmd.StoreHash(hash);
2294
1/2
✓ Branch 1 taken 796 times.
✗ Branch 2 not taken.
796 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2295 796 }
2296
2297
2298 76 void PosixQuotaManager::UnregisterBackChannel(int back_channel[2],
2299 const string &channel_id) {
2300
1/2
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
76 if (protocol_revision_ >= 1) {
2301
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2302
2303 76 LruCommand cmd;
2304 76 cmd.command_type = kUnregisterBackChannel;
2305 // Not StoreHash(). This is an MD5 hash.
2306 76 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2307
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 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 76 times.
✗ Branch 2 not taken.
76 close(back_channel[0]);
2311 } else {
2312 ClosePipe(back_channel);
2313 }
2314 76 }
2315
2316 3805016 void PosixQuotaManager::ManagedReadHalfPipe(int fd, void *buf, size_t nbyte) {
2317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3805016 times.
3805016 const unsigned timeout_ms = cachemgr_pid_ ? 1000 : 0;
2318 3805016 bool result = false;
2319 do {
2320 3805016 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 3805016 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3805016 times.
3805016 } while (!result && getpgid(cachemgr_pid_) >= 0);
2323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3805016 times.
3805016 if (!result) {
2324 PANIC(kLogStderr,
2325 "Error: quota manager could not read from cachemanager pipe");
2326 }
2327 3805016 }
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