GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.cc
Date: 2026-08-23 02:40:52
Exec Total Coverage
Lines: 987 1421 69.5%
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 2162 int PosixQuotaManager::BindReturnPipe(int pipe_wronly) {
67
2/2
✓ Branch 0 taken 2116 times.
✓ Branch 1 taken 46 times.
2162 if (!shared_)
68 2116 return pipe_wronly;
69
70 // Connect writer's end
71
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 const int result = open(
72
2/4
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 46 times.
✗ Branch 6 not taken.
92 (workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str(),
73 O_WRONLY | O_NONBLOCK);
74
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if (result >= 0) {
75 23 Nonblock2Block(result);
76 } else {
77 23 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
78 23 "failed to bind return pipe (%d)", errno);
79 }
80 46 return result;
81 }
82
83
84 606 void PosixQuotaManager::CheckHighPinWatermark() {
85 606 const uint64_t watermark = kHighPinWatermark * cleanup_threshold_ / 100;
86
3/4
✓ Branch 0 taken 606 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✓ Branch 3 taken 537 times.
606 if ((cleanup_threshold_ > 0) && (pinned_ > watermark)) {
87 69 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
88 "high watermark of pinned files (%" PRIu64 "M > %" PRIu64 "M)",
89 69 pinned_ / (1024 * 1024), watermark / (1024 * 1024));
90
2/4
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 69 times.
✗ Branch 6 not taken.
69 BroadcastBackchannels("R"); // clients: please release pinned catalogs
91 }
92 606 }
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 207 bool PosixQuotaManager::Cleanup(const uint64_t leave_size) {
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 207 times.
207 if (!spawned_)
129 return DoCleanup(leave_size);
130
131 bool result;
132 int pipe_cleanup[2];
133
1/2
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
207 MakeReturnPipe(pipe_cleanup);
134
135 207 LruCommand cmd;
136 207 cmd.command_type = kCleanup;
137 207 cmd.size = leave_size;
138 207 cmd.return_pipe = pipe_cleanup[1];
139
140
1/2
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
207 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
141
1/2
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
207 ManagedReadHalfPipe(pipe_cleanup[0], &result, sizeof(result));
142
1/2
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
207 CloseReturnPipe(pipe_cleanup);
143
144 207 return result;
145 }
146
147
148 2354 void PosixQuotaManager::CloseDatabase() {
149
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_list_catalogs_)
150 2354 sqlite3_finalize(stmt_list_catalogs_);
151
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_list_pinned_)
152 2354 sqlite3_finalize(stmt_list_pinned_);
153
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_list_volatile_)
154 2354 sqlite3_finalize(stmt_list_volatile_);
155
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_list_)
156 2354 sqlite3_finalize(stmt_list_);
157
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_lru_)
158 2354 sqlite3_finalize(stmt_lru_);
159
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_rm_)
160 2354 sqlite3_finalize(stmt_rm_);
161
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_rm_batch_)
162 2354 sqlite3_finalize(stmt_rm_batch_);
163
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_size_)
164 2354 sqlite3_finalize(stmt_size_);
165
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_touch_)
166 2354 sqlite3_finalize(stmt_touch_);
167
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_unpin_)
168 2354 sqlite3_finalize(stmt_unpin_);
169
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_block_)
170 2354 sqlite3_finalize(stmt_block_);
171
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_unblock_)
172 2354 sqlite3_finalize(stmt_unblock_);
173
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (stmt_new_)
174 2354 sqlite3_finalize(stmt_new_);
175
1/2
✓ Branch 0 taken 2354 times.
✗ Branch 1 not taken.
2354 if (database_)
176 2354 sqlite3_close(database_);
177 2354 UnlockFile(fd_lock_cachedb_);
178
179 2354 stmt_list_catalogs_ = NULL;
180 2354 stmt_list_pinned_ = NULL;
181 2354 stmt_list_volatile_ = NULL;
182 2354 stmt_list_ = NULL;
183 2354 stmt_rm_ = NULL;
184 2354 stmt_rm_batch_ = NULL;
185 2354 stmt_size_ = NULL;
186 2354 stmt_touch_ = NULL;
187 2354 stmt_unpin_ = NULL;
188 2354 stmt_block_ = NULL;
189 2354 stmt_unblock_ = NULL;
190 2354 stmt_new_ = NULL;
191 2354 database_ = NULL;
192
193 2354 pinned_chunks_.clear();
194 2354 }
195
196
197 2047 void PosixQuotaManager::CloseReturnPipe(int pipe[2]) {
198
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 2001 times.
2047 if (shared_) {
199 46 close(pipe[0]);
200 46 UnlinkReturnPipe(pipe[1]);
201 } else {
202 2001 ClosePipe(pipe);
203 }
204 2047 }
205
206
207 2301532 bool PosixQuotaManager::Contains(const string &hash_str) {
208 2301532 bool result = false;
209
210 2301532 sqlite3_bind_text(stmt_size_, 1, &hash_str[0], hash_str.length(),
211 SQLITE_STATIC);
212
2/2
✓ Branch 1 taken 443 times.
✓ Branch 2 taken 2301089 times.
2301532 if (sqlite3_step(stmt_size_) == SQLITE_ROW)
213 443 result = true;
214 2301532 sqlite3_reset(stmt_size_);
215 2301532 LogCvmfs(kLogQuota, kLogDebug, "contains %s returns %d", hash_str.c_str(),
216 result);
217
218 2301532 return result;
219 }
220
221
222 2309 void PosixQuotaManager::CheckFreeSpace() {
223
3/4
✓ Branch 0 taken 2309 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 2286 times.
2309 if ((limit_ == 0) || (gauge_ >= limit_))
224 23 return;
225
226 struct statvfs vfs_info;
227
1/2
✓ Branch 1 taken 2286 times.
✗ Branch 2 not taken.
2286 const int retval = statvfs((cache_dir_ + "/cachedb").c_str(), &vfs_info);
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2286 times.
2286 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 2286 const int64_t free_space_byte = vfs_info.f_bavail * vfs_info.f_bsize;
235
1/2
✓ Branch 1 taken 2286 times.
✗ Branch 2 not taken.
2286 LogCvmfs(kLogQuota, kLogDebug, "free space: %" PRId64 " MB",
236 free_space_byte / (1024 * 1024));
237
238 2286 const int64_t required_byte = limit_ - gauge_;
239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2286 times.
2286 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 2378 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 46 times.
✓ Branch 1 taken 2332 times.
2378 if (cleanup_threshold >= limit) {
253 46 LogCvmfs(kLogQuota, kLogDebug,
254 "invalid parameters: limit %" PRIu64 ", "
255 "cleanup_threshold %" PRIu64,
256 limit, cleanup_threshold);
257 46 return NULL;
258 }
259
260 PosixQuotaManager *quota_manager = new PosixQuotaManager(
261
1/2
✓ Branch 2 taken 2332 times.
✗ Branch 3 not taken.
2332 limit, cleanup_threshold, cache_workspace);
262
263 // Initialize cache catalog
264
2/2
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 2309 times.
2332 if (!quota_manager->InitDatabase(rebuild_database)) {
265
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 delete quota_manager;
266 23 return NULL;
267 }
268 2309 quota_manager->CheckFreeSpace();
269 2309 MakePipe(quota_manager->pipe_lru_);
270
271 2309 quota_manager->protocol_revision_ = kProtocolRevision;
272 2309 quota_manager->initialized_ = true;
273 2309 return quota_manager;
274 }
275
276
277 /**
278 * Connects to a running shared local quota manager. Creates one if necessary.
279 */
280 46 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 46 string cache_dir;
287 46 string workspace_dir;
288
2/4
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
46 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 46 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
46 const int fd_lockfile = LockFile(workspace_dir + "/lock_cachemgr");
294
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if (fd_lockfile < 0) {
295
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
296
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
46 (workspace_dir + "/lock_cachemgr").c_str(), errno);
297 23 return NULL;
298 }
299
300 PosixQuotaManager *quota_mgr = new PosixQuotaManager(limit, cleanup_threshold,
301
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 cache_workspace);
302 23 quota_mgr->shared_ = true;
303 23 quota_mgr->spawned_ = true;
304
305 // Try to connect to pipe
306
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const string fifo_path = workspace_dir + "/cachemgr";
307
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug, "trying to connect to existing pipe");
308
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 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 23 const int connect_error = errno;
356
357 // Lock file: let existing cache manager finish first
358
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 const int fd_lockfile_fifo = LockFile(workspace_dir + "/lock_cachemgr.fifo");
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 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 23 times.
✗ Branch 2 not taken.
23 UnlockFile(fd_lockfile_fifo);
367
368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 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 23 int retval = mkfifo(fifo_path.c_str(), 0600);
375
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 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 23 times.
✗ Branch 2 not taken.
23 MakePipe(pipe_boot);
387
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 MakePipe(pipe_handshake);
388
389 23 vector<string> command_line;
390
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 command_line.push_back(exe_path);
391
2/4
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
23 command_line.push_back("__cachemgr__");
392
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 command_line.push_back(cache_workspace);
393
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 command_line.push_back(StringifyInt(pipe_boot[1]));
394
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 command_line.push_back(StringifyInt(pipe_handshake[0]));
395
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 command_line.push_back(StringifyInt(limit));
396
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 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 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 command_line.push_back(StringifyInt(true)); // foreground
400
3/6
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
23 command_line.push_back(StringifyInt(GetLogSyslogLevel()));
401
3/6
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
23 command_line.push_back(StringifyInt(GetLogSyslogFacility()));
402
5/14
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 23 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
23 command_line.push_back(GetLogDebugFile() + ":" + GetLogMicroSyslog());
403
404 23 set<int> preserve_filedes;
405
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 preserve_filedes.insert(0);
406
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 preserve_filedes.insert(1);
407
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 preserve_filedes.insert(2);
408
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 preserve_filedes.insert(pipe_boot[1]);
409
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 preserve_filedes.insert(pipe_handshake[0]);
410
411
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (foreground) {
412
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 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 23 times.
23 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 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug,
428 "new cache manager pid: %d protocol revision %d", new_cachemgr_pid,
429 QuotaManager::kProtocolRevision);
430 23 quota_mgr->SetCacheMgrPid(new_cachemgr_pid);
431
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
23 const int fd_lockfile_rw = open((workspace_dir + "/lock_cachemgr").c_str(),
432 O_RDWR | O_TRUNC, 0600);
433 23 const unsigned magic_number = PosixQuotaManager::kLockFileMagicNumber;
434
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const bool result_mn = SafeWrite(fd_lockfile_rw, &magic_number,
435 sizeof(magic_number));
436
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const bool result = SafeWrite(fd_lockfile_rw, &new_cachemgr_pid,
437 sizeof(new_cachemgr_pid));
438
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 if (!result || !result_mn) {
439 PANIC(kLogSyslogErr, "could not write cache manager pid to lockfile");
440 }
441
442
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 close(fd_lockfile_rw);
443 // Wait for cache manager to be ready
444
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 close(pipe_boot[1]);
445
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 close(pipe_handshake[0]);
446 char buf;
447
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
23 if (read(pipe_boot[0], &buf, 1) != 1) {
448
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 UnlockFile(fd_lockfile);
449
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 close(pipe_boot[0]);
450
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 close(pipe_handshake[1]);
451
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 delete quota_mgr;
452
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
453 "cache manager did not start");
454 23 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 46 }
494
495
496 230 bool PosixQuotaManager::DoCleanup(const uint64_t leave_size) {
497
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 184 times.
230 if (gauge_ <= leave_size)
498 46 return true;
499
500 // TODO(jblomer) transaction
501
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
502 "clean up cache until at most %lu KB is used", leave_size / 1024);
503
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 LogCvmfs(kLogQuota, kLogDebug, "gauge %" PRIu64, gauge_);
504
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 cleanup_recorder_.Tick();
505
506 bool result;
507 184 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 184 int64_t max_acseq = -1;
513 184 std::vector<EvictCandidate> lru_ordered_open;
514
515 do {
516
1/2
✓ Branch 1 taken 1334 times.
✗ Branch 2 not taken.
1334 sqlite3_reset(stmt_lru_);
517
3/4
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 1150 times.
✓ Branch 3 taken 1334 times.
✗ Branch 4 not taken.
1518 sqlite3_bind_int64(stmt_lru_, 1,
518 184 (max_acseq == -1) ? std::numeric_limits<int64_t>::min()
519 : (max_acseq + 1));
520
521 1334 std::vector<EvictCandidate> candidates;
522
1/2
✓ Branch 1 taken 1334 times.
✗ Branch 2 not taken.
1334 candidates.reserve(kEvictBatchSize);
523 1334 string hash_str;
524 1334 unsigned i = 0;
525
3/4
✓ Branch 1 taken 1197564 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1196230 times.
✓ Branch 4 taken 1334 times.
1197564 while (sqlite3_step(stmt_lru_) == SQLITE_ROW) {
526 hash_str = reinterpret_cast<const char *>(
527
2/4
✓ Branch 1 taken 1196230 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1196230 times.
✗ Branch 5 not taken.
1196230 sqlite3_column_text(stmt_lru_, 0));
528
1/2
✓ Branch 2 taken 1196230 times.
✗ Branch 3 not taken.
1196230 LogCvmfs(kLogQuota, kLogDebug, "add %s to candidates for eviction",
529 hash_str.c_str());
530
1/2
✓ Branch 1 taken 1196230 times.
✗ Branch 2 not taken.
1196230 candidates.push_back(
531
1/2
✓ Branch 1 taken 1196230 times.
✗ Branch 2 not taken.
1196230 EvictCandidate(shash::MkFromHexPtr(shash::HexPtr(hash_str)),
532 1196230 sqlite3_column_int64(stmt_lru_, 1),
533
2/4
✓ Branch 1 taken 1196230 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1196230 times.
✗ Branch 5 not taken.
1196230 sqlite3_column_int64(stmt_lru_, 2)));
534 1196230 i++;
535 }
536
2/2
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 1311 times.
1334 if (candidates.empty()) {
537
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug, "no more entries to evict");
538 23 break;
539 }
540
541 1311 const unsigned N = candidates.size();
542
543 1311 open_files_.clear();
544
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1311 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
2622 open_files_ = (cleanup_unused_first_) ? CollectAllOpenHashes()
545 1311 : std::vector<shash::Short>();
546
547
2/2
✓ Branch 0 taken 1150207 times.
✓ Branch 1 taken 1150 times.
1151357 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 1150207 times.
✗ Branch 3 not taken.
1150207 const bool is_pinned = pinned_chunks_.find(candidates[i].hash)
552 2300414 != 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 1150207 bool is_open = false;
565
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1150207 times.
1150207 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 23 times.
✓ Branch 1 taken 1150184 times.
1150207 if (is_pinned) {
573
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 SkipEviction(candidates[i]);
574 23 continue;
575 }
576
577
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1150184 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1150184 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 1150184 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1150184 times.
✗ Branch 5 not taken.
2300368 trash.push_back(cache_dir_ + "/"
584
2/4
✓ Branch 2 taken 1150184 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1150184 times.
✗ Branch 6 not taken.
3450552 + candidates[i].hash.MakePathWithoutSuffix());
585 1150184 gauge_ -= candidates[i].size;
586 1150184 max_acseq = candidates[i].acseq;
587
1/2
✓ Branch 2 taken 1150184 times.
✗ Branch 3 not taken.
1150184 LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
588
1/2
✓ Branch 2 taken 1150184 times.
✗ Branch 3 not taken.
2300368 candidates[i].hash.ToString().c_str(), gauge_);
589
590
2/2
✓ Branch 0 taken 161 times.
✓ Branch 1 taken 1150023 times.
1150184 if (gauge_ <= leave_size)
591 161 break;
592 }
593
6/6
✓ Branch 1 taken 1311 times.
✓ Branch 2 taken 23 times.
✓ Branch 4 taken 1311 times.
✓ Branch 5 taken 23 times.
✓ Branch 6 taken 1150 times.
✓ Branch 7 taken 161 times.
2668 } while (gauge_ > leave_size);
594
595
1/2
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
184 if (max_acseq != -1) {
596
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 sqlite3_bind_int64(stmt_rm_batch_, 1, max_acseq);
597
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 result = (sqlite3_step(stmt_rm_batch_) == SQLITE_DONE);
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 184 times.
184 assert(result);
599
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 sqlite3_reset(stmt_rm_batch_);
600
601
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 result = (sqlite3_step(stmt_unblock_) == SQLITE_DONE);
602
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 sqlite3_reset(stmt_unblock_);
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 184 times.
184 assert(result);
604 }
605
606
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 184 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 184 times.
184 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 184 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 184 times.
184 if (!EmptyTrash(trash))
618 return false;
619
620
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 161 times.
184 if (gauge_ > leave_size) {
621
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
622 "request to clean until %" PRIu64 ", "
623 "but effective gauge is %" PRIu64,
624 leave_size, gauge_);
625 23 return false;
626 }
627 161 return true;
628 184 }
629
630 184 bool PosixQuotaManager::EmptyTrash(const std::vector<std::string> &trash) {
631
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 184 times.
184 if (trash.empty())
632 return true;
633
634
2/2
✓ Branch 0 taken 138 times.
✓ Branch 1 taken 46 times.
184 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 138 times.
138 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 138 times.
✗ Branch 1 not taken.
138 if (pid > 0)
656
1/2
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
138 waitpid(pid, &statloc, 0);
657 else
658 return false;
659 }
660 } else { // !async_delete_
661
2/2
✓ Branch 1 taken 69 times.
✓ Branch 2 taken 46 times.
115 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
662 69 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
663 69 unlink(trash[i].c_str());
664 }
665 }
666 184 return true;
667 }
668
669
670 2301017 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 2301017 times.
✗ Branch 2 not taken.
2301017 const string hash_str = hash.ToString();
675
1/2
✓ Branch 3 taken 2301017 times.
✗ Branch 4 not taken.
2301017 LogCvmfs(kLogQuota, kLogDebug, "insert into lru %s, path %s, method %d",
676 hash_str.c_str(), description.c_str(), command_type);
677 2301017 const unsigned desc_length = (description.length() > kMaxDescription)
678 ? kMaxDescription
679
1/2
✓ Branch 0 taken 2301017 times.
✗ Branch 1 not taken.
2301017 : description.length();
680
681 LruCommand *cmd = reinterpret_cast<LruCommand *>(
682 2301017 alloca(sizeof(LruCommand) + desc_length));
683 2301017 new (cmd) LruCommand;
684 2301017 cmd->command_type = command_type;
685 2301017 cmd->SetSize(size);
686
1/2
✓ Branch 1 taken 2301017 times.
✗ Branch 2 not taken.
2301017 cmd->StoreHash(hash);
687 2301017 cmd->desc_length = desc_length;
688 2301017 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &description[0],
689 desc_length);
690
1/2
✓ Branch 1 taken 2301017 times.
✗ Branch 2 not taken.
2301017 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
691 2301017 }
692
693
694 851 vector<string> PosixQuotaManager::DoList(const CommandType list_command) {
695 851 vector<string> result;
696
697 int pipe_list[2];
698
1/2
✓ Branch 1 taken 851 times.
✗ Branch 2 not taken.
851 MakeReturnPipe(pipe_list);
699 char description_buffer[kMaxDescription];
700
701 851 LruCommand cmd;
702 851 cmd.command_type = list_command;
703 851 cmd.return_pipe = pipe_list[1];
704
1/2
✓ Branch 1 taken 851 times.
✗ Branch 2 not taken.
851 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
705
706 int length;
707 do {
708
1/2
✓ Branch 1 taken 2301794 times.
✗ Branch 2 not taken.
2301794 ManagedReadHalfPipe(pipe_list[0], &length, sizeof(length));
709
2/2
✓ Branch 0 taken 2300943 times.
✓ Branch 1 taken 851 times.
2301794 if (length > 0) {
710
1/2
✓ Branch 1 taken 2300943 times.
✗ Branch 2 not taken.
2300943 ReadPipe(pipe_list[0], description_buffer, length);
711
2/4
✓ Branch 2 taken 2300943 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2300943 times.
✗ Branch 6 not taken.
2300943 result.push_back(string(description_buffer, length));
712 }
713
2/2
✓ Branch 0 taken 2300943 times.
✓ Branch 1 taken 851 times.
2301794 } while (length >= 0);
714
715
1/2
✓ Branch 1 taken 851 times.
✗ Branch 2 not taken.
851 CloseReturnPipe(pipe_list);
716 1702 return result;
717 }
718
719
720 975 uint64_t PosixQuotaManager::GetCapacity() {
721
1/2
✓ Branch 0 taken 975 times.
✗ Branch 1 not taken.
975 if (limit_ != (uint64_t)(-1))
722 975 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 165 uint64_t PosixQuotaManager::GetMaxFileSize() {
756 165 return limit_ - cleanup_threshold_;
757 }
758
759
760 23 pid_t PosixQuotaManager::GetPid() {
761
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
23 if (!shared_ || !spawned_) {
762 23 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 23 uint32_t PosixQuotaManager::GetProtocolRevision() {
783 int pipe_revision[2];
784
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 MakeReturnPipe(pipe_revision);
785
786 23 LruCommand cmd;
787 23 cmd.command_type = kGetProtocolRevision;
788 23 cmd.return_pipe = pipe_revision[1];
789
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
790
791 uint32_t revision;
792
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 ManagedReadHalfPipe(pipe_revision[0], &revision, sizeof(revision));
793
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 CloseReturnPipe(pipe_revision);
794 23 return revision;
795 }
796
797 975 void PosixQuotaManager::SetCleanupPolicy(bool cleanup_unused_first) {
798
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 975 times.
975 if (protocol_revision_ < 3)
799 return;
800
801
3/4
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 952 times.
✓ Branch 3 taken 975 times.
✗ Branch 4 not taken.
975 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 23 times.
✓ Branch 1 taken 952 times.
975 char policy = (cleanup_unused_first) ? 'S' : 'R'; // S: smart, R: regular;
806
807 LruCommand *cmd = reinterpret_cast<LruCommand *>(
808 975 alloca(sizeof(LruCommand) + sizeof(policy)));
809 975 new (cmd) LruCommand;
810 975 cmd->command_type = kSetCleanupPolicy;
811 975 cmd->desc_length = sizeof(policy);
812 975 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &policy,
813 sizeof(policy));
814
1/2
✓ Branch 1 taken 975 times.
✗ Branch 2 not taken.
975 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + sizeof(policy));
815 }
816
817 23 void PosixQuotaManager::RegisterMountpoint(const std::string &mountpoint) {
818
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (protocol_revision_ < 3)
819 return;
820
821 23 LogCvmfs(kLogQuota, kLogDebug, "Register Mountpoint %s", mountpoint.c_str());
822
823 23 const unsigned desc_length = (mountpoint.size() > kMaxDescription)
824 ? kMaxDescription
825
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 : mountpoint.size();
826 LruCommand *cmd = reinterpret_cast<LruCommand *>(
827 23 alloca(sizeof(LruCommand) + desc_length));
828 23 new (cmd) LruCommand;
829 23 cmd->command_type = kRegisterMountpoint;
830 23 cmd->desc_length = desc_length;
831 23 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), mountpoint.data(),
832 desc_length);
833 23 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
834 }
835
836 23 std::string PosixQuotaManager::ReadPipeString(int fd, size_t size) {
837
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (size == 0)
838 return "";
839
840
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 std::vector<char> buf(size);
841
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 ManagedReadHalfPipe(fd, buf.data(), size);
842
1/2
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
23 return std::string(buf.data(), size);
843 23 }
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 414 void PosixQuotaManager::GetSharedStatus(uint64_t *gauge, uint64_t *pinned) {
885 int pipe_status[2];
886
1/2
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
414 MakeReturnPipe(pipe_status);
887
888 414 LruCommand cmd;
889 414 cmd.command_type = kStatus;
890 414 cmd.return_pipe = pipe_status[1];
891
1/2
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
414 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
892
1/2
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
414 ManagedReadHalfPipe(pipe_status[0], gauge, sizeof(*gauge));
893
1/2
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
414 ReadPipe(pipe_status[0], pinned, sizeof(*pinned));
894
1/2
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
414 CloseReturnPipe(pipe_status);
895 414 }
896
897 23 bool PosixQuotaManager::SetSharedLimit(uint64_t limit) {
898 int pipe_set_limit[2];
899 bool result;
900
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 MakeReturnPipe(pipe_set_limit);
901
902 23 LruCommand cmd;
903 23 cmd.command_type = kSetLimit;
904 23 cmd.size = limit;
905 23 cmd.return_pipe = pipe_set_limit[1];
906
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
907
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 ReadHalfPipe(pipe_set_limit[0], &result, sizeof(result));
908
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 CloseReturnPipe(pipe_set_limit);
909 23 return result;
910 }
911
912
913 23 bool PosixQuotaManager::SetLimit(uint64_t size) {
914
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 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 23 return SetSharedLimit(size);
923 }
924
925 2272 uint64_t PosixQuotaManager::GetSize() {
926
2/2
✓ Branch 0 taken 1904 times.
✓ Branch 1 taken 368 times.
2272 if (!spawned_)
927 1904 return gauge_;
928 uint64_t gauge, size_pinned;
929
1/2
✓ Branch 1 taken 368 times.
✗ Branch 2 not taken.
368 GetSharedStatus(&gauge, &size_pinned);
930 368 return gauge;
931 }
932
933
934 46 uint64_t PosixQuotaManager::GetSizePinned() {
935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (!spawned_)
936 return pinned_;
937 uint64_t gauge, size_pinned;
938
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 GetSharedStatus(&gauge, &size_pinned);
939 46 return size_pinned;
940 }
941
942
943 92 uint64_t PosixQuotaManager::GetCleanupRate(uint64_t period_s) {
944
2/4
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
92 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 92 times.
✗ Branch 2 not taken.
92 MakeReturnPipe(pipe_cleanup_rate);
950 92 LruCommand cmd;
951 92 cmd.command_type = kCleanupRate;
952 92 cmd.size = period_s;
953 92 cmd.return_pipe = pipe_cleanup_rate[1];
954
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
955
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 ManagedReadHalfPipe(pipe_cleanup_rate[0], &cleanup_rate,
956 sizeof(cleanup_rate));
957
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 CloseReturnPipe(pipe_cleanup_rate);
958
959 92 return cleanup_rate;
960 }
961
962
963 2447 bool PosixQuotaManager::InitDatabase(const bool rebuild_database) {
964 2447 string sql;
965 sqlite3_stmt *stmt;
966
967
2/4
✓ Branch 1 taken 2447 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2447 times.
✗ Branch 5 not taken.
2447 fd_lock_cachedb_ = LockFile(workspace_dir_ + "/lock_cachedb");
968
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2424 times.
2447 if (fd_lock_cachedb_ < 0) {
969
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug, "failed to create cachedb lock");
970 23 return false;
971 }
972
973 2424 bool retry = false;
974
1/2
✓ Branch 1 taken 2424 times.
✗ Branch 2 not taken.
2424 const string db_file = cache_dir_ + "/cachedb";
975
2/2
✓ Branch 0 taken 2308 times.
✓ Branch 1 taken 116 times.
2424 if (rebuild_database) {
976
1/2
✓ Branch 2 taken 116 times.
✗ Branch 3 not taken.
116 LogCvmfs(kLogQuota, kLogDebug, "rebuild database, unlinking existing (%s)",
977 db_file.c_str());
978 116 unlink(db_file.c_str());
979
1/2
✓ Branch 1 taken 116 times.
✗ Branch 2 not taken.
116 unlink((db_file + "-journal").c_str());
980 }
981
982 2308 init_recover:
983
1/2
✓ Branch 2 taken 2424 times.
✗ Branch 3 not taken.
2424 int err = sqlite3_open(db_file.c_str(), &database_);
984
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2424 times.
2424 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 2424 times.
✗ Branch 2 not taken.
2424 " CONSTRAINT pk_properties PRIMARY KEY(key));";
1001
1/2
✓ Branch 2 taken 2424 times.
✗ Branch 3 not taken.
2424 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2424 times.
2424 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 2424 times.
✗ Branch 2 not taken.
2424 "ALTER TABLE cache_catalog ADD pinned INTEGER";
1021
1/2
✓ Branch 2 taken 2424 times.
✗ Branch 3 not taken.
2424 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2424 times.
2424 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 2424 times.
✗ Branch 2 not taken.
2424 sql = "UPDATE cache_catalog SET pinned=0;";
1034
1/2
✓ Branch 2 taken 2424 times.
✗ Branch 3 not taken.
2424 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1035
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2424 times.
2424 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 2424 times.
✗ Branch 2 not taken.
2424 "VALUES ('schema', '1.0')";
1044
1/2
✓ Branch 2 taken 2424 times.
✗ Branch 3 not taken.
2424 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1045
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2424 times.
2424 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 2424 times.
✗ Branch 2 not taken.
2424 sql = "SELECT count(*) FROM cache_catalog;";
1053
1/2
✓ Branch 2 taken 2424 times.
✗ Branch 3 not taken.
2424 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1054
2/4
✓ Branch 1 taken 2424 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2424 times.
✗ Branch 4 not taken.
2424 if (sqlite3_step(stmt) == SQLITE_ROW) {
1055
6/8
✓ Branch 1 taken 2424 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
✓ Branch 4 taken 2332 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 92 times.
✓ Branch 7 taken 2332 times.
✓ Branch 8 taken 92 times.
2424 if ((sqlite3_column_int64(stmt, 0)) == 0 || rebuild_database) {
1056
1/2
✓ Branch 1 taken 2332 times.
✗ Branch 2 not taken.
2332 LogCvmfs(kLogCvmfs, kLogDebug,
1057 "CernVM-FS: building lru cache database...");
1058
3/4
✓ Branch 1 taken 2332 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
✓ Branch 4 taken 2263 times.
2332 if (!RebuildDatabase()) {
1059
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 LogCvmfs(kLogQuota, kLogDebug,
1060 "could not build cache database from file system");
1061
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 sqlite3_finalize(stmt);
1062 69 goto init_database_fail;
1063 }
1064 }
1065
1/2
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 sql = "SELECT sum(size) FROM cache_catalog;";
1074
1/2
✓ Branch 2 taken 2355 times.
✗ Branch 3 not taken.
2355 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1075
2/4
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2355 times.
✗ Branch 4 not taken.
2355 if (sqlite3_step(stmt) == SQLITE_ROW) {
1076
1/2
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 sqlite3_finalize(stmt);
1083
1084 // Highest seq-no?
1085
1/2
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
2355 sql = "SELECT coalesce(max(acseq & (~(1<<63))), 0) FROM cache_catalog;";
1086
1/2
✓ Branch 2 taken 2355 times.
✗ Branch 3 not taken.
2355 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1087
2/4
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2355 times.
✗ Branch 4 not taken.
2355 if (sqlite3_step(stmt) == SQLITE_ROW) {
1088
1/2
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 sqlite3_finalize(stmt);
1095
1096 // Prepare touch, new, remove statements
1097
1/2
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 sqlite3_prepare_v2(database_, "DELETE FROM cache_catalog WHERE sha1=:sha1;",
1122 -1, &stmt_rm_, NULL);
1123
1/2
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 3 not taken.
2355 sqlite3_prepare_v2(database_,
1127
1/2
✓ Branch 2 taken 2355 times.
✗ Branch 3 not taken.
4710 (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 2355 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2355 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2355 times.
✗ Branch 8 not taken.
9420 + StringifyInt(kEvictBatchSize) + ";")
1132 .c_str(),
1133 -1, &stmt_lru_, NULL);
1134
1/2
✓ Branch 2 taken 2355 times.
✗ Branch 3 not taken.
2355 sqlite3_prepare_v2(database_,
1135 ("SELECT path FROM cache_catalog WHERE type="
1136
3/6
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2355 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2355 times.
✗ Branch 8 not taken.
4710 + StringifyInt(kFileRegular) + ";")
1137 .c_str(),
1138 -1, &stmt_list_, NULL);
1139
1/2
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 2 not taken.
2355 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 2355 times.
✗ Branch 3 not taken.
2355 sqlite3_prepare_v2(database_,
1146 ("SELECT path FROM cache_catalog WHERE type="
1147
3/6
✓ Branch 1 taken 2355 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2355 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2355 times.
✗ Branch 8 not taken.
4710 + StringifyInt(kFileCatalog) + ";")
1148 .c_str(),
1149 -1, &stmt_list_catalogs_, NULL);
1150 2355 return true;
1151
1152 69 init_database_fail:
1153
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 sqlite3_close(database_);
1154 69 database_ = NULL;
1155
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 UnlockFile(fd_lock_cachedb_);
1156 69 return false;
1157 2447 }
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 2300626 void PosixQuotaManager::Insert(const shash::Any &any_hash,
1165 const uint64_t size,
1166 const string &description) {
1167 2300626 DoInsert(any_hash, size, description, kInsert);
1168 2300626 }
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 92 void PosixQuotaManager::InsertVolatile(const shash::Any &any_hash,
1177 const uint64_t size,
1178 const string &description) {
1179 92 DoInsert(any_hash, size, description, kInsertVolatile);
1180 92 }
1181
1182
1183 /**
1184 * Lists all path names from the cache db.
1185 */
1186 529 vector<string> PosixQuotaManager::List() { return DoList(kList); }
1187
1188
1189 /**
1190 * Lists all pinned files from the cache db.
1191 */
1192 184 vector<string> PosixQuotaManager::ListPinned() { return DoList(kListPinned); }
1193
1194
1195 /**
1196 * Lists all sqlite catalog files from the cache db.
1197 */
1198 69 vector<string> PosixQuotaManager::ListCatalogs() {
1199 69 return DoList(kListCatalogs);
1200 }
1201
1202
1203 /**
1204 * Lists only files flagged as volatile (priority removal)
1205 */
1206 69 vector<string> PosixQuotaManager::ListVolatile() {
1207 69 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 assert(ClearPermittedCapabilities(nocaps, nocaps));
1250 // Leave this process ptraceable
1251 assert(platform_set_dumpable());
1252 // but without core dumps
1253 assert(SetLimitCore(0));
1254 }
1255
1256 const std::unique_ptr<Watchdog> watchdog(
1257 Watchdog::Create(NULL, false /* needs_read_environ */));
1258 assert(watchdog.get() != nullptr);
1259 watchdog->Spawn("./stacktrace.cachemgr");
1260
1261 // Initialize pipe, open non-blocking as cvmfs is not yet connected
1262 const int fd_lockfile_fifo = LockFile(shared_manager.workspace_dir_
1263 + "/lock_cachemgr.fifo");
1264 if (fd_lockfile_fifo < 0) {
1265 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1266 "could not open lock file "
1267 "%s (%d)",
1268 (shared_manager.workspace_dir_ + "/lock_cachemgr.fifo").c_str(),
1269 errno);
1270 return 1;
1271 }
1272 const string crash_guard = shared_manager.cache_dir_ + "/cachemgr.running";
1273 const bool rebuild = FileExists(crash_guard);
1274 retval = open(crash_guard.c_str(), O_RDONLY | O_CREAT, 0600);
1275 if (retval < 0) {
1276 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1277 "failed to create shared cache manager crash guard");
1278 UnlockFile(fd_lockfile_fifo);
1279 return 1;
1280 }
1281 close(retval);
1282
1283 // Redirect SQlite temp directory to cache (global variable)
1284 const string tmp_dir = shared_manager.workspace_dir_;
1285 sqlite3_temp_directory = static_cast<char *>(
1286 sqlite3_malloc(tmp_dir.length() + 1));
1287 snprintf(sqlite3_temp_directory, tmp_dir.length() + 1, "%s", tmp_dir.c_str());
1288
1289 // Cleanup leftover named pipes
1290 shared_manager.CleanupPipes();
1291
1292 if (!shared_manager.InitDatabase(rebuild)) {
1293 UnlockFile(fd_lockfile_fifo);
1294 return 1;
1295 }
1296 shared_manager.CheckFreeSpace();
1297
1298 // Save protocol revision to file. If the file is not found, it indicates
1299 // to the client that the cache manager is from times before the protocol
1300 // was versioned.
1301 const string protocol_revision_path = shared_manager.workspace_dir_
1302 + "/cachemgr.protocol";
1303 retval = open(protocol_revision_path.c_str(), O_WRONLY | O_CREAT, 0600);
1304 if (retval < 0) {
1305 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1306 "failed to open protocol revision file (%d)", errno);
1307 UnlockFile(fd_lockfile_fifo);
1308 return 1;
1309 }
1310 const string revision = StringifyInt(kProtocolRevision);
1311 const int written = write(retval, revision.data(), revision.length());
1312 close(retval);
1313 if ((written < 0) || static_cast<unsigned>(written) != revision.length()) {
1314 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1315 "failed to write protocol revision (%d)", errno);
1316 UnlockFile(fd_lockfile_fifo);
1317 return 1;
1318 }
1319
1320 const string fifo_path = shared_manager.workspace_dir_ + "/cachemgr";
1321 shared_manager.pipe_lru_[0] = open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK);
1322 if (shared_manager.pipe_lru_[0] < 0) {
1323 LogCvmfs(kLogQuota, kLogDebug, "failed to listen on FIFO %s (%d)",
1324 fifo_path.c_str(), errno);
1325 UnlockFile(fd_lockfile_fifo);
1326 return 1;
1327 }
1328 Nonblock2Block(shared_manager.pipe_lru_[0]);
1329 LogCvmfs(kLogQuota, kLogDebug, "shared cache manager listening");
1330
1331 char buf = 'C';
1332 WritePipe(pipe_boot, &buf, 1);
1333 close(pipe_boot);
1334
1335 ReadPipe(pipe_handshake, &buf, 1);
1336 close(pipe_handshake);
1337 LogCvmfs(kLogQuota, kLogDebug, "shared cache manager handshake done");
1338
1339 // Ensure that broken pipes from clients do not kill the cache manager
1340 signal(SIGPIPE, SIG_IGN);
1341 // Don't let Ctrl-C ungracefully kill interactive session
1342 signal(SIGINT, SIG_IGN);
1343
1344 shared_manager.MainCommandServer(&shared_manager);
1345 unlink(fifo_path.c_str());
1346 unlink(protocol_revision_path.c_str());
1347 shared_manager.CloseDatabase();
1348 unlink(crash_guard.c_str());
1349 UnlockFile(fd_lockfile_fifo);
1350
1351 if (sqlite3_temp_directory) {
1352 sqlite3_free(sqlite3_temp_directory);
1353 sqlite3_temp_directory = NULL;
1354 }
1355
1356 return 0;
1357 }
1358
1359
1360 782 void *PosixQuotaManager::MainCommandServer(void *data) {
1361 782 PosixQuotaManager *quota_mgr = static_cast<PosixQuotaManager *>(data);
1362
1363
1/2
✓ Branch 1 taken 782 times.
✗ Branch 2 not taken.
782 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1364
1/2
✓ Branch 1 taken 782 times.
✗ Branch 2 not taken.
782 sqlite3_soft_heap_limit(quota_mgr->kSqliteMemPerThread);
1365
1366
2/2
✓ Branch 1 taken 25024 times.
✓ Branch 2 taken 782 times.
25806 LruCommand command_buffer[kCommandBufferSize];
1367 char description_buffer[kCommandBufferSize * kMaxDescription];
1368 782 unsigned num_commands = 0;
1369
1370
1/2
✓ Branch 1 taken 3453910 times.
✗ Branch 2 not taken.
3453910 while (read(quota_mgr->pipe_lru_[0], &command_buffer[num_commands],
1371 sizeof(command_buffer[0]))
1372
2/2
✓ Branch 0 taken 3453128 times.
✓ Branch 1 taken 782 times.
3453910 == sizeof(command_buffer[0])) {
1373 3453128 const CommandType command_type = command_buffer[num_commands].command_type;
1374
1/2
✓ Branch 1 taken 3453128 times.
✗ Branch 2 not taken.
3453128 LogCvmfs(kLogQuota, kLogDebug, "received command %d", command_type);
1375 3453128 const uint64_t size = command_buffer[num_commands].GetSize();
1376
1377 // Inserts and pins come with a description (usually a path)
1378
4/4
✓ Branch 0 taken 1152691 times.
✓ Branch 1 taken 2300437 times.
✓ Branch 2 taken 1152599 times.
✓ Branch 3 taken 92 times.
3453128 if ((command_type == kInsert) || (command_type == kInsertVolatile)
1379
4/4
✓ Branch 0 taken 1152553 times.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 1152300 times.
✓ Branch 3 taken 253 times.
1152599 || (command_type == kPin) || (command_type == kPinRegular)
1380
2/2
✓ Branch 0 taken 1152277 times.
✓ Branch 1 taken 23 times.
1152300 || (command_type == kRegisterMountpoint)
1381
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 1152254 times.
1152277 || (command_type == kSetCleanupPolicy)) {
1382 2300874 const int desc_length = command_buffer[num_commands].desc_length;
1383 2300874 ReadPipe(quota_mgr->pipe_lru_[0],
1384
1/2
✓ Branch 1 taken 2300874 times.
✗ Branch 2 not taken.
2300874 &description_buffer[kMaxDescription * num_commands],
1385 desc_length);
1386 }
1387
1388 // The protocol revision is returned immediately
1389
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 3453105 times.
3453128 if (command_type == kGetProtocolRevision) {
1390
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const int return_pipe = quota_mgr->BindReturnPipe(
1391 command_buffer[num_commands].return_pipe);
1392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (return_pipe < 0)
1393 continue;
1394
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 WritePipe(return_pipe, &quota_mgr->kProtocolRevision,
1395 sizeof(quota_mgr->kProtocolRevision));
1396
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 quota_mgr->UnbindReturnPipe(return_pipe);
1397 23 continue;
1398 23 }
1399
1400 // Register a new mountpoint
1401
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 3453082 times.
3453105 if (command_type == kRegisterMountpoint) {
1402 const std::string mountpoint(
1403 23 &description_buffer[num_commands * kMaxDescription],
1404
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 command_buffer[num_commands].desc_length);
1405
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 quota_mgr->mountpoints_.push_back(mountpoint);
1406
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1407 "Mountpoint %s registered in the group", mountpoint.c_str());
1408 23 continue;
1409 23 }
1410
1411 // Set Cleanup Policy
1412
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 3453059 times.
3453082 if (command_type == kSetCleanupPolicy) {
1413 23 quota_mgr->cleanup_unused_first_ = (description_buffer[num_commands
1414 23 * kMaxDescription]
1415 == 'S')
1416 23 ? true
1417 : false;
1418 23 continue;
1419 }
1420 // Mountpoints are returned immediately
1421
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3453059 times.
3453059 if (command_type == kGetMountpoints) {
1422 const int return_pipe = quota_mgr->BindReturnPipe(
1423 command_buffer[num_commands].return_pipe);
1424 if (return_pipe < 0)
1425 continue;
1426
1427 std::string mps;
1428 for (auto it = quota_mgr->mountpoints_.begin();
1429 it != quota_mgr->mountpoints_.end();
1430 ++it) {
1431 mps += *it + "\n";
1432 }
1433 size_t mp_size = mps.size();
1434 WritePipe(return_pipe, &mp_size, sizeof(size_t));
1435 WritePipe(return_pipe, mps.c_str(), mp_size);
1436 quota_mgr->UnbindReturnPipe(return_pipe);
1437 continue;
1438 }
1439
1440 // Group hashes are returned immediately
1441
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3453059 times.
3453059 if (command_type == kGetGroupHashes) {
1442 const int return_pipe = quota_mgr->BindReturnPipe(
1443 command_buffer[num_commands].return_pipe);
1444 if (return_pipe < 0)
1445 continue;
1446
1447 std::vector<shash::Short> gh = quota_mgr->CollectAllOpenHashes();
1448 std::string result;
1449 for (auto it = gh.begin(); it != gh.end(); ++it) {
1450 result += (*it).ToString() + "\n";
1451 }
1452 size_t result_size = result.size();
1453 WritePipe(return_pipe, &result_size, sizeof(size_t));
1454 WritePipe(return_pipe, result.c_str(), result_size);
1455 quota_mgr->UnbindReturnPipe(return_pipe);
1456 continue;
1457 }
1458
1459 // The cleanup rate is returned immediately
1460
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 3452967 times.
3453059 if (command_type == kCleanupRate) {
1461
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 const int return_pipe = quota_mgr->BindReturnPipe(
1462 command_buffer[num_commands].return_pipe);
1463
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if (return_pipe < 0)
1464 continue;
1465 const uint64_t
1466 92 period_s = size; // use the size field to transmit the period
1467
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 uint64_t rate = quota_mgr->cleanup_recorder_.GetNoTicks(period_s);
1468
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 WritePipe(return_pipe, &rate, sizeof(rate));
1469
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 quota_mgr->UnbindReturnPipe(return_pipe);
1470 92 continue;
1471 92 }
1472
1473
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 3452944 times.
3452967 if (command_type == kSetLimit) {
1474
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const int return_pipe = quota_mgr->BindReturnPipe(
1475 command_buffer[num_commands].return_pipe);
1476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (return_pipe < 0)
1477 continue;
1478 23 quota_mgr->limit_ = size; // use the size field to transmit the size
1479 23 quota_mgr->cleanup_threshold_ = size / 2;
1480
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1481 "Quota limit set to %lu / threshold %lu", quota_mgr->limit_,
1482 quota_mgr->cleanup_threshold_);
1483 23 bool ret = true;
1484
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 WritePipe(return_pipe, &ret, sizeof(ret));
1485
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 quota_mgr->UnbindReturnPipe(return_pipe);
1486 23 continue;
1487 23 }
1488
1489 // Reservations are handled immediately and "out of band"
1490
2/2
✓ Branch 0 taken 322 times.
✓ Branch 1 taken 3452622 times.
3452944 if (command_type == kReserve) {
1491 322 bool success = true;
1492
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 const int return_pipe = quota_mgr->BindReturnPipe(
1493 command_buffer[num_commands].return_pipe);
1494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 322 times.
322 if (return_pipe < 0)
1495 continue;
1496
1497
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1498
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 const string hash_str(hash.ToString());
1499
1/2
✓ Branch 2 taken 322 times.
✗ Branch 3 not taken.
322 LogCvmfs(kLogQuota, kLogDebug, "reserve %lu bytes for %s", size,
1500 hash_str.c_str());
1501
1502
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 if (quota_mgr->pinned_chunks_.find(hash)
1503
2/2
✓ Branch 2 taken 276 times.
✓ Branch 3 taken 46 times.
644 == quota_mgr->pinned_chunks_.end()) {
1504
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 253 times.
276 if ((quota_mgr->pinned_ + size) > quota_mgr->cleanup_threshold_) {
1505
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 LogCvmfs(kLogQuota, kLogDebug,
1506 "failed to insert %s (pinned), no space", hash_str.c_str());
1507 23 success = false;
1508 } else {
1509
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 quota_mgr->pinned_chunks_[hash] = size;
1510 253 quota_mgr->pinned_ += size;
1511
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 quota_mgr->CheckHighPinWatermark();
1512 }
1513 }
1514
1515
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 WritePipe(return_pipe, &success, sizeof(success));
1516
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 quota_mgr->UnbindReturnPipe(return_pipe);
1517 322 continue;
1518 322 }
1519
1520 // Back channels are also handled out of band
1521
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 3452530 times.
3452622 if (command_type == kRegisterBackChannel) {
1522
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 const int return_pipe = quota_mgr->BindReturnPipe(
1523 command_buffer[num_commands].return_pipe);
1524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if (return_pipe < 0)
1525 continue;
1526
1527
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 quota_mgr->UnlinkReturnPipe(command_buffer[num_commands].return_pipe);
1528
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 Block2Nonblock(return_pipe); // back channels are opportunistic
1529
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 shash::Md5 hash;
1530 92 memcpy(hash.digest, command_buffer[num_commands].digest,
1531 92 shash::kDigestSizes[shash::kMd5]);
1532
1533 92 quota_mgr->LockBackChannels();
1534 const map<shash::Md5, int>::const_iterator
1535
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 iter = quota_mgr->back_channels_.find(hash);
1536
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 92 times.
92 if (iter != quota_mgr->back_channels_.end()) {
1537 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1538 "closing left-over back channel %s", hash.ToString().c_str());
1539 close(iter->second);
1540 }
1541
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 quota_mgr->back_channels_[hash] = return_pipe;
1542 92 quota_mgr->UnlockBackChannels();
1543
1544 92 char success = 'S';
1545
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 WritePipe(return_pipe, &success, sizeof(success));
1546
1/2
✓ Branch 2 taken 92 times.
✗ Branch 3 not taken.
92 LogCvmfs(kLogQuota, kLogDebug, "register back channel %s on fd %d",
1547
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
184 hash.ToString().c_str(), return_pipe);
1548
1549 92 continue;
1550 92 }
1551
1552
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 3452484 times.
3452530 if (command_type == kUnregisterBackChannel) {
1553
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 shash::Md5 hash;
1554 46 memcpy(hash.digest, command_buffer[num_commands].digest,
1555 46 shash::kDigestSizes[shash::kMd5]);
1556
1557 46 quota_mgr->LockBackChannels();
1558 const map<shash::Md5, int>::iterator iter = quota_mgr->back_channels_
1559
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 .find(hash);
1560
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 if (iter != quota_mgr->back_channels_.end()) {
1561
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 LogCvmfs(kLogQuota, kLogDebug, "closing back channel %s",
1562
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
92 hash.ToString().c_str());
1563
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 close(iter->second);
1564
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 quota_mgr->back_channels_.erase(iter);
1565 } else {
1566 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1567 "did not find back channel %s", hash.ToString().c_str());
1568 }
1569 46 quota_mgr->UnlockBackChannels();
1570
1571 46 continue;
1572 46 }
1573
1574 // Unpinnings are also handled immediately with respect to the pinned gauge
1575
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 3452438 times.
3452484 if (command_type == kUnpin) {
1576
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1577
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 const string hash_str(hash.ToString());
1578
1579 const map<shash::Any, uint64_t>::iterator iter = quota_mgr->pinned_chunks_
1580
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 .find(hash);
1581
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 if (iter != quota_mgr->pinned_chunks_.end()) {
1582 46 quota_mgr->pinned_ -= iter->second;
1583
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 quota_mgr->pinned_chunks_.erase(iter);
1584 // It can happen that files get pinned that were removed from the cache
1585 // (see cache.cc). We fix this at this point, where we remove such
1586 // entries from the cache database.
1587
2/4
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
92 if (!FileExists(quota_mgr->cache_dir_ + "/"
1588
4/6
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 23 times.
✓ Branch 10 taken 23 times.
138 + hash.MakePathWithoutSuffix())) {
1589
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 LogCvmfs(kLogQuota, kLogDebug,
1590 "remove orphaned pinned hash %s from cache database",
1591 hash_str.c_str());
1592
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1593 23 hash_str.length(), SQLITE_STATIC);
1594 int retval;
1595
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
23 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1596
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1597 23 0);
1598
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1599 23 hash_str.length(), SQLITE_STATIC);
1600
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 retval = sqlite3_step(quota_mgr->stmt_rm_);
1601
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
23 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1602 23 quota_mgr->gauge_ -= size;
1603 } else {
1604 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1605 "failed to delete %s (%d)", hash_str.c_str(), retval);
1606 }
1607
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_reset(quota_mgr->stmt_rm_);
1608 }
1609
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_reset(quota_mgr->stmt_size_);
1610 }
1611 } else {
1612 LogCvmfs(kLogQuota, kLogDebug, "this chunk was not pinned");
1613 }
1614 46 }
1615
1616 // Immediate commands trigger flushing of the buffer
1617 3452484 const bool immediate_command = (command_type == kCleanup)
1618
2/2
✓ Branch 0 taken 3451748 times.
✓ Branch 1 taken 529 times.
3452277 || (command_type == kList)
1619
2/2
✓ Branch 0 taken 3451564 times.
✓ Branch 1 taken 184 times.
3451748 || (command_type == kListPinned)
1620
2/2
✓ Branch 0 taken 3451495 times.
✓ Branch 1 taken 69 times.
3451564 || (command_type == kListCatalogs)
1621
2/2
✓ Branch 0 taken 3451426 times.
✓ Branch 1 taken 69 times.
3451495 || (command_type == kListVolatile)
1622
2/2
✓ Branch 0 taken 3451357 times.
✓ Branch 1 taken 69 times.
3451426 || (command_type == kRemove)
1623
2/2
✓ Branch 0 taken 3450943 times.
✓ Branch 1 taken 414 times.
3451357 || (command_type == kStatus)
1624
1/2
✓ Branch 0 taken 3450943 times.
✗ Branch 1 not taken.
3450943 || (command_type == kLimits)
1625
3/4
✓ Branch 0 taken 3452277 times.
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3450943 times.
6904761 || (command_type == kPid);
1626
2/2
✓ Branch 0 taken 3450943 times.
✓ Branch 1 taken 1541 times.
3452484 if (!immediate_command)
1627 3450943 num_commands++;
1628
1629
4/4
✓ Branch 0 taken 3344683 times.
✓ Branch 1 taken 107801 times.
✓ Branch 2 taken 1541 times.
✓ Branch 3 taken 3343142 times.
3452484 if ((num_commands == kCommandBufferSize) || immediate_command) {
1630
1/2
✓ Branch 1 taken 109342 times.
✗ Branch 2 not taken.
109342 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1631 description_buffer);
1632
2/2
✓ Branch 0 taken 107801 times.
✓ Branch 1 taken 1541 times.
109342 if (!immediate_command)
1633 107801 num_commands = 0;
1634 }
1635
1636
2/2
✓ Branch 0 taken 1541 times.
✓ Branch 1 taken 3450943 times.
3452484 if (immediate_command) {
1637 // Process cleanup, listings
1638
1/2
✓ Branch 1 taken 1541 times.
✗ Branch 2 not taken.
1541 const int return_pipe = quota_mgr->BindReturnPipe(
1639 command_buffer[num_commands].return_pipe);
1640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1541 times.
1541 if (return_pipe < 0) {
1641 num_commands = 0;
1642 continue;
1643 }
1644
1645 int retval;
1646 1541 sqlite3_stmt *this_stmt_list = NULL;
1647
7/10
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 207 times.
✓ Branch 2 taken 529 times.
✓ Branch 3 taken 184 times.
✓ Branch 4 taken 69 times.
✓ Branch 5 taken 69 times.
✓ Branch 6 taken 414 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1541 switch (command_type) {
1648 69 case kRemove: {
1649
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1650
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 const string hash_str = hash.ToString();
1651
1/2
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
69 LogCvmfs(kLogQuota, kLogDebug, "manually removing %s",
1652 hash_str.c_str());
1653 69 bool success = false;
1654
1655
1/2
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
69 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1656 69 hash_str.length(), SQLITE_STATIC);
1657 int retval;
1658
3/4
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 46 times.
✓ Branch 4 taken 23 times.
69 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1659
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1660 46 0);
1661
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 const uint64_t is_pinned = sqlite3_column_int64(
1662 46 quota_mgr->stmt_size_, 1);
1663
1664
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1665 46 hash_str.length(), SQLITE_STATIC);
1666
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 retval = sqlite3_step(quota_mgr->stmt_rm_);
1667
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
46 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1668 46 success = true;
1669 46 quota_mgr->gauge_ -= size;
1670
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if (is_pinned) {
1671
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 quota_mgr->pinned_chunks_.erase(hash);
1672 23 quota_mgr->pinned_ -= size;
1673 }
1674 } else {
1675 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1676 "failed to delete %s (%d)", hash_str.c_str(), retval);
1677 }
1678
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 sqlite3_reset(quota_mgr->stmt_rm_);
1679 } else {
1680 // File does not exist
1681 23 success = true;
1682 }
1683
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 sqlite3_reset(quota_mgr->stmt_size_);
1684
1685
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 WritePipe(return_pipe, &success, sizeof(success));
1686 69 break;
1687 69 }
1688 207 case kCleanup:
1689
1/2
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
207 retval = quota_mgr->DoCleanup(size);
1690
1/2
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
207 WritePipe(return_pipe, &retval, sizeof(retval));
1691 207 break;
1692 529 case kList:
1693
1/2
✓ Branch 0 taken 529 times.
✗ Branch 1 not taken.
529 if (!this_stmt_list)
1694 529 this_stmt_list = quota_mgr->stmt_list_;
1695 case kListPinned:
1696
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 529 times.
713 if (!this_stmt_list)
1697 184 this_stmt_list = quota_mgr->stmt_list_pinned_;
1698 case kListCatalogs:
1699
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 713 times.
782 if (!this_stmt_list)
1700 69 this_stmt_list = quota_mgr->stmt_list_catalogs_;
1701 case kListVolatile:
1702
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 782 times.
851 if (!this_stmt_list)
1703 69 this_stmt_list = quota_mgr->stmt_list_volatile_;
1704
1705 // Pipe back the list, one by one
1706 int length;
1707
3/4
✓ Branch 1 taken 2301794 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2300943 times.
✓ Branch 4 taken 851 times.
2301794 while (sqlite3_step(this_stmt_list) == SQLITE_ROW) {
1708
1/2
✓ Branch 2 taken 2300943 times.
✗ Branch 3 not taken.
2300943 string path = "(NULL)";
1709
2/4
✓ Branch 1 taken 2300943 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2300943 times.
✗ Branch 4 not taken.
2300943 if (sqlite3_column_type(this_stmt_list, 0) != SQLITE_NULL) {
1710 4601886 path = string(reinterpret_cast<const char *>(
1711
2/4
✓ Branch 1 taken 2300943 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2300943 times.
✗ Branch 5 not taken.
2300943 sqlite3_column_text(this_stmt_list, 0)));
1712 }
1713 2300943 length = path.length();
1714
1/2
✓ Branch 1 taken 2300943 times.
✗ Branch 2 not taken.
2300943 WritePipe(return_pipe, &length, sizeof(length));
1715
1/2
✓ Branch 0 taken 2300943 times.
✗ Branch 1 not taken.
2300943 if (length > 0)
1716
2/4
✓ Branch 1 taken 2300943 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2300943 times.
✗ Branch 5 not taken.
2300943 WritePipe(return_pipe, &path[0], length);
1717 2300943 }
1718 851 length = -1;
1719
1/2
✓ Branch 1 taken 851 times.
✗ Branch 2 not taken.
851 WritePipe(return_pipe, &length, sizeof(length));
1720
1/2
✓ Branch 1 taken 851 times.
✗ Branch 2 not taken.
851 sqlite3_reset(this_stmt_list);
1721 851 break;
1722 414 case kStatus:
1723
1/2
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
414 WritePipe(return_pipe, &quota_mgr->gauge_, sizeof(quota_mgr->gauge_));
1724
1/2
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
414 WritePipe(return_pipe, &quota_mgr->pinned_,
1725 sizeof(quota_mgr->pinned_));
1726 414 break;
1727 case kLimits:
1728 WritePipe(return_pipe, &quota_mgr->limit_, sizeof(quota_mgr->limit_));
1729 WritePipe(return_pipe, &quota_mgr->cleanup_threshold_,
1730 sizeof(quota_mgr->cleanup_threshold_));
1731 break;
1732 case kPid: {
1733 pid_t pid = getpid();
1734 WritePipe(return_pipe, &pid, sizeof(pid));
1735 break;
1736 }
1737 default:
1738 PANIC(NULL); // other types are handled by the bunch processor
1739 }
1740
1/2
✓ Branch 1 taken 1541 times.
✗ Branch 2 not taken.
1541 quota_mgr->UnbindReturnPipe(return_pipe);
1741 1541 num_commands = 0;
1742 }
1743 }
1744
1745
1/2
✓ Branch 1 taken 782 times.
✗ Branch 2 not taken.
782 LogCvmfs(kLogQuota, kLogDebug, "stopping cache manager (%d)", errno);
1746
1/2
✓ Branch 1 taken 782 times.
✗ Branch 2 not taken.
782 close(quota_mgr->pipe_lru_[0]);
1747
1/2
✓ Branch 1 taken 782 times.
✗ Branch 2 not taken.
782 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1748 description_buffer);
1749
1750 // Unpin
1751 782 command_buffer[0].command_type = kTouch;
1752 782 for (map<shash::Any, uint64_t>::const_iterator
1753 782 i = quota_mgr->pinned_chunks_.begin(),
1754 782 iEnd = quota_mgr->pinned_chunks_.end();
1755
2/2
✓ Branch 1 taken 230 times.
✓ Branch 2 taken 782 times.
1012 i != iEnd;
1756 230 ++i) {
1757
1/2
✓ Branch 2 taken 230 times.
✗ Branch 3 not taken.
230 command_buffer[0].StoreHash(i->first);
1758
1/2
✓ Branch 1 taken 230 times.
✗ Branch 2 not taken.
230 quota_mgr->ProcessCommandBunch(1, command_buffer, description_buffer);
1759 }
1760
1761 782 return NULL;
1762 }
1763
1764
1765 2162 void PosixQuotaManager::MakeReturnPipe(int pipe[2]) {
1766
2/2
✓ Branch 0 taken 2093 times.
✓ Branch 1 taken 69 times.
2162 if (!shared_) {
1767 2093 MakePipe(pipe);
1768 2093 return;
1769 }
1770
1771 // Create FIFO in cache directory, store path name (number) in pipe write end
1772 69 int i = 0;
1773 int retval;
1774 do {
1775
2/4
✓ Branch 2 taken 92 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 92 times.
✗ Branch 6 not taken.
92 retval = mkfifo((workspace_dir_ + "/pipe" + StringifyInt(i)).c_str(), 0600);
1776 92 pipe[1] = i;
1777 92 i++;
1778
3/4
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 69 times.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
92 } while ((retval == -1) && (errno == EEXIST));
1779
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 assert(retval == 0);
1780
1781 // Connect reader's end
1782
3/6
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 69 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 69 times.
✗ Branch 10 not taken.
69 pipe[0] = open((workspace_dir_ + "/pipe" + StringifyInt(pipe[1])).c_str(),
1783 O_RDONLY | O_NONBLOCK);
1784
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 assert(pipe[0] >= 0);
1785 69 Nonblock2Block(pipe[0]);
1786 }
1787
1788
1789 2424 void PosixQuotaManager::ParseDirectories(const std::string cache_workspace,
1790 std::string *cache_dir,
1791 std::string *workspace_dir) {
1792
1/2
✓ Branch 1 taken 2424 times.
✗ Branch 2 not taken.
2424 vector<string> dir_tokens(SplitString(cache_workspace, ':'));
1793
2/3
✓ Branch 1 taken 2378 times.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
2424 switch (dir_tokens.size()) {
1794 2378 case 1:
1795
2/4
✓ Branch 2 taken 2378 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2378 times.
✗ Branch 6 not taken.
2378 *cache_dir = *workspace_dir = dir_tokens[0];
1796 2378 break;
1797 46 case 2:
1798
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 *cache_dir = dir_tokens[0];
1799
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 *workspace_dir = dir_tokens[1];
1800 46 break;
1801 default:
1802 PANIC(NULL);
1803 }
1804 2424 }
1805
1806 23 void PosixQuotaManager::SkipEviction(const EvictCandidate &candidate) {
1807 23 bool res = true;
1808
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 std::string hash_str = candidate.hash.ToString();
1809
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 LogCvmfs(kLogQuota, kLogDebug, "Exclude %s from eviction", hash_str.c_str());
1810
2/4
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
23 sqlite3_bind_text(stmt_block_, 1, &hash_str[0], hash_str.length(),
1811 SQLITE_STATIC);
1812
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 res = (sqlite3_step(stmt_block_) == SQLITE_DONE);
1813
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_reset(stmt_block_);
1814
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 assert(res);
1815 23 }
1816
1817 /**
1818 * Immediately inserts a new pinned catalog. Does cache cleanup if necessary.
1819 *
1820 * \return True on success, false otherwise
1821 */
1822 1003 bool PosixQuotaManager::Pin(const shash::Any &hash,
1823 const uint64_t size,
1824 const string &description,
1825 const bool is_catalog) {
1826
3/4
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 957 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 46 times.
1003 assert((size > 0) || !is_catalog);
1827
1828
1/2
✓ Branch 1 taken 1003 times.
✗ Branch 2 not taken.
1003 const string hash_str = hash.ToString();
1829
1/2
✓ Branch 3 taken 1003 times.
✗ Branch 4 not taken.
1003 LogCvmfs(kLogQuota, kLogDebug, "pin into lru %s, path %s", hash_str.c_str(),
1830 description.c_str());
1831
1832 // Has to run when not yet spawned (cvmfs initialization)
1833
2/2
✓ Branch 0 taken 681 times.
✓ Branch 1 taken 322 times.
1003 if (!spawned_) {
1834 // Code duplication here
1835
3/4
✓ Branch 2 taken 681 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 399 times.
✓ Branch 6 taken 282 times.
681 if (pinned_chunks_.find(hash) == pinned_chunks_.end()) {
1836
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 353 times.
399 if (pinned_ + size > cleanup_threshold_) {
1837
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 LogCvmfs(kLogQuota, kLogDebug, "failed to insert %s (pinned), no space",
1838 hash_str.c_str());
1839 46 return false;
1840 } else {
1841
1/2
✓ Branch 1 taken 353 times.
✗ Branch 2 not taken.
353 pinned_chunks_[hash] = size;
1842 353 pinned_ += size;
1843
1/2
✓ Branch 1 taken 353 times.
✗ Branch 2 not taken.
353 CheckHighPinWatermark();
1844 }
1845 }
1846
1/2
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
635 const bool exists = Contains(hash_str);
1847
4/4
✓ Branch 0 taken 353 times.
✓ Branch 1 taken 282 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 330 times.
635 if (!exists && (gauge_ + size > limit_)) {
1848
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %lu",
1849 gauge_, size);
1850
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const int retval = DoCleanup(cleanup_threshold_);
1851
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 assert(retval != 0);
1852 }
1853
1/2
✓ Branch 3 taken 635 times.
✗ Branch 4 not taken.
635 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1854 SQLITE_STATIC);
1855
1/2
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
635 sqlite3_bind_int64(stmt_new_, 2, size);
1856
1/2
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
635 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1857
1/2
✓ Branch 3 taken 635 times.
✗ Branch 4 not taken.
635 sqlite3_bind_text(stmt_new_, 4, &description[0], description.length(),
1858 SQLITE_STATIC);
1859
3/4
✓ Branch 0 taken 566 times.
✓ Branch 1 taken 69 times.
✓ Branch 3 taken 635 times.
✗ Branch 4 not taken.
635 sqlite3_bind_int64(stmt_new_, 5, is_catalog ? kFileCatalog : kFileRegular);
1860
1/2
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
635 sqlite3_bind_int64(stmt_new_, 6, 1);
1861
1/2
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
635 const int retval = sqlite3_step(stmt_new_);
1862
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
635 assert((retval == SQLITE_DONE) || (retval == SQLITE_OK));
1863
1/2
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
635 sqlite3_reset(stmt_new_);
1864
2/2
✓ Branch 0 taken 353 times.
✓ Branch 1 taken 282 times.
635 if (!exists)
1865 353 gauge_ += size;
1866 635 return true;
1867 }
1868
1869 int pipe_reserve[2];
1870
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 MakeReturnPipe(pipe_reserve);
1871
1872 322 LruCommand cmd;
1873 322 cmd.command_type = kReserve;
1874 322 cmd.SetSize(size);
1875
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 cmd.StoreHash(hash);
1876 322 cmd.return_pipe = pipe_reserve[1];
1877
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1878 bool result;
1879
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 ManagedReadHalfPipe(pipe_reserve[0], &result, sizeof(result));
1880
1/2
✓ Branch 1 taken 322 times.
✗ Branch 2 not taken.
322 CloseReturnPipe(pipe_reserve);
1881
1882
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 299 times.
322 if (!result)
1883 23 return false;
1884
3/4
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 253 times.
✓ Branch 3 taken 299 times.
✗ Branch 4 not taken.
299 DoInsert(hash, size, description, is_catalog ? kPin : kPinRegular);
1885
1886 299 return true;
1887 1003 }
1888
1889
1890 2378 PosixQuotaManager::PosixQuotaManager(const uint64_t limit,
1891 const uint64_t cleanup_threshold,
1892 2378 const string &cache_workspace)
1893 2378 : shared_(false)
1894 2378 , spawned_(false)
1895 2378 , limit_(limit)
1896 2378 , cleanup_threshold_(cleanup_threshold)
1897 2378 , gauge_(0)
1898 2378 , pinned_(0)
1899 2378 , seq_(0)
1900 2378 , cache_dir_() // initialized in body
1901 2378 , workspace_dir_() // initialized in body
1902 2378 , fd_lock_cachedb_(-1)
1903 2378 , async_delete_(true)
1904 2378 , cachemgr_pid_(0)
1905 2378 , database_(NULL)
1906 2378 , stmt_touch_(NULL)
1907 2378 , stmt_unpin_(NULL)
1908 2378 , stmt_block_(NULL)
1909 2378 , stmt_unblock_(NULL)
1910 2378 , stmt_new_(NULL)
1911 2378 , stmt_lru_(NULL)
1912 2378 , stmt_size_(NULL)
1913 2378 , stmt_rm_(NULL)
1914 2378 , stmt_rm_batch_(NULL)
1915 2378 , stmt_list_(NULL)
1916 2378 , stmt_list_pinned_(NULL)
1917 2378 , stmt_list_catalogs_(NULL)
1918 2378 , stmt_list_volatile_(NULL)
1919 2378 , initialized_(false)
1920 4756 , cleanup_unused_first_(false) {
1921
2/4
✓ Branch 1 taken 2378 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2378 times.
✗ Branch 5 not taken.
2378 ParseDirectories(cache_workspace, &cache_dir_, &workspace_dir_);
1922 2378 pipe_lru_[0] = pipe_lru_[1] = -1;
1923
1/2
✓ Branch 1 taken 2378 times.
✗ Branch 2 not taken.
2378 cleanup_recorder_.AddRecorder(1, 90); // last 1.5 min with second resolution
1924 // last 1.5 h with minute resolution
1925
1/2
✓ Branch 1 taken 2378 times.
✗ Branch 2 not taken.
2378 cleanup_recorder_.AddRecorder(60, 90 * 60);
1926 // last 18 hours with 20 min resolution
1927
1/2
✓ Branch 1 taken 2378 times.
✗ Branch 2 not taken.
2378 cleanup_recorder_.AddRecorder(20 * 60, 60 * 60 * 18);
1928 // last 4 days with hour resolution
1929
1/2
✓ Branch 1 taken 2378 times.
✗ Branch 2 not taken.
2378 cleanup_recorder_.AddRecorder(60 * 60, 60 * 60 * 24 * 4);
1930
1931 2378 lock_open_files_ = reinterpret_cast<pthread_mutex_t *>(
1932 2378 smalloc(sizeof(pthread_mutex_t)));
1933 2378 }
1934
1935
1936 9508 PosixQuotaManager::~PosixQuotaManager() {
1937 4754 free(lock_open_files_);
1938
1939
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 2308 times.
4754 if (!initialized_)
1940 138 return;
1941
1942
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2308 times.
4616 if (shared_) {
1943 // Most of cleanup is done elsewhen by shared cache manager
1944 close(pipe_lru_[1]);
1945 return;
1946 }
1947
1948
2/2
✓ Branch 0 taken 782 times.
✓ Branch 1 taken 1526 times.
4616 if (spawned_) {
1949 1564 char fin = 0;
1950 1564 WritePipe(pipe_lru_[1], &fin, 1);
1951 1564 close(pipe_lru_[1]);
1952 1564 pthread_join(thread_lru_, NULL);
1953 } else {
1954 3052 ClosePipe(pipe_lru_);
1955 }
1956
1957 4616 CloseDatabase();
1958
14/14
✓ Branch 1 taken 2308 times.
✓ Branch 2 taken 69 times.
✓ Branch 4 taken 2308 times.
✓ Branch 5 taken 69 times.
✓ Branch 7 taken 2308 times.
✓ Branch 8 taken 69 times.
✓ Branch 10 taken 2308 times.
✓ Branch 11 taken 69 times.
✓ Branch 13 taken 2308 times.
✓ Branch 14 taken 69 times.
✓ Branch 16 taken 2308 times.
✓ Branch 17 taken 69 times.
✓ Branch 19 taken 2308 times.
✓ Branch 20 taken 69 times.
10336 }
1959
1960
1961 110354 void PosixQuotaManager::ProcessCommandBunch(const unsigned num,
1962 const LruCommand *commands,
1963 const char *descriptions) {
1964 110354 int retval = sqlite3_exec(database_, "BEGIN", NULL, NULL, NULL);
1965
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 110354 times.
110354 assert(retval == SQLITE_OK);
1966
1967
2/2
✓ Branch 0 taken 3451173 times.
✓ Branch 1 taken 110354 times.
3561527 for (unsigned i = 0; i < num; ++i) {
1968
1/2
✓ Branch 1 taken 3451173 times.
✗ Branch 2 not taken.
3451173 const shash::Any hash = commands[i].RetrieveHash();
1969
1/2
✓ Branch 1 taken 3451173 times.
✗ Branch 2 not taken.
3451173 const string hash_str = hash.ToString();
1970 3451173 const unsigned size = commands[i].GetSize();
1971
1/2
✓ Branch 1 taken 3451173 times.
✗ Branch 2 not taken.
3451173 LogCvmfs(kLogQuota, kLogDebug, "processing %s (%d)", hash_str.c_str(),
1972 3451173 commands[i].command_type);
1973
1974 bool exists;
1975
3/4
✓ Branch 0 taken 1150299 times.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 2300828 times.
✗ Branch 3 not taken.
3451173 switch (commands[i].command_type) {
1976 1150299 case kTouch:
1977
1/2
✓ Branch 1 taken 1150299 times.
✗ Branch 2 not taken.
1150299 sqlite3_bind_int64(stmt_touch_, 1, seq_++);
1978
1/2
✓ Branch 3 taken 1150299 times.
✗ Branch 4 not taken.
1150299 sqlite3_bind_text(stmt_touch_, 2, &hash_str[0], hash_str.length(),
1979 SQLITE_STATIC);
1980
1/2
✓ Branch 1 taken 1150299 times.
✗ Branch 2 not taken.
1150299 retval = sqlite3_step(stmt_touch_);
1981
1/2
✓ Branch 1 taken 1150299 times.
✗ Branch 2 not taken.
1150299 LogCvmfs(kLogQuota, kLogDebug, "touching %s (%ld): %d",
1982 1150299 hash_str.c_str(), seq_ - 1, retval);
1983
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1150299 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1150299 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1984 PANIC(kLogSyslogErr, "failed to update %s in cachedb, error %d",
1985 hash_str.c_str(), retval);
1986 }
1987
1/2
✓ Branch 1 taken 1150299 times.
✗ Branch 2 not taken.
1150299 sqlite3_reset(stmt_touch_);
1988 1150299 break;
1989 46 case kUnpin:
1990
1/2
✓ Branch 3 taken 46 times.
✗ Branch 4 not taken.
46 sqlite3_bind_text(stmt_unpin_, 1, &hash_str[0], hash_str.length(),
1991 SQLITE_STATIC);
1992
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 retval = sqlite3_step(stmt_unpin_);
1993
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 LogCvmfs(kLogQuota, kLogDebug, "unpinning %s: %d", hash_str.c_str(),
1994 retval);
1995
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
46 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1996 PANIC(kLogSyslogErr, "failed to unpin %s in cachedb, error %d",
1997 hash_str.c_str(), retval);
1998 }
1999
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 sqlite3_reset(stmt_unpin_);
2000 46 break;
2001 2300828 case kPin:
2002 case kPinRegular:
2003 case kInsert:
2004 case kInsertVolatile:
2005 // It could already be in, check
2006
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 exists = Contains(hash_str);
2007
2008 // Cleanup, move to trash and unlink
2009
3/4
✓ Branch 0 taken 2300713 times.
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2300713 times.
2300828 if (!exists && (gauge_ + size > limit_)) {
2010 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %u",
2011 gauge_, size);
2012 retval = DoCleanup(cleanup_threshold_);
2013 assert(retval != 0);
2014 }
2015
2016 // Insert or replace
2017
1/2
✓ Branch 3 taken 2300828 times.
✗ Branch 4 not taken.
2300828 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
2018 SQLITE_STATIC);
2019
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 sqlite3_bind_int64(stmt_new_, 2, size);
2020
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 2300736 times.
2300828 if (commands[i].command_type == kInsertVolatile) {
2021
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 sqlite3_bind_int64(stmt_new_, 3, (seq_++) | kVolatileFlag);
2022 } else {
2023
1/2
✓ Branch 1 taken 2300736 times.
✗ Branch 2 not taken.
2300736 sqlite3_bind_int64(stmt_new_, 3, seq_++);
2024 }
2025 2300828 sqlite3_bind_text(stmt_new_, 4, &descriptions[i * kMaxDescription],
2026
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 commands[i].desc_length, SQLITE_STATIC);
2027
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 sqlite3_bind_int64(
2028 stmt_new_, 5,
2029
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 2300782 times.
2300828 (commands[i].command_type == kPin) ? kFileCatalog : kFileRegular);
2030
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 sqlite3_bind_int64(stmt_new_, 6,
2031
2/2
✓ Branch 0 taken 2300782 times.
✓ Branch 1 taken 46 times.
2300828 ((commands[i].command_type == kPin)
2032
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 2300529 times.
2300782 || (commands[i].command_type == kPinRegular))
2033 ? 1
2034 : 0);
2035
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 retval = sqlite3_step(stmt_new_);
2036
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 LogCvmfs(kLogQuota, kLogDebug, "insert or replace %s, method %d: %d",
2037 2300828 hash_str.c_str(), commands[i].command_type, retval);
2038
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2300828 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
2039 PANIC(kLogSyslogErr, "failed to insert %s in cachedb, error %d",
2040 hash_str.c_str(), retval);
2041 }
2042
1/2
✓ Branch 1 taken 2300828 times.
✗ Branch 2 not taken.
2300828 sqlite3_reset(stmt_new_);
2043
2044
2/2
✓ Branch 0 taken 2300713 times.
✓ Branch 1 taken 115 times.
2300828 if (!exists)
2045 2300713 gauge_ += size;
2046 2300828 break;
2047 default:
2048 // other types should have been taken care of by event loop
2049 PANIC(NULL);
2050 }
2051 3451173 }
2052
2053 110354 retval = sqlite3_exec(database_, "COMMIT", NULL, NULL, NULL);
2054
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 110354 times.
110354 if (retval != SQLITE_OK) {
2055 PANIC(kLogSyslogErr, "failed to commit to cachedb, error %d", retval);
2056 }
2057 110354 }
2058
2059
2060 2332 bool PosixQuotaManager::RebuildDatabase() {
2061 2332 bool result = false;
2062 2332 string sql;
2063 2332 sqlite3_stmt *stmt_select = NULL;
2064 2332 sqlite3_stmt *stmt_insert = NULL;
2065 int sqlerr;
2066 2332 int seq = 0;
2067 char hex[4];
2068 struct stat info;
2069 platform_dirent64 *d;
2070 2332 DIR *dirp = NULL;
2071 2332 string path;
2072
2073
1/2
✓ Branch 1 taken 2332 times.
✗ Branch 2 not taken.
2332 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug, "re-building cache database");
2074
2075 // Empty cache catalog and fscache
2076
1/2
✓ Branch 1 taken 2332 times.
✗ Branch 2 not taken.
2332 sql = "DELETE FROM cache_catalog; DELETE FROM fscache;";
2077
1/2
✓ Branch 2 taken 2332 times.
✗ Branch 3 not taken.
2332 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2332 times.
2332 if (sqlerr != SQLITE_OK) {
2079 LogCvmfs(kLogQuota, kLogDebug, "could not clear cache database");
2080 goto build_return;
2081 }
2082
2083 2332 gauge_ = 0;
2084
2085 // Insert files from cache sub-directories 00 - ff
2086 // TODO(jblomer): fs_traversal
2087
1/2
✓ Branch 1 taken 2332 times.
✗ Branch 2 not taken.
2332 sqlite3_prepare_v2(database_,
2088 "INSERT INTO fscache (sha1, size, actime) "
2089 "VALUES (:sha1, :s, :t);",
2090 -1, &stmt_insert, NULL);
2091
2092
2/2
✓ Branch 0 taken 579397 times.
✓ Branch 1 taken 2263 times.
581660 for (int i = 0; i <= 0xff; i++) {
2093 579397 snprintf(hex, sizeof(hex), "%02x", i);
2094
3/6
✓ Branch 2 taken 579397 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 579397 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 579397 times.
✗ Branch 9 not taken.
579397 path = cache_dir_ + "/" + string(hex);
2095
3/4
✓ Branch 2 taken 579397 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 69 times.
✓ Branch 5 taken 579328 times.
579397 if ((dirp = opendir(path.c_str())) == NULL) {
2096
1/2
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
69 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
2097 "failed to open directory %s (tmpwatch interfering?)",
2098 path.c_str());
2099 69 goto build_return;
2100 }
2101
3/4
✓ Branch 1 taken 1738030 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1158702 times.
✓ Branch 4 taken 579328 times.
1738030 while ((d = platform_readdir(dirp)) != NULL) {
2102
3/6
✓ Branch 2 taken 1158702 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1158702 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1158702 times.
✗ Branch 9 not taken.
2317404 const string file_path = path + "/" + string(d->d_name);
2103
1/2
✓ Branch 2 taken 1158702 times.
✗ Branch 3 not taken.
1158702 if (stat(file_path.c_str(), &info) == 0) {
2104
2/2
✓ Branch 0 taken 1158656 times.
✓ Branch 1 taken 46 times.
1158702 if (!S_ISREG(info.st_mode))
2105 1158679 continue;
2106
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
46 if (info.st_size == 0) {
2107
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
2108 "removing empty file %s during automatic cache db rebuild",
2109 file_path.c_str());
2110 23 unlink(file_path.c_str());
2111 23 continue;
2112 }
2113
2114
3/6
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 23 times.
✗ Branch 10 not taken.
46 string hash = string(hex) + string(d->d_name);
2115
1/2
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
23 sqlite3_bind_text(stmt_insert, 1, hash.data(), hash.length(),
2116 SQLITE_STATIC);
2117
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_bind_int64(stmt_insert, 2, info.st_size);
2118
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_bind_int64(stmt_insert, 3, info.st_atime);
2119
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
23 if (sqlite3_step(stmt_insert) != SQLITE_DONE) {
2120 LogCvmfs(kLogQuota, kLogDebug, "could not insert into temp table");
2121 goto build_return;
2122 }
2123
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_reset(stmt_insert);
2124
2125 23 gauge_ += info.st_size;
2126
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 } else {
2127 LogCvmfs(kLogQuota, kLogDebug, "could not stat %s", file_path.c_str());
2128 }
2129
2/3
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 1158679 times.
✗ Branch 3 not taken.
1158702 }
2130
1/2
✓ Branch 1 taken 579328 times.
✗ Branch 2 not taken.
579328 closedir(dirp);
2131 579328 dirp = NULL;
2132 }
2133
1/2
✓ Branch 1 taken 2263 times.
✗ Branch 2 not taken.
2263 sqlite3_finalize(stmt_insert);
2134 2263 stmt_insert = NULL;
2135
2136 // Transfer from temp table in cache catalog
2137
1/2
✓ Branch 1 taken 2263 times.
✗ Branch 2 not taken.
2263 sqlite3_prepare_v2(database_,
2138 "SELECT sha1, size FROM fscache ORDER BY actime;", -1,
2139 &stmt_select, NULL);
2140
1/2
✓ Branch 1 taken 2263 times.
✗ Branch 2 not taken.
2263 sqlite3_prepare_v2(
2141 database_,
2142 "INSERT INTO cache_catalog (sha1, size, acseq, path, type, pinned) "
2143 "VALUES (:sha1, :s, :seq, 'unknown (automatic rebuild)', :t, 0);",
2144 -1, &stmt_insert, NULL);
2145
3/4
✓ Branch 1 taken 2286 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✓ Branch 4 taken 2263 times.
2286 while (sqlite3_step(stmt_select) == SQLITE_ROW) {
2146 const string hash = string(
2147
2/4
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
23 reinterpret_cast<const char *>(sqlite3_column_text(stmt_select, 0)));
2148
1/2
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
23 sqlite3_bind_text(stmt_insert, 1, &hash[0], hash.length(), SQLITE_STATIC);
2149
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 sqlite3_bind_int64(stmt_insert, 2, sqlite3_column_int64(stmt_select, 1));
2150
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_bind_int64(stmt_insert, 3, seq++);
2151 // Might also be a catalog (information is lost)
2152
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_bind_int64(stmt_insert, 4, kFileRegular);
2153
2154
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 const int retval = sqlite3_step(stmt_insert);
2155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (retval != SQLITE_DONE) {
2156 // If the file system hosting the cache is full, we'll likely notice here
2157 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
2158 "could not insert into cache catalog (%d - %s)", retval,
2159 sqlite3_errstr(retval));
2160 goto build_return;
2161 }
2162
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 sqlite3_reset(stmt_insert);
2163
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 }
2164
2165 // Delete temporary table
2166
1/2
✓ Branch 1 taken 2263 times.
✗ Branch 2 not taken.
2263 sql = "DELETE FROM fscache;";
2167
1/2
✓ Branch 2 taken 2263 times.
✗ Branch 3 not taken.
2263 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2263 times.
2263 if (sqlerr != SQLITE_OK) {
2169 LogCvmfs(kLogQuota, kLogDebug, "could not clear temporary table (%d)",
2170 sqlerr);
2171 goto build_return;
2172 }
2173
2174 2263 seq_ = seq;
2175 2263 result = true;
2176
1/2
✓ Branch 1 taken 2263 times.
✗ Branch 2 not taken.
2263 LogCvmfs(kLogQuota, kLogDebug,
2177 "rebuilding finished, sequence %" PRIu64 ", gauge %" PRIu64, seq_,
2178 gauge_);
2179
2180 2332 build_return:
2181
1/2
✓ Branch 0 taken 2332 times.
✗ Branch 1 not taken.
2332 if (stmt_insert)
2182
1/2
✓ Branch 1 taken 2332 times.
✗ Branch 2 not taken.
2332 sqlite3_finalize(stmt_insert);
2183
2/2
✓ Branch 0 taken 2263 times.
✓ Branch 1 taken 69 times.
2332 if (stmt_select)
2184
1/2
✓ Branch 1 taken 2263 times.
✗ Branch 2 not taken.
2263 sqlite3_finalize(stmt_select);
2185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2332 times.
2332 if (dirp)
2186 closedir(dirp);
2187 2332 return result;
2188 2332 }
2189
2190
2191 /**
2192 * Register a channel that allows the cache manager to trigger action to its
2193 * clients. Currently used for releasing pinned catalogs.
2194 */
2195 92 void PosixQuotaManager::RegisterBackChannel(int back_channel[2],
2196 const string &channel_id) {
2197
1/2
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
92 if (protocol_revision_ >= 1) {
2198
1/2
✓ Branch 2 taken 92 times.
✗ Branch 3 not taken.
92 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2199
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 MakeReturnPipe(back_channel);
2200
2201 92 LruCommand cmd;
2202 92 cmd.command_type = kRegisterBackChannel;
2203 92 cmd.return_pipe = back_channel[1];
2204 // Not StoreHash(). This is an MD5 hash.
2205 92 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2206
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2207
2208 char success;
2209
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 ManagedReadHalfPipe(back_channel[0], &success, sizeof(success));
2210 // At this point, the named FIFO is unlinked, so don't use CloseReturnPipe
2211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if (success != 'S') {
2212 PANIC(kLogDebug | kLogSyslogErr,
2213 "failed to register quota back channel (%c)", success);
2214 }
2215 } else {
2216 // Dummy pipe to return valid file descriptors
2217 MakePipe(back_channel);
2218 }
2219 92 }
2220
2221
2222 /**
2223 * Removes a chunk from cache, if it exists.
2224 */
2225 69 void PosixQuotaManager::Remove(const shash::Any &hash) {
2226
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 const string hash_str = hash.ToString();
2227
2228 int pipe_remove[2];
2229
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 MakeReturnPipe(pipe_remove);
2230
2231 69 LruCommand cmd;
2232 69 cmd.command_type = kRemove;
2233 69 cmd.return_pipe = pipe_remove[1];
2234
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 cmd.StoreHash(hash);
2235
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2236
2237 bool success;
2238
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 ManagedReadHalfPipe(pipe_remove[0], &success, sizeof(success));
2239
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 CloseReturnPipe(pipe_remove);
2240
2241
3/6
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 69 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 69 times.
✗ Branch 8 not taken.
69 unlink((cache_dir_ + "/" + hash.MakePathWithoutSuffix()).c_str());
2242 69 }
2243
2244
2245 828 void PosixQuotaManager::Spawn() {
2246
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 782 times.
828 if (spawned_)
2247 46 return;
2248
2249 782 if (pthread_create(&thread_lru_, NULL, MainCommandServer,
2250 static_cast<void *>(this))
2251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 782 times.
782 != 0) {
2252 PANIC(kLogDebug, "could not create lru thread");
2253 }
2254
2255 782 spawned_ = true;
2256 }
2257
2258
2259 /**
2260 * Updates the sequence number of the file specified by the hash.
2261 */
2262 1150400 void PosixQuotaManager::Touch(const shash::Any &hash) {
2263 1150400 LruCommand cmd;
2264 1150400 cmd.command_type = kTouch;
2265
1/2
✓ Branch 1 taken 1150400 times.
✗ Branch 2 not taken.
1150400 cmd.StoreHash(hash);
2266
1/2
✓ Branch 1 taken 1150400 times.
✗ Branch 2 not taken.
1150400 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2267 1150400 }
2268
2269
2270 2024 void PosixQuotaManager::UnbindReturnPipe(int pipe_wronly) {
2271
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2001 times.
2024 if (shared_)
2272 23 close(pipe_wronly);
2273 2024 }
2274
2275
2276 161 void PosixQuotaManager::UnlinkReturnPipe(int pipe_wronly) {
2277
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 92 times.
161 if (shared_)
2278
2/4
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 69 times.
✗ Branch 6 not taken.
69 unlink((workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str());
2279 161 }
2280
2281
2282 451 void PosixQuotaManager::Unpin(const shash::Any &hash) {
2283
2/4
✓ Branch 1 taken 451 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 451 times.
✗ Branch 6 not taken.
451 LogCvmfs(kLogQuota, kLogDebug, "Unpin %s", hash.ToString().c_str());
2284
2285 451 LruCommand cmd;
2286 451 cmd.command_type = kUnpin;
2287
1/2
✓ Branch 1 taken 451 times.
✗ Branch 2 not taken.
451 cmd.StoreHash(hash);
2288
1/2
✓ Branch 1 taken 451 times.
✗ Branch 2 not taken.
451 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2289 451 }
2290
2291
2292 46 void PosixQuotaManager::UnregisterBackChannel(int back_channel[2],
2293 const string &channel_id) {
2294
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 if (protocol_revision_ >= 1) {
2295
1/2
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2296
2297 46 LruCommand cmd;
2298 46 cmd.command_type = kUnregisterBackChannel;
2299 // Not StoreHash(). This is an MD5 hash.
2300 46 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2301
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2302
2303 // Writer's end will be closed by cache manager, FIFO is already unlinked
2304
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 close(back_channel[0]);
2305 } else {
2306 ClosePipe(back_channel);
2307 }
2308 46 }
2309
2310 2303036 void PosixQuotaManager::ManagedReadHalfPipe(int fd, void *buf, size_t nbyte) {
2311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2303036 times.
2303036 const unsigned timeout_ms = cachemgr_pid_ ? 1000 : 0;
2312 2303036 bool result = false;
2313 do {
2314 2303036 result = ReadHalfPipe(fd, buf, nbyte, timeout_ms);
2315 // try only as long as the cachemgr is still alive
2316
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2303036 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2303036 times.
2303036 } while (!result && getpgid(cachemgr_pid_) >= 0);
2317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2303036 times.
2303036 if (!result) {
2318 PANIC(kLogStderr,
2319 "Error: quota manager could not read from cachemanager pipe");
2320 }
2321 2303036 }
2322
2323 void *PosixQuotaManager::CollectMountpointsHashes(void *data) {
2324 #ifndef __APPLE__
2325 pthread_setname_np(pthread_self(), "hash_collector");
2326 auto *handler = static_cast<CollectorHandler *>(data);
2327
2328 const std::string mountpoint = handler->mp[handler->i];
2329 ssize_t n = getxattr(mountpoint.c_str(), "user.list_open_hashes", nullptr, 0);
2330 if (n < 0) {
2331 pthread_exit(nullptr);
2332 }
2333 std::vector<char> buf((size_t)n);
2334 n = getxattr(mountpoint.c_str(), "user.list_open_hashes", buf.data(),
2335 buf.size());
2336 if (n < 0) {
2337 pthread_exit(nullptr);
2338 }
2339
2340 std::vector<std::string> hash_strs;
2341 std::string hash_str;
2342 for (const char c : buf) {
2343 if (c == '\n') {
2344 hash_strs.push_back(hash_str);
2345 hash_str.clear();
2346 } else {
2347 hash_str += c;
2348 }
2349 }
2350 const MutexLockGuard lock_guard(handler->l);
2351 for (auto hash_str : hash_strs) {
2352 handler->of.push_back(
2353 shash::Short(shash::MkFromHexPtr(shash::HexPtr(hash_str))));
2354 }
2355 #endif
2356 pthread_exit(nullptr);
2357 }
2358
2359 std::vector<shash::Short> PosixQuotaManager::CollectAllOpenHashes() {
2360 std::vector<CollectorHandler *> handlers;
2361 std::vector<pthread_t *> threads;
2362 open_files_.clear();
2363 #ifndef __APPLE__
2364 auto &&a_after_b = [](const struct timespec a, const struct timespec b) {
2365 return (a.tv_sec > b.tv_sec) ? true : false;
2366 };
2367
2368 for (size_t i = 0; i < mountpoints_.size(); ++i) {
2369 handlers.push_back(
2370 new CollectorHandler{open_files_, mountpoints_, lock_open_files_, i});
2371 threads.push_back(new pthread_t);
2372 }
2373
2374 const int retval = pthread_mutex_init(lock_open_files_, NULL);
2375 assert(retval == 0);
2376
2377 for (size_t i = 0; i < mountpoints_.size(); ++i) {
2378 pthread_create(threads[i], nullptr, CollectMountpointsHashes, handlers[i]);
2379 }
2380
2381 std::vector<bool> joined(handlers.size(), false);
2382 struct timespec reference, current;
2383 clock_gettime(CLOCK_REALTIME, &reference);
2384 clock_gettime(CLOCK_REALTIME, &current);
2385 reference.tv_sec += 10; // Give 10sec for hash collection
2386 size_t i = 0;
2387 while (
2388 (not std::all_of(joined.begin(), joined.end(), [](bool b) { return b; }))
2389 and a_after_b(reference, current)) {
2390 // as long as there are still threads that haven't joined yet
2391 // and for 10 seconds
2392 if (not joined[i]) {
2393 const int s = pthread_tryjoin_np(*threads[i], NULL);
2394 if (s == 0) {
2395 joined[i] = true;
2396 }
2397 }
2398 ++i;
2399 i = i % handlers.size();
2400 clock_gettime(CLOCK_REALTIME, &current);
2401 }
2402
2403 for (size_t i = 0; i < handlers.size(); ++i) {
2404 delete handlers[i];
2405 }
2406
2407 pthread_mutex_destroy(lock_open_files_);
2408 #endif
2409 return open_files_;
2410 }
2411