GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.cc
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 821 1098 74.8%
Branches: 680 1612 42.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 #define __STDC_FORMAT_MACROS
17
18 #include "cvmfs_config.h"
19 #include "quota_posix.h"
20
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <stdint.h>
28 #include <sys/dir.h>
29 #include <sys/stat.h>
30 #ifndef __APPLE__
31 #include <sys/statfs.h>
32 #endif
33 #include <sys/statvfs.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <unistd.h>
37
38 #include <cassert>
39 #include <cstdio>
40 #include <cstdlib>
41 #include <cstring>
42
43 #include <map>
44 #include <set>
45 #include <string>
46 #include <vector>
47
48 #include "crypto/hash.h"
49 #include "duplex_sqlite3.h"
50 #include "monitor.h"
51 #include "statistics.h"
52 #include "util/concurrency.h"
53 #include "util/exception.h"
54 #include "util/logging.h"
55 #include "util/platform.h"
56 #include "util/pointer.h"
57 #include "util/posix.h"
58 #include "util/smalloc.h"
59 #include "util/string.h"
60
61 using namespace std; // NOLINT
62
63
64 91 int PosixQuotaManager::BindReturnPipe(int pipe_wronly) {
65
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 2 times.
91 if (!shared_)
66 89 return pipe_wronly;
67
68 // Connect writer's end
69 int result =
70
3/6
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
2 open((workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str(),
71 O_WRONLY | O_NONBLOCK);
72
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (result >= 0) {
73 1 Nonblock2Block(result);
74 } else {
75 1 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
76 1 "failed to bind return pipe (%d)", errno);
77 }
78 2 return result;
79 }
80
81
82 26 void PosixQuotaManager::CheckHighPinWatermark() {
83 26 const uint64_t watermark = kHighPinWatermark*cleanup_threshold_/100;
84
3/4
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 23 times.
26 if ((cleanup_threshold_ > 0) && (pinned_ > watermark)) {
85 3 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
86 "high watermark of pinned files (%" PRIu64 "M > %" PRIu64 "M)",
87 3 pinned_/(1024*1024), watermark/(1024*1024));
88
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 BroadcastBackchannels("R"); // clients: please release pinned catalogs
89 }
90 26 }
91
92
93 void PosixQuotaManager::CleanupPipes() {
94 DIR *dirp = opendir(workspace_dir_.c_str());
95 assert(dirp != NULL);
96
97 platform_dirent64 *dent;
98 bool found_leftovers = false;
99 while ((dent = platform_readdir(dirp)) != NULL) {
100 const string name = dent->d_name;
101 const string path = workspace_dir_ + "/" + name;
102 platform_stat64 info;
103 int retval = platform_stat(path.c_str(), &info);
104 if (retval != 0)
105 continue;
106 if (S_ISFIFO(info.st_mode) && (name.substr(0, 4) == "pipe")) {
107 if (!found_leftovers) {
108 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn,
109 "removing left-over FIFOs from cache directory");
110 }
111 found_leftovers = true;
112 unlink(path.c_str());
113 }
114 }
115 closedir(dirp);
116 }
117
118
119 /**
120 * Cleans up in data cache, until cache size is below leave_size.
121 * The actual unlinking is done in a separate process (fork).
122 *
123 * \return True on success, false otherwise
124 */
125 9 bool PosixQuotaManager::Cleanup(const uint64_t leave_size) {
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (!spawned_)
127 return DoCleanup(leave_size);
128
129 bool result;
130 int pipe_cleanup[2];
131
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 MakeReturnPipe(pipe_cleanup);
132
133 9 LruCommand cmd;
134 9 cmd.command_type = kCleanup;
135 9 cmd.size = leave_size;
136 9 cmd.return_pipe = pipe_cleanup[1];
137
138
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
139
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 ReadHalfPipe(pipe_cleanup[0], &result, sizeof(result));
140
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 CloseReturnPipe(pipe_cleanup);
141
142 9 return result;
143 }
144
145
146 85 void PosixQuotaManager::CloseDatabase() {
147
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_list_catalogs_) sqlite3_finalize(stmt_list_catalogs_);
148
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_list_pinned_) sqlite3_finalize(stmt_list_pinned_);
149
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_list_volatile_) sqlite3_finalize(stmt_list_volatile_);
150
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_list_) sqlite3_finalize(stmt_list_);
151
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_lru_) sqlite3_finalize(stmt_lru_);
152
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_rm_) sqlite3_finalize(stmt_rm_);
153
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_size_) sqlite3_finalize(stmt_size_);
154
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_touch_) sqlite3_finalize(stmt_touch_);
155
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_unpin_) sqlite3_finalize(stmt_unpin_);
156
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_block_) sqlite3_finalize(stmt_block_);
157
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_unblock_) sqlite3_finalize(stmt_unblock_);
158
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (stmt_new_) sqlite3_finalize(stmt_new_);
159
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 if (database_) sqlite3_close(database_);
160 85 UnlockFile(fd_lock_cachedb_);
161
162 85 stmt_list_catalogs_ = NULL;
163 85 stmt_list_pinned_ = NULL;
164 85 stmt_list_volatile_ = NULL;
165 85 stmt_list_ = NULL;
166 85 stmt_rm_ = NULL;
167 85 stmt_size_ = NULL;
168 85 stmt_touch_ = NULL;
169 85 stmt_unpin_ = NULL;
170 85 stmt_block_ = NULL;
171 85 stmt_unblock_ = NULL;
172 85 stmt_new_ = NULL;
173 85 database_ = NULL;
174
175 85 pinned_chunks_.clear();
176 85 }
177
178
179 86 void PosixQuotaManager::CloseReturnPipe(int pipe[2]) {
180
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 84 times.
86 if (shared_) {
181 2 close(pipe[0]);
182 2 UnlinkReturnPipe(pipe[1]);
183 } else {
184 84 ClosePipe(pipe);
185 }
186 86 }
187
188
189 76 bool PosixQuotaManager::Contains(const string &hash_str) {
190 76 bool result = false;
191
192 76 sqlite3_bind_text(stmt_size_, 1, &hash_str[0], hash_str.length(),
193 SQLITE_STATIC);
194
2/2
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 57 times.
76 if (sqlite3_step(stmt_size_) == SQLITE_ROW)
195 19 result = true;
196 76 sqlite3_reset(stmt_size_);
197 76 LogCvmfs(kLogQuota, kLogDebug, "contains %s returns %d",
198 hash_str.c_str(), result);
199
200 76 return result;
201 }
202
203
204 84 void PosixQuotaManager::CheckFreeSpace() {
205
3/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 83 times.
84 if ((limit_ == 0) || (gauge_ >= limit_))
206 1 return;
207
208 struct statvfs vfs_info;
209
1/2
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
83 int retval = statvfs((cache_dir_ + "/cachedb").c_str(), &vfs_info);
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (retval != 0) {
211 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
212 "failed to query %s for free space (%d)",
213 cache_dir_.c_str(), errno);
214 return;
215 }
216 83 int64_t free_space_byte = vfs_info.f_bavail * vfs_info.f_bsize;
217
1/2
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
83 LogCvmfs(kLogQuota, kLogDebug, "free space: %" PRId64 " MB",
218 free_space_byte / (1024 * 1024));
219
220 83 int64_t required_byte = limit_ - gauge_;
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (free_space_byte < required_byte) {
222 LogCvmfs(kLogQuota, kLogSyslogWarn,
223 "too little free space on the file system hosting the cache,"
224 " %" PRId64 " MB available",
225 free_space_byte / (1024 * 1024));
226 }
227 }
228
229
230 87 PosixQuotaManager *PosixQuotaManager::Create(
231 const string &cache_workspace,
232 const uint64_t limit,
233 const uint64_t cleanup_threshold,
234 const bool rebuild_database)
235 {
236
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 85 times.
87 if (cleanup_threshold >= limit) {
237 2 LogCvmfs(kLogQuota, kLogDebug, "invalid parameters: limit %" PRIu64 ", "
238 "cleanup_threshold %" PRIu64, limit, cleanup_threshold);
239 2 return NULL;
240 }
241
242 PosixQuotaManager *quota_manager =
243
1/2
✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
85 new PosixQuotaManager(limit, cleanup_threshold, cache_workspace);
244
245 // Initialize cache catalog
246
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 84 times.
85 if (!quota_manager->InitDatabase(rebuild_database)) {
247
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 delete quota_manager;
248 1 return NULL;
249 }
250 84 quota_manager->CheckFreeSpace();
251 84 MakePipe(quota_manager->pipe_lru_);
252
253 84 quota_manager->protocol_revision_ = kProtocolRevision;
254 84 quota_manager->initialized_ = true;
255 84 return quota_manager;
256 }
257
258
259 /**
260 * Connects to a running shared local quota manager. Creates one if necessary.
261 */
262 3 PosixQuotaManager *PosixQuotaManager::CreateShared(
263 const std::string &exe_path,
264 const std::string &cache_workspace,
265 const uint64_t limit,
266 const uint64_t cleanup_threshold,
267 bool foreground)
268 {
269 3 string cache_dir;
270 3 string workspace_dir;
271
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 ParseDirectories(cache_workspace, &cache_dir, &workspace_dir);
272
273 // Create lock file: only one fuse client at a time
274
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 const int fd_lockfile = LockFile(workspace_dir + "/lock_cachemgr");
275
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (fd_lockfile < 0) {
276
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
277
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 (workspace_dir + "/lock_cachemgr").c_str(), errno);
278 1 return NULL;
279 }
280
281 PosixQuotaManager *quota_mgr =
282
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 new PosixQuotaManager(limit, cleanup_threshold, cache_workspace);
283 2 quota_mgr->shared_ = true;
284 2 quota_mgr->spawned_ = true;
285
286 // Try to connect to pipe
287
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const string fifo_path = workspace_dir + "/cachemgr";
288
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 LogCvmfs(kLogQuota, kLogDebug, "trying to connect to existing pipe");
289
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (quota_mgr->pipe_lru_[1] >= 0) {
291 LogCvmfs(kLogQuota, kLogDebug, "connected to existing cache manager pipe");
292 quota_mgr->initialized_ = true;
293 Nonblock2Block(quota_mgr->pipe_lru_[1]);
294 UnlockFile(fd_lockfile);
295 quota_mgr->GetLimits(&quota_mgr->limit_, &quota_mgr->cleanup_threshold_);
296 LogCvmfs(kLogQuota, kLogDebug,
297 "received limit %" PRIu64 ", threshold %" PRIu64,
298 quota_mgr->limit_, quota_mgr->cleanup_threshold_);
299 if (FileExists(workspace_dir + "/cachemgr.protocol")) {
300 quota_mgr->protocol_revision_ = quota_mgr->GetProtocolRevision();
301 LogCvmfs(kLogQuota, kLogDebug, "connected protocol revision %u",
302 quota_mgr->protocol_revision_);
303 } else {
304 LogCvmfs(kLogQuota, kLogDebug, "connected to ancient cache manager");
305 }
306 return quota_mgr;
307 }
308 2 const int connect_error = errno;
309
310 // Lock file: let existing cache manager finish first
311
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 const int fd_lockfile_fifo = LockFile(workspace_dir + "/lock_cachemgr.fifo");
312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (fd_lockfile_fifo < 0) {
313 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
314 (workspace_dir + "/lock_cachemgr.fifo").c_str(), errno);
315 UnlockFile(fd_lockfile);
316 delete quota_mgr;
317 return NULL;
318 }
319
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 UnlockFile(fd_lockfile_fifo);
320
321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (connect_error == ENXIO) {
322 LogCvmfs(kLogQuota, kLogDebug, "left-over FIFO found, unlinking");
323 unlink(fifo_path.c_str());
324 }
325
326 // Creating a new FIFO for the cache manager (to be bound later)
327 2 int retval = mkfifo(fifo_path.c_str(), 0600);
328
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (retval != 0) {
329 LogCvmfs(kLogQuota, kLogDebug, "failed to create cache manager FIFO (%d)",
330 errno);
331 UnlockFile(fd_lockfile);
332 delete quota_mgr;
333 return NULL;
334 }
335
336 // Create new cache manager
337 int pipe_boot[2];
338 int pipe_handshake[2];
339
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 MakePipe(pipe_boot);
340
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 MakePipe(pipe_handshake);
341
342 2 vector<string> command_line;
343
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 command_line.push_back(exe_path);
344
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 command_line.push_back("__cachemgr__");
345
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 command_line.push_back(cache_workspace);
346
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 command_line.push_back(StringifyInt(pipe_boot[1]));
347
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 command_line.push_back(StringifyInt(pipe_handshake[0]));
348
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 command_line.push_back(StringifyInt(limit));
349
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 command_line.push_back(StringifyInt(cleanup_threshold));
350
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 command_line.push_back(StringifyInt(foreground));
351
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 command_line.push_back(StringifyInt(GetLogSyslogLevel()));
352
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 command_line.push_back(StringifyInt(GetLogSyslogFacility()));
353
5/14
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
2 command_line.push_back(GetLogDebugFile() + ":" + GetLogMicroSyslog());
354
355 2 set<int> preserve_filedes;
356
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 preserve_filedes.insert(0);
357
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 preserve_filedes.insert(1);
358
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 preserve_filedes.insert(2);
359
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 preserve_filedes.insert(pipe_boot[1]);
360
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 preserve_filedes.insert(pipe_handshake[0]);
361
362
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 retval = ManagedExec(command_line, preserve_filedes, map<int, int>(), false);
363
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!retval) {
364 UnlockFile(fd_lockfile);
365 ClosePipe(pipe_boot);
366 ClosePipe(pipe_handshake);
367 delete quota_mgr;
368 LogCvmfs(kLogQuota, kLogDebug, "failed to start cache manager");
369 return NULL;
370 }
371
372 // Wait for cache manager to be ready
373
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 close(pipe_boot[1]);
374
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 close(pipe_handshake[0]);
375 char buf;
376
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 if (read(pipe_boot[0], &buf, 1) != 1) {
377
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 UnlockFile(fd_lockfile);
378
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 close(pipe_boot[0]);
379
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 close(pipe_handshake[1]);
380
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 delete quota_mgr;
381
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
382 "cache manager did not start");
383 2 return NULL;
384 }
385 close(pipe_boot[0]);
386
387 // Connect write end
388 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
389 if (quota_mgr->pipe_lru_[1] < 0) {
390 LogCvmfs(kLogQuota, kLogDebug,
391 "failed to connect to newly created FIFO (%d)", errno);
392 close(pipe_handshake[1]);
393 UnlockFile(fd_lockfile);
394 delete quota_mgr;
395 return NULL;
396 }
397
398 // Finalize handshake
399 buf = 'C';
400 if (write(pipe_handshake[1], &buf, 1) != 1) {
401 UnlockFile(fd_lockfile);
402 close(pipe_handshake[1]);
403 LogCvmfs(kLogQuota, kLogDebug, "could not finalize handshake");
404 delete quota_mgr;
405 return NULL;
406 }
407 close(pipe_handshake[1]);
408
409 Nonblock2Block(quota_mgr->pipe_lru_[1]);
410 LogCvmfs(kLogQuota, kLogDebug, "connected to a new cache manager");
411 quota_mgr->protocol_revision_ = kProtocolRevision;
412
413 UnlockFile(fd_lockfile);
414
415 quota_mgr->initialized_ = true;
416 quota_mgr->GetLimits(&quota_mgr->limit_, &quota_mgr->cleanup_threshold_);
417 LogCvmfs(kLogQuota, kLogDebug, "received limit %" PRIu64 ", "
418 "threshold %" PRIu64,
419 quota_mgr->limit_, quota_mgr->cleanup_threshold_);
420 return quota_mgr;
421 3 }
422
423
424 10 bool PosixQuotaManager::DoCleanup(const uint64_t leave_size) {
425
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
10 if (gauge_ <= leave_size)
426 2 return true;
427
428 // TODO(jblomer) transaction
429
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogSyslog,
430 "clean up cache until at most %lu KB is used", leave_size/1024);
431
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "gauge %" PRIu64, gauge_);
432
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 cleanup_recorder_.Tick();
433
434 bool result;
435 8 string hash_str;
436 8 vector<string> trash;
437
438 do {
439
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 sqlite3_reset(stmt_lru_);
440
3/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 15 times.
16 if (sqlite3_step(stmt_lru_) != SQLITE_ROW) {
441
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 LogCvmfs(kLogQuota, kLogDebug, "could not get lru-entry");
442 1 break;
443 }
444
445 30 hash_str = string(reinterpret_cast<const char *>(
446
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 sqlite3_column_text(stmt_lru_, 0)));
447
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 LogCvmfs(kLogQuota, kLogDebug, "removing %s", hash_str.c_str());
448
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 shash::Any hash = shash::MkFromHexPtr(shash::HexPtr(hash_str));
449
450 // That's a critical condition. We must not delete a not yet inserted
451 // pinned file as it is already reserved (but will be inserted later).
452 // Instead, set the pin bit in the db to not run into an endless loop
453
3/4
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 1 times.
15 if (pinned_chunks_.find(hash) == pinned_chunks_.end()) {
454
4/8
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
14 trash.push_back(cache_dir_ + "/" + hash.MakePathWithoutSuffix());
455
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 gauge_ -= sqlite3_column_int64(stmt_lru_, 1);
456
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
457 hash_str.c_str(), gauge_);
458
459
2/4
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
14 sqlite3_bind_text(stmt_rm_, 1, &hash_str[0], hash_str.length(),
460 SQLITE_STATIC);
461
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 result = (sqlite3_step(stmt_rm_) == SQLITE_DONE);
462
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 sqlite3_reset(stmt_rm_);
463
464
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!result) {
465 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
466 "failed to find %s in cache database (%d). "
467 "Cache database is out of sync. "
468 "Restart cvmfs with clean cache.", hash_str.c_str(), result);
469 return false;
470 }
471 } else {
472
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 sqlite3_bind_text(stmt_block_, 1, &hash_str[0], hash_str.length(),
473 SQLITE_STATIC);
474
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 result = (sqlite3_step(stmt_block_) == SQLITE_DONE);
475
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_reset(stmt_block_);
476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 assert(result);
477 }
478
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 7 times.
15 } while (gauge_ > leave_size);
479
480
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 result = (sqlite3_step(stmt_unblock_) == SQLITE_DONE);
481
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_reset(stmt_unblock_);
482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 assert(result);
483
484 // Double fork avoids zombie, forked removal process must not flush file
485 // buffers
486
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (!trash.empty()) {
487
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (async_delete_) {
488 pid_t pid;
489 int statloc;
490
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if ((pid = fork()) == 0) {
491 // TODO(jblomer): eviciting files in the cache should perhaps become a
492 // thread. This would also allow to block the chunks and prevent the
493 // race with re-insertion. Then again, a thread can block umount.
494 #ifndef DEBUGMSG
495 CloseAllFildes(std::set<int>());
496 #endif
497 if (fork() == 0) {
498 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
499 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
500 unlink(trash[i].c_str());
501 }
502 _exit(0);
503 }
504 _exit(0);
505 } else {
506
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (pid > 0)
507
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 waitpid(pid, &statloc, 0);
508 else
509 return false;
510 }
511 } else { // !async_delete_
512
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
5 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
513
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
514 3 unlink(trash[i].c_str());
515 }
516 }
517 }
518
519
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (gauge_ > leave_size) {
520
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
521 "request to clean until %" PRIu64 ", "
522 "but effective gauge is %" PRIu64, leave_size, gauge_);
523 1 return false;
524 }
525 7 return true;
526 8 }
527
528
529 55 void PosixQuotaManager::DoInsert(
530 const shash::Any &hash,
531 const uint64_t size,
532 const string &description,
533 const CommandType command_type)
534 {
535
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 const string hash_str = hash.ToString();
536
1/2
✓ Branch 3 taken 55 times.
✗ Branch 4 not taken.
55 LogCvmfs(kLogQuota, kLogDebug, "insert into lru %s, path %s, method %d",
537 hash_str.c_str(), description.c_str(), command_type);
538 55 const unsigned desc_length = (description.length() > kMaxDescription) ?
539
1/2
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
55 kMaxDescription : description.length();
540
541 LruCommand *cmd =
542 55 reinterpret_cast<LruCommand *>(alloca(sizeof(LruCommand) + desc_length));
543 55 new (cmd) LruCommand;
544 55 cmd->command_type = command_type;
545 55 cmd->SetSize(size);
546
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 cmd->StoreHash(hash);
547 55 cmd->desc_length = desc_length;
548 110 memcpy(reinterpret_cast<char *>(cmd)+sizeof(LruCommand),
549 55 &description[0], desc_length);
550
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
551 55 }
552
553
554 35 vector<string> PosixQuotaManager::DoList(const CommandType list_command) {
555 35 vector<string> result;
556
557 int pipe_list[2];
558
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 MakeReturnPipe(pipe_list);
559 char description_buffer[kMaxDescription];
560
561 35 LruCommand cmd;
562 35 cmd.command_type = list_command;
563 35 cmd.return_pipe = pipe_list[1];
564
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
565
566 int length;
567 do {
568
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 ReadHalfPipe(pipe_list[0], &length, sizeof(length));
569
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 35 times.
86 if (length > 0) {
570
1/2
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
51 ReadPipe(pipe_list[0], description_buffer, length);
571
2/4
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 51 times.
✗ Branch 6 not taken.
51 result.push_back(string(description_buffer, length));
572 }
573
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 35 times.
86 } while (length >= 0);
574
575
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 CloseReturnPipe(pipe_list);
576 70 return result;
577 }
578
579
580 33 uint64_t PosixQuotaManager::GetCapacity() {
581
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 if (limit_ != (uint64_t)(-1))
582 33 return limit_;
583
584 // Unrestricted cache, look at free space on cache dir fs
585 struct statfs info;
586 if (statfs(".", &info) == 0) {
587 return info.f_bavail * info.f_bsize;
588 } else {
589 LogCvmfs(kLogQuota, kLogSyslogErr | kLogDebug,
590 "failed to query file system info of cache (%d)", errno);
591 return limit_;
592 }
593 }
594
595
596 void PosixQuotaManager::GetLimits(uint64_t *limit, uint64_t *cleanup_threshold)
597 {
598 int pipe_limits[2];
599 MakeReturnPipe(pipe_limits);
600
601 LruCommand cmd;
602 cmd.command_type = kLimits;
603 cmd.return_pipe = pipe_limits[1];
604 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
605 ReadHalfPipe(pipe_limits[0], limit, sizeof(*limit));
606 ReadPipe(pipe_limits[0], cleanup_threshold, sizeof(*cleanup_threshold));
607 CloseReturnPipe(pipe_limits);
608 }
609
610
611 /**
612 * Since we only cleanup until cleanup_threshold, we can only add
613 * files smaller than limit-cleanup_threshold.
614 */
615 8 uint64_t PosixQuotaManager::GetMaxFileSize() {
616 8 return limit_ - cleanup_threshold_;
617 }
618
619
620 1 pid_t PosixQuotaManager::GetPid() {
621
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (!shared_ || !spawned_) {
622 1 return getpid();
623 }
624
625 pid_t result;
626 int pipe_pid[2];
627 MakeReturnPipe(pipe_pid);
628
629 LruCommand cmd;
630 cmd.command_type = kPid;
631 cmd.return_pipe = pipe_pid[1];
632 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
633 ReadHalfPipe(pipe_pid[0], &result, sizeof(result));
634 CloseReturnPipe(pipe_pid);
635 return result;
636 }
637
638
639 1 uint32_t PosixQuotaManager::GetProtocolRevision() {
640 int pipe_revision[2];
641
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 MakeReturnPipe(pipe_revision);
642
643 1 LruCommand cmd;
644 1 cmd.command_type = kGetProtocolRevision;
645 1 cmd.return_pipe = pipe_revision[1];
646
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
647
648 uint32_t revision;
649
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ReadHalfPipe(pipe_revision[0], &revision, sizeof(revision));
650
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 CloseReturnPipe(pipe_revision);
651 1 return revision;
652 }
653
654
655 /**
656 * Queries the shared local hard disk quota manager.
657 */
658 18 void PosixQuotaManager::GetSharedStatus(uint64_t *gauge, uint64_t *pinned) {
659 int pipe_status[2];
660
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 MakeReturnPipe(pipe_status);
661
662 18 LruCommand cmd;
663 18 cmd.command_type = kStatus;
664 18 cmd.return_pipe = pipe_status[1];
665
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
666
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 ReadHalfPipe(pipe_status[0], gauge, sizeof(*gauge));
667
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 ReadPipe(pipe_status[0], pinned, sizeof(*pinned));
668
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 CloseReturnPipe(pipe_status);
669 18 }
670
671
672 82 uint64_t PosixQuotaManager::GetSize() {
673
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 16 times.
82 if (!spawned_) return gauge_;
674 uint64_t gauge, size_pinned;
675
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 GetSharedStatus(&gauge, &size_pinned);
676 16 return gauge;
677 }
678
679
680 2 uint64_t PosixQuotaManager::GetSizePinned() {
681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!spawned_) return pinned_;
682 uint64_t gauge, size_pinned;
683
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 GetSharedStatus(&gauge, &size_pinned);
684 2 return size_pinned;
685 }
686
687
688 4 uint64_t PosixQuotaManager::GetCleanupRate(uint64_t period_s) {
689
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!spawned_ || (protocol_revision_ < 2)) return 0;
690 uint64_t cleanup_rate;
691
692 int pipe_cleanup_rate[2];
693
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 MakeReturnPipe(pipe_cleanup_rate);
694 4 LruCommand cmd;
695 4 cmd.command_type = kCleanupRate;
696 4 cmd.size = period_s;
697 4 cmd.return_pipe = pipe_cleanup_rate[1];
698
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
699
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 ReadHalfPipe(pipe_cleanup_rate[0], &cleanup_rate, sizeof(cleanup_rate));
700
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 CloseReturnPipe(pipe_cleanup_rate);
701
702 4 return cleanup_rate;
703 }
704
705
706 90 bool PosixQuotaManager::InitDatabase(const bool rebuild_database) {
707 90 string sql;
708 sqlite3_stmt *stmt;
709
710
2/4
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 90 times.
✗ Branch 5 not taken.
90 fd_lock_cachedb_ = LockFile(workspace_dir_ + "/lock_cachedb");
711
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 89 times.
90 if (fd_lock_cachedb_ < 0) {
712
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 LogCvmfs(kLogQuota, kLogDebug, "failed to create cachedb lock");
713 1 return false;
714 }
715
716 89 bool retry = false;
717
1/2
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
89 const string db_file = cache_dir_ + "/cachedb";
718
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 5 times.
89 if (rebuild_database) {
719
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 LogCvmfs(kLogQuota, kLogDebug, "rebuild database, unlinking existing (%s)",
720 db_file.c_str());
721 5 unlink(db_file.c_str());
722
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 unlink((db_file + "-journal").c_str());
723 }
724
725 84 init_recover:
726
1/2
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 int err = sqlite3_open(db_file.c_str(), &database_);
727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 if (err != SQLITE_OK) {
728 LogCvmfs(kLogQuota, kLogDebug, "could not open cache database (%d)", err);
729 goto init_database_fail;
730 }
731 // TODO(reneme): make this a `QuotaDatabase : public sqlite::Database`
732 sql = "PRAGMA synchronous=0; PRAGMA locking_mode=EXCLUSIVE; "
733 "PRAGMA auto_vacuum=1; "
734 "CREATE TABLE IF NOT EXISTS cache_catalog (sha1 TEXT, size INTEGER, "
735 " acseq INTEGER, path TEXT, type INTEGER, pinned INTEGER, "
736 "CONSTRAINT pk_cache_catalog PRIMARY KEY (sha1)); "
737 "CREATE UNIQUE INDEX IF NOT EXISTS idx_cache_catalog_acseq "
738 " ON cache_catalog (acseq); "
739 "CREATE TEMP TABLE fscache (sha1 TEXT, size INTEGER, actime INTEGER, "
740 "CONSTRAINT pk_fscache PRIMARY KEY (sha1)); "
741 "CREATE INDEX idx_fscache_actime ON fscache (actime); "
742 "CREATE TABLE IF NOT EXISTS properties (key TEXT, value TEXT, "
743
1/2
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
89 " CONSTRAINT pk_properties PRIMARY KEY(key));";
744
1/2
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
745
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 if (err != SQLITE_OK) {
746 if (!retry) {
747 retry = true;
748 sqlite3_close(database_);
749 unlink(db_file.c_str());
750 unlink((db_file + "-journal").c_str());
751 LogCvmfs(kLogQuota, kLogSyslogWarn,
752 "LRU database corrupted, re-building");
753 goto init_recover;
754 }
755 LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
756 sql.c_str());
757 goto init_database_fail;
758 }
759
760 // If this an old cache catalog,
761 // add and initialize new columns to cache_catalog
762 sql = "ALTER TABLE cache_catalog ADD type INTEGER; "
763
1/2
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
89 "ALTER TABLE cache_catalog ADD pinned INTEGER";
764
1/2
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
765
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 if (err == SQLITE_OK) {
766 sql = "UPDATE cache_catalog SET type=" + StringifyInt(kFileRegular) + ";";
767 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
768 if (err != SQLITE_OK) {
769 LogCvmfs(kLogQuota, kLogDebug,
770 "could not init cache database (failed: %s)", sql.c_str());
771 goto init_database_fail;
772 }
773 }
774
775 // Set pinned back
776
1/2
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
89 sql = "UPDATE cache_catalog SET pinned=0;";
777
1/2
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
778
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 if (err != SQLITE_OK) {
779 LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
780 sql.c_str());
781 goto init_database_fail;
782 }
783
784 // Set schema version
785 sql = "INSERT OR REPLACE INTO properties (key, value) "
786
1/2
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
89 "VALUES ('schema', '1.0')";
787
1/2
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
788
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 if (err != SQLITE_OK) {
789 LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
790 sql.c_str());
791 goto init_database_fail;
792 }
793
794 // If cache catalog is empty, recreate from file system
795
1/2
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
89 sql = "SELECT count(*) FROM cache_catalog;";
796
1/2
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
797
2/4
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
89 if (sqlite3_step(stmt) == SQLITE_ROW) {
798
6/8
✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 85 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 85 times.
✓ Branch 8 taken 4 times.
89 if ((sqlite3_column_int64(stmt, 0)) == 0 || rebuild_database) {
799
1/2
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
85 LogCvmfs(kLogCvmfs, kLogDebug,
800 "CernVM-FS: building lru cache database...");
801
3/4
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 82 times.
85 if (!RebuildDatabase()) {
802
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 LogCvmfs(kLogQuota, kLogDebug,
803 "could not build cache database from file system");
804
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 sqlite3_finalize(stmt);
805 3 goto init_database_fail;
806 }
807 }
808
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_finalize(stmt);
809 } else {
810 LogCvmfs(kLogQuota, kLogDebug, "could not select on cache catalog");
811 sqlite3_finalize(stmt);
812 goto init_database_fail;
813 }
814
815 // How many bytes do we already have in cache?
816
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sql = "SELECT sum(size) FROM cache_catalog;";
817
1/2
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
86 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
818
2/4
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 86 times.
✗ Branch 4 not taken.
86 if (sqlite3_step(stmt) == SQLITE_ROW) {
819
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 gauge_ = sqlite3_column_int64(stmt, 0);
820 } else {
821 LogCvmfs(kLogQuota, kLogDebug, "could not determine cache size");
822 sqlite3_finalize(stmt);
823 goto init_database_fail;
824 }
825
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_finalize(stmt);
826
827 // Highest seq-no?
828
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sql = "SELECT coalesce(max(acseq & (~(1<<63))), 0) FROM cache_catalog;";
829
1/2
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
86 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
830
2/4
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 86 times.
✗ Branch 4 not taken.
86 if (sqlite3_step(stmt) == SQLITE_ROW) {
831
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 seq_ = sqlite3_column_int64(stmt, 0)+1;
832 } else {
833 LogCvmfs(kLogQuota, kLogDebug, "could not determine highest seq-no");
834 sqlite3_finalize(stmt);
835 goto init_database_fail;
836 }
837
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_finalize(stmt);
838
839 // Prepare touch, new, remove statements
840
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_,
841 "UPDATE cache_catalog SET acseq=:seq | (acseq&(1<<63)) "
842 "WHERE sha1=:sha1;", -1, &stmt_touch_, NULL);
843
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_, "UPDATE cache_catalog SET pinned=0 "
844 "WHERE sha1=:sha1;", -1, &stmt_unpin_, NULL);
845
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_, "UPDATE cache_catalog SET pinned=2 "
846 "WHERE sha1=:sha1;", -1, &stmt_block_, NULL);
847
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_, "UPDATE cache_catalog SET pinned=1 "
848 "WHERE pinned=2;", -1, &stmt_unblock_, NULL);
849
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_,
850 "INSERT OR REPLACE INTO cache_catalog "
851 "(sha1, size, acseq, path, type, pinned) "
852 "VALUES (:sha1, :s, :seq, :p, :t, :pin);",
853 -1, &stmt_new_, NULL);
854
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_,
855 "SELECT size, pinned FROM cache_catalog WHERE sha1=:sha1;",
856 -1, &stmt_size_, NULL);
857
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_, "DELETE FROM cache_catalog WHERE sha1=:sha1;",
858 -1, &stmt_rm_, NULL);
859
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_,
860 "SELECT sha1, size FROM cache_catalog WHERE "
861 "acseq=(SELECT min(acseq) "
862 "FROM cache_catalog WHERE pinned<>2);",
863 -1, &stmt_lru_, NULL);
864
1/2
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
86 sqlite3_prepare_v2(database_,
865
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 ("SELECT path FROM cache_catalog WHERE type=" +
866
2/4
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 86 times.
✗ Branch 5 not taken.
258 StringifyInt(kFileRegular) +
867 ";").c_str(), -1, &stmt_list_, NULL);
868
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_,
869 "SELECT path FROM cache_catalog WHERE pinned<>0;",
870 -1, &stmt_list_pinned_, NULL);
871
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 sqlite3_prepare_v2(database_,
872 "SELECT path FROM cache_catalog WHERE acseq < 0;",
873 -1, &stmt_list_volatile_, NULL);
874
1/2
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
86 sqlite3_prepare_v2(database_,
875
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 ("SELECT path FROM cache_catalog WHERE type=" +
876
2/4
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 86 times.
✗ Branch 5 not taken.
258 StringifyInt(kFileCatalog) +
877 ";").c_str(), -1, &stmt_list_catalogs_, NULL);
878 86 return true;
879
880 3 init_database_fail:
881
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 sqlite3_close(database_);
882 3 database_ = NULL;
883
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 UnlockFile(fd_lock_cachedb_);
884 3 return false;
885 90 }
886
887
888 /**
889 * Inserts a new file into cache catalog. This file gets a new,
890 * highest sequence number. Does cache cleanup if necessary.
891 */
892 38 void PosixQuotaManager::Insert(
893 const shash::Any &any_hash,
894 const uint64_t size,
895 const string &description)
896 {
897 38 DoInsert(any_hash, size, description, kInsert);
898 38 }
899
900
901 /**
902 * Inserts a new file into cache catalog. This file is marked as volatile
903 * and gets a new highest sequence number with the first bit set. Cache cleanup
904 * treats these files with priority.
905 */
906 4 void PosixQuotaManager::InsertVolatile(
907 const shash::Any &any_hash,
908 const uint64_t size,
909 const string &description)
910 {
911 4 DoInsert(any_hash, size, description, kInsertVolatile);
912 4 }
913
914
915 /**
916 * Lists all path names from the cache db.
917 */
918 21 vector<string> PosixQuotaManager::List() {
919 21 return DoList(kList);
920 }
921
922
923 /**
924 * Lists all pinned files from the cache db.
925 */
926 8 vector<string> PosixQuotaManager::ListPinned() {
927 8 return DoList(kListPinned);
928 }
929
930
931 /**
932 * Lists all sqlite catalog files from the cache db.
933 */
934 3 vector<string> PosixQuotaManager::ListCatalogs() {
935 3 return DoList(kListCatalogs);
936 }
937
938
939 /**
940 * Lists only files flagged as volatile (priority removal)
941 */
942 3 vector<string> PosixQuotaManager::ListVolatile() {
943 3 return DoList(kListVolatile);
944 }
945
946
947 /**
948 * Entry point for the shared cache manager process
949 */
950 int PosixQuotaManager::MainCacheManager(int argc, char **argv) {
951 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
952 int retval;
953
954 PosixQuotaManager shared_manager(0, 0, "");
955 shared_manager.shared_ = true;
956 shared_manager.spawned_ = true;
957 shared_manager.pinned_ = 0;
958
959 // Process command line arguments
960 ParseDirectories(string(argv[2]),
961 &shared_manager.cache_dir_,
962 &shared_manager.workspace_dir_);
963 int pipe_boot = String2Int64(argv[3]);
964 int pipe_handshake = String2Int64(argv[4]);
965 shared_manager.limit_ = String2Int64(argv[5]);
966 shared_manager.cleanup_threshold_ = String2Int64(argv[6]);
967 int foreground = String2Int64(argv[7]);
968 int syslog_level = String2Int64(argv[8]);
969 int syslog_facility = String2Int64(argv[9]);
970 vector<string> logfiles = SplitString(argv[10], ':');
971
972 SetLogSyslogLevel(syslog_level);
973 SetLogSyslogFacility(syslog_facility);
974 if ((logfiles.size() > 0) && (logfiles[0] != ""))
975 SetLogDebugFile(logfiles[0] + ".cachemgr");
976 if (logfiles.size() > 1)
977 SetLogMicroSyslog(logfiles[1]);
978
979 if (!foreground)
980 Daemonize();
981
982 UniquePtr<Watchdog> watchdog(Watchdog::Create(NULL));
983 assert(watchdog.IsValid());
984 watchdog->Spawn("./stacktrace.cachemgr");
985
986 // Initialize pipe, open non-blocking as cvmfs is not yet connected
987 const int fd_lockfile_fifo =
988 LockFile(shared_manager.workspace_dir_ + "/lock_cachemgr.fifo");
989 if (fd_lockfile_fifo < 0) {
990 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr, "could not open lock file "
991 "%s (%d)",
992 (shared_manager.workspace_dir_ + "/lock_cachemgr.fifo").c_str(),
993 errno);
994 return 1;
995 }
996 const string crash_guard = shared_manager.cache_dir_ + "/cachemgr.running";
997 const bool rebuild = FileExists(crash_guard);
998 retval = open(crash_guard.c_str(), O_RDONLY | O_CREAT, 0600);
999 if (retval < 0) {
1000 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1001 "failed to create shared cache manager crash guard");
1002 UnlockFile(fd_lockfile_fifo);
1003 return 1;
1004 }
1005 close(retval);
1006
1007 // Redirect SQlite temp directory to cache (global variable)
1008 const string tmp_dir = shared_manager.workspace_dir_;
1009 sqlite3_temp_directory =
1010 static_cast<char *>(sqlite3_malloc(tmp_dir.length() + 1));
1011 snprintf(sqlite3_temp_directory, tmp_dir.length() + 1, "%s", tmp_dir.c_str());
1012
1013 // Cleanup leftover named pipes
1014 shared_manager.CleanupPipes();
1015
1016 if (!shared_manager.InitDatabase(rebuild)) {
1017 UnlockFile(fd_lockfile_fifo);
1018 return 1;
1019 }
1020 shared_manager.CheckFreeSpace();
1021
1022 // Save protocol revision to file. If the file is not found, it indicates
1023 // to the client that the cache manager is from times before the protocol
1024 // was versioned.
1025 const string protocol_revision_path =
1026 shared_manager.workspace_dir_ + "/cachemgr.protocol";
1027 retval = open(protocol_revision_path.c_str(), O_WRONLY | O_CREAT, 0600);
1028 if (retval < 0) {
1029 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1030 "failed to open protocol revision file (%d)", errno);
1031 UnlockFile(fd_lockfile_fifo);
1032 return 1;
1033 }
1034 const string revision = StringifyInt(kProtocolRevision);
1035 int written = write(retval, revision.data(), revision.length());
1036 close(retval);
1037 if ((written < 0) || static_cast<unsigned>(written) != revision.length()) {
1038 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1039 "failed to write protocol revision (%d)", errno);
1040 UnlockFile(fd_lockfile_fifo);
1041 return 1;
1042 }
1043
1044 const string fifo_path = shared_manager.workspace_dir_ + "/cachemgr";
1045 shared_manager.pipe_lru_[0] = open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK);
1046 if (shared_manager.pipe_lru_[0] < 0) {
1047 LogCvmfs(kLogQuota, kLogDebug, "failed to listen on FIFO %s (%d)",
1048 fifo_path.c_str(), errno);
1049 UnlockFile(fd_lockfile_fifo);
1050 return 1;
1051 }
1052 Nonblock2Block(shared_manager.pipe_lru_[0]);
1053 LogCvmfs(kLogQuota, kLogDebug, "shared cache manager listening");
1054
1055 char buf = 'C';
1056 WritePipe(pipe_boot, &buf, 1);
1057 close(pipe_boot);
1058
1059 ReadPipe(pipe_handshake, &buf, 1);
1060 close(pipe_handshake);
1061 LogCvmfs(kLogQuota, kLogDebug, "shared cache manager handshake done");
1062
1063 // Ensure that broken pipes from clients do not kill the cache manager
1064 signal(SIGPIPE, SIG_IGN);
1065 // Don't let Ctrl-C ungracefully kill interactive session
1066 signal(SIGINT, SIG_IGN);
1067
1068 shared_manager.MainCommandServer(&shared_manager);
1069 unlink(fifo_path.c_str());
1070 unlink(protocol_revision_path.c_str());
1071 shared_manager.CloseDatabase();
1072 unlink(crash_guard.c_str());
1073 UnlockFile(fd_lockfile_fifo);
1074
1075 if (sqlite3_temp_directory) {
1076 sqlite3_free(sqlite3_temp_directory);
1077 sqlite3_temp_directory = NULL;
1078 }
1079
1080 return 0;
1081 }
1082
1083
1084 30 void *PosixQuotaManager::MainCommandServer(void *data) {
1085 30 PosixQuotaManager *quota_mgr = static_cast<PosixQuotaManager *>(data);
1086
1087
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1088
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 sqlite3_soft_heap_limit(quota_mgr->kSqliteMemPerThread);
1089
1090
2/2
✓ Branch 1 taken 960 times.
✓ Branch 2 taken 30 times.
990 LruCommand command_buffer[kCommandBufferSize];
1091 char description_buffer[kCommandBufferSize*kMaxDescription];
1092 30 unsigned num_commands = 0;
1093
1094
1/2
✓ Branch 1 taken 177 times.
✗ Branch 2 not taken.
177 while (read(quota_mgr->pipe_lru_[0], &command_buffer[num_commands],
1095
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 30 times.
177 sizeof(command_buffer[0])) == sizeof(command_buffer[0]))
1096 {
1097 147 const CommandType command_type = command_buffer[num_commands].command_type;
1098
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 LogCvmfs(kLogQuota, kLogDebug, "received command %d", command_type);
1099 147 const uint64_t size = command_buffer[num_commands].GetSize();
1100
1101 // Inserts and pins come with a description (usually a path)
1102
6/6
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 114 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 112 times.
✓ Branch 5 taken 2 times.
147 if ((command_type == kInsert) || (command_type == kInsertVolatile) ||
1103
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 101 times.
112 (command_type == kPin) || (command_type == kPinRegular))
1104 {
1105 46 const int desc_length = command_buffer[num_commands].desc_length;
1106 46 ReadPipe(quota_mgr->pipe_lru_[0],
1107
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 &description_buffer[kMaxDescription*num_commands], desc_length);
1108 }
1109
1110 // The protocol revision is returned immediately
1111
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 146 times.
147 if (command_type == kGetProtocolRevision) {
1112 int return_pipe =
1113
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 quota_mgr->BindReturnPipe(command_buffer[num_commands].return_pipe);
1114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (return_pipe < 0)
1115 continue;
1116
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 WritePipe(return_pipe, &quota_mgr->kProtocolRevision,
1117 sizeof(quota_mgr->kProtocolRevision));
1118
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 quota_mgr->UnbindReturnPipe(return_pipe);
1119 1 continue;
1120 1 }
1121
1122 // The cleanup rate is returned immediately
1123
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 142 times.
146 if (command_type == kCleanupRate) {
1124 int return_pipe =
1125
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 quota_mgr->BindReturnPipe(command_buffer[num_commands].return_pipe);
1126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (return_pipe < 0)
1127 continue;
1128 4 uint64_t period_s = size; // use the size field to transmit the period
1129
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 uint64_t rate = quota_mgr->cleanup_recorder_.GetNoTicks(period_s);
1130
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 WritePipe(return_pipe, &rate, sizeof(rate));
1131
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 quota_mgr->UnbindReturnPipe(return_pipe);
1132 4 continue;
1133 4 }
1134
1135 // Reservations are handled immediately and "out of band"
1136
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 128 times.
142 if (command_type == kReserve) {
1137 14 bool success = true;
1138 int return_pipe =
1139
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 quota_mgr->BindReturnPipe(command_buffer[num_commands].return_pipe);
1140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (return_pipe < 0)
1141 continue;
1142
1143
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1144
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 const string hash_str(hash.ToString());
1145
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 LogCvmfs(kLogQuota, kLogDebug, "reserve %lu bytes for %s",
1146 size, hash_str.c_str());
1147
1148
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 if (quota_mgr->pinned_chunks_.find(hash) ==
1149
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
28 quota_mgr->pinned_chunks_.end())
1150 {
1151
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
12 if ((quota_mgr->pinned_ + size) > quota_mgr->cleanup_threshold_) {
1152
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 LogCvmfs(kLogQuota, kLogDebug,
1153 "failed to insert %s (pinned), no space", hash_str.c_str());
1154 1 success = false;
1155 } else {
1156
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 quota_mgr->pinned_chunks_[hash] = size;
1157 11 quota_mgr->pinned_ += size;
1158
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 quota_mgr->CheckHighPinWatermark();
1159 }
1160 }
1161
1162
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 WritePipe(return_pipe, &success, sizeof(success));
1163
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 quota_mgr->UnbindReturnPipe(return_pipe);
1164 14 continue;
1165 14 }
1166
1167 // Back channels are also handled out of band
1168
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 124 times.
128 if (command_type == kRegisterBackChannel) {
1169 int return_pipe =
1170
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 quota_mgr->BindReturnPipe(command_buffer[num_commands].return_pipe);
1171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (return_pipe < 0)
1172 continue;
1173
1174
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 quota_mgr->UnlinkReturnPipe(command_buffer[num_commands].return_pipe);
1175
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 Block2Nonblock(return_pipe); // back channels are opportunistic
1176
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 shash::Md5 hash;
1177 4 memcpy(hash.digest, command_buffer[num_commands].digest,
1178 4 shash::kDigestSizes[shash::kMd5]);
1179
1180 4 quota_mgr->LockBackChannels();
1181 map<shash::Md5, int>::const_iterator iter =
1182
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 quota_mgr->back_channels_.find(hash);
1183
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if (iter != quota_mgr->back_channels_.end()) {
1184 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1185 "closing left-over back channel %s", hash.ToString().c_str());
1186 close(iter->second);
1187 }
1188
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 quota_mgr->back_channels_[hash] = return_pipe;
1189 4 quota_mgr->UnlockBackChannels();
1190
1191 4 char success = 'S';
1192
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 WritePipe(return_pipe, &success, sizeof(success));
1193
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 LogCvmfs(kLogQuota, kLogDebug, "register back channel %s on fd %d",
1194
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 hash.ToString().c_str(), return_pipe);
1195
1196 4 continue;
1197 4 }
1198
1199
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 122 times.
124 if (command_type == kUnregisterBackChannel) {
1200
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 shash::Md5 hash;
1201 2 memcpy(hash.digest, command_buffer[num_commands].digest,
1202 2 shash::kDigestSizes[shash::kMd5]);
1203
1204 2 quota_mgr->LockBackChannels();
1205 map<shash::Md5, int>::iterator iter =
1206
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 quota_mgr->back_channels_.find(hash);
1207
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (iter != quota_mgr->back_channels_.end()) {
1208
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 LogCvmfs(kLogQuota, kLogDebug,
1209
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 "closing back channel %s", hash.ToString().c_str());
1210
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 close(iter->second);
1211
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 quota_mgr->back_channels_.erase(iter);
1212 } else {
1213 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1214 "did not find back channel %s", hash.ToString().c_str());
1215 }
1216 2 quota_mgr->UnlockBackChannels();
1217
1218 2 continue;
1219 2 }
1220
1221 // Unpinnings are also handled immediately with respect to the pinned gauge
1222
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 120 times.
122 if (command_type == kUnpin) {
1223
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1224
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const string hash_str(hash.ToString());
1225
1226 map<shash::Any, uint64_t>::iterator iter =
1227
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 quota_mgr->pinned_chunks_.find(hash);
1228
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (iter != quota_mgr->pinned_chunks_.end()) {
1229 2 quota_mgr->pinned_ -= iter->second;
1230
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 quota_mgr->pinned_chunks_.erase(iter);
1231 // It can happen that files get pinned that were removed from the cache
1232 // (see cache.cc). We fix this at this point, where we remove such
1233 // entries from the cache database.
1234
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 if (!FileExists(quota_mgr->cache_dir_ + "/" +
1235
3/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
4 hash.MakePathWithoutSuffix()))
1236 {
1237
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 LogCvmfs(kLogQuota, kLogDebug,
1238 "remove orphaned pinned hash %s from cache database",
1239 hash_str.c_str());
1240
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1241 1 hash_str.length(), SQLITE_STATIC);
1242 int retval;
1243
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1244
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_, 0);
1245
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1246 1 hash_str.length(), SQLITE_STATIC);
1247
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 retval = sqlite3_step(quota_mgr->stmt_rm_);
1248
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1249 1 quota_mgr->gauge_ -= size;
1250 } else {
1251 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1252 "failed to delete %s (%d)", hash_str.c_str(), retval);
1253 }
1254
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_reset(quota_mgr->stmt_rm_);
1255 }
1256
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_reset(quota_mgr->stmt_size_);
1257 }
1258 } else {
1259 LogCvmfs(kLogQuota, kLogDebug, "this chunk was not pinned");
1260 }
1261 2 }
1262
1263 // Immediate commands trigger flushing of the buffer
1264
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 21 times.
113 bool immediate_command = (command_type == kCleanup) ||
1265
4/4
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 81 times.
✓ Branch 3 taken 3 times.
92 (command_type == kList) || (command_type == kListPinned) ||
1266
4/4
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 75 times.
✓ Branch 3 taken 3 times.
81 (command_type == kListCatalogs) || (command_type == kListVolatile) ||
1267
3/4
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 57 times.
✗ Branch 3 not taken.
75 (command_type == kRemove) || (command_type == kStatus) ||
1268
3/4
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 57 times.
235 (command_type == kLimits) || (command_type == kPid);
1269
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 65 times.
122 if (!immediate_command) num_commands++;
1270
1271
3/4
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 57 times.
122 if ((num_commands == kCommandBufferSize) || immediate_command)
1272 {
1273
1/2
✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
65 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1274 description_buffer);
1275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (!immediate_command) num_commands = 0;
1276 }
1277
1278
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 57 times.
122 if (immediate_command) {
1279 // Process cleanup, listings
1280 int return_pipe =
1281
1/2
✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
65 quota_mgr->BindReturnPipe(command_buffer[num_commands].return_pipe);
1282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (return_pipe < 0) {
1283 num_commands = 0;
1284 continue;
1285 }
1286
1287 int retval;
1288 65 sqlite3_stmt *this_stmt_list = NULL;
1289
7/10
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
65 switch (command_type) {
1290 3 case kRemove: {
1291
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1292
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const string hash_str = hash.ToString();
1293
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 LogCvmfs(kLogQuota, kLogDebug, "manually removing %s",
1294 hash_str.c_str());
1295 3 bool success = false;
1296
1297
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1298 3 hash_str.length(), SQLITE_STATIC);
1299 int retval;
1300
3/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
3 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1301
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_, 0);
1302
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 uint64_t is_pinned = sqlite3_column_int64(quota_mgr->stmt_size_, 1);
1303
1304
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1305 2 hash_str.length(), SQLITE_STATIC);
1306
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 retval = sqlite3_step(quota_mgr->stmt_rm_);
1307
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1308 2 success = true;
1309 2 quota_mgr->gauge_ -= size;
1310
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (is_pinned) {
1311
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 quota_mgr->pinned_chunks_.erase(hash);
1312 1 quota_mgr->pinned_ -= size;
1313 }
1314 } else {
1315 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1316 "failed to delete %s (%d)", hash_str.c_str(), retval);
1317 }
1318
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 sqlite3_reset(quota_mgr->stmt_rm_);
1319 } else {
1320 // File does not exist
1321 1 success = true;
1322 }
1323
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 sqlite3_reset(quota_mgr->stmt_size_);
1324
1325
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 WritePipe(return_pipe, &success, sizeof(success));
1326 3 break; }
1327 9 case kCleanup:
1328
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 retval = quota_mgr->DoCleanup(size);
1329
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 WritePipe(return_pipe, &retval, sizeof(retval));
1330 9 break;
1331 21 case kList:
1332
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if (!this_stmt_list) this_stmt_list = quota_mgr->stmt_list_;
1333 case kListPinned:
1334
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 21 times.
29 if (!this_stmt_list) this_stmt_list = quota_mgr->stmt_list_pinned_;
1335 case kListCatalogs:
1336
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 29 times.
32 if (!this_stmt_list) this_stmt_list = quota_mgr->stmt_list_catalogs_;
1337 case kListVolatile:
1338
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 32 times.
35 if (!this_stmt_list) this_stmt_list = quota_mgr->stmt_list_volatile_;
1339
1340 // Pipe back the list, one by one
1341 int length;
1342
3/4
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 51 times.
✓ Branch 4 taken 35 times.
86 while (sqlite3_step(this_stmt_list) == SQLITE_ROW) {
1343
1/2
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
51 string path = "(NULL)";
1344
2/4
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 51 times.
✗ Branch 4 not taken.
51 if (sqlite3_column_type(this_stmt_list, 0) != SQLITE_NULL) {
1345 102 path = string(
1346 reinterpret_cast<const char *>(
1347
2/4
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 51 times.
✗ Branch 5 not taken.
51 sqlite3_column_text(this_stmt_list, 0)));
1348 }
1349 51 length = path.length();
1350
1/2
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
51 WritePipe(return_pipe, &length, sizeof(length));
1351
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 if (length > 0)
1352
2/4
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 51 times.
✗ Branch 5 not taken.
51 WritePipe(return_pipe, &path[0], length);
1353 51 }
1354 35 length = -1;
1355
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 WritePipe(return_pipe, &length, sizeof(length));
1356
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 sqlite3_reset(this_stmt_list);
1357 35 break;
1358 18 case kStatus:
1359
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 WritePipe(return_pipe, &quota_mgr->gauge_, sizeof(quota_mgr->gauge_));
1360
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 WritePipe(return_pipe, &quota_mgr->pinned_,
1361 sizeof(quota_mgr->pinned_));
1362 18 break;
1363 case kLimits:
1364 WritePipe(return_pipe, &quota_mgr->limit_, sizeof(quota_mgr->limit_));
1365 WritePipe(return_pipe, &quota_mgr->cleanup_threshold_,
1366 sizeof(quota_mgr->cleanup_threshold_));
1367 break;
1368 case kPid: {
1369 pid_t pid = getpid();
1370 WritePipe(return_pipe, &pid, sizeof(pid));
1371 break;
1372 }
1373 default:
1374 PANIC(NULL); // other types are handled by the bunch processor
1375 }
1376
1/2
✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
65 quota_mgr->UnbindReturnPipe(return_pipe);
1377 65 num_commands = 0;
1378 }
1379 }
1380
1381
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 LogCvmfs(kLogQuota, kLogDebug, "stopping cache manager (%d)", errno);
1382
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 close(quota_mgr->pipe_lru_[0]);
1383
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1384 description_buffer);
1385
1386 // Unpin
1387 30 command_buffer[0].command_type = kTouch;
1388 30 for (map<shash::Any, uint64_t>::const_iterator i =
1389 30 quota_mgr->pinned_chunks_.begin(),
1390
2/2
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 30 times.
40 iEnd = quota_mgr->pinned_chunks_.end(); i != iEnd; ++i)
1391 {
1392
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 command_buffer[0].StoreHash(i->first);
1393
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 quota_mgr->ProcessCommandBunch(1, command_buffer, description_buffer);
1394 }
1395
1396 30 return NULL;
1397 }
1398
1399
1400 91 void PosixQuotaManager::MakeReturnPipe(int pipe[2]) {
1401
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 3 times.
91 if (!shared_) {
1402 88 MakePipe(pipe);
1403 88 return;
1404 }
1405
1406 // Create FIFO in cache directory, store path name (number) in pipe write end
1407 3 int i = 0;
1408 int retval;
1409 do {
1410
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
4 retval = mkfifo((workspace_dir_ + "/pipe" + StringifyInt(i)).c_str(), 0600);
1411 4 pipe[1] = i;
1412 4 i++;
1413
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
4 } while ((retval == -1) && (errno == EEXIST));
1414
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 assert(retval == 0);
1415
1416 // Connect reader's end
1417
3/6
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
3 pipe[0] = open((workspace_dir_ + "/pipe" + StringifyInt(pipe[1])).c_str(),
1418 O_RDONLY | O_NONBLOCK);
1419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 assert(pipe[0] >= 0);
1420 3 Nonblock2Block(pipe[0]);
1421 }
1422
1423
1424 91 void PosixQuotaManager::ParseDirectories(
1425 const std::string cache_workspace,
1426 std::string *cache_dir,
1427 std::string *workspace_dir)
1428 {
1429
1/2
✓ Branch 1 taken 91 times.
✗ Branch 2 not taken.
91 vector<string> dir_tokens(SplitString(cache_workspace, ':'));
1430
2/3
✓ Branch 1 taken 89 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
91 switch (dir_tokens.size()) {
1431 89 case 1:
1432
2/4
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 89 times.
✗ Branch 6 not taken.
89 *cache_dir = *workspace_dir = dir_tokens[0];
1433 89 break;
1434 2 case 2:
1435
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 *cache_dir = dir_tokens[0];
1436
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 *workspace_dir = dir_tokens[1];
1437 2 break;
1438 default:
1439 PANIC(NULL);
1440 }
1441 91 }
1442
1443
1444 /**
1445 * Immediately inserts a new pinned catalog. Does cache cleanup if necessary.
1446 *
1447 * \return True on success, false otherwise
1448 */
1449 43 bool PosixQuotaManager::Pin(
1450 const shash::Any &hash,
1451 const uint64_t size,
1452 const string &description,
1453 const bool is_catalog)
1454 {
1455
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
43 assert((size > 0) || !is_catalog);
1456
1457
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 const string hash_str = hash.ToString();
1458
1/2
✓ Branch 3 taken 43 times.
✗ Branch 4 not taken.
43 LogCvmfs(kLogQuota, kLogDebug, "pin into lru %s, path %s",
1459 hash_str.c_str(), description.c_str());
1460
1461 // Has to run when not yet spawned (cvmfs initialization)
1462
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 14 times.
43 if (!spawned_) {
1463 // Code duplication here
1464
3/4
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 17 times.
✓ Branch 6 taken 12 times.
29 if (pinned_chunks_.find(hash) == pinned_chunks_.end()) {
1465
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
17 if (pinned_ + size > cleanup_threshold_) {
1466
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 LogCvmfs(kLogQuota, kLogDebug, "failed to insert %s (pinned), no space",
1467 hash_str.c_str());
1468 2 return false;
1469 } else {
1470
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 pinned_chunks_[hash] = size;
1471 15 pinned_ += size;
1472
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 CheckHighPinWatermark();
1473 }
1474 }
1475
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 bool exists = Contains(hash_str);
1476
4/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14 times.
27 if (!exists && (gauge_ + size > limit_)) {
1477
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %lu",
1478 gauge_, size);
1479
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 int retval = DoCleanup(cleanup_threshold_);
1480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 assert(retval != 0);
1481 }
1482
1/2
✓ Branch 3 taken 27 times.
✗ Branch 4 not taken.
27 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1483 SQLITE_STATIC);
1484
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 sqlite3_bind_int64(stmt_new_, 2, size);
1485
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1486
1/2
✓ Branch 3 taken 27 times.
✗ Branch 4 not taken.
27 sqlite3_bind_text(stmt_new_, 4, &description[0], description.length(),
1487 SQLITE_STATIC);
1488
3/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 27 times.
✗ Branch 4 not taken.
27 sqlite3_bind_int64(stmt_new_, 5, is_catalog ? kFileCatalog : kFileRegular);
1489
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 sqlite3_bind_int64(stmt_new_, 6, 1);
1490
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 int retval = sqlite3_step(stmt_new_);
1491
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
27 assert((retval == SQLITE_DONE) || (retval == SQLITE_OK));
1492
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 sqlite3_reset(stmt_new_);
1493
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 12 times.
27 if (!exists) gauge_ += size;
1494 27 return true;
1495 }
1496
1497 int pipe_reserve[2];
1498
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 MakeReturnPipe(pipe_reserve);
1499
1500 14 LruCommand cmd;
1501 14 cmd.command_type = kReserve;
1502 14 cmd.SetSize(size);
1503
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 cmd.StoreHash(hash);
1504 14 cmd.return_pipe = pipe_reserve[1];
1505
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1506 bool result;
1507
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 ReadHalfPipe(pipe_reserve[0], &result, sizeof(result));
1508
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 CloseReturnPipe(pipe_reserve);
1509
1510
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
14 if (!result) return false;
1511
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
13 DoInsert(hash, size, description, is_catalog ? kPin : kPinRegular);
1512
1513 13 return true;
1514 43 }
1515
1516
1517 88 PosixQuotaManager::PosixQuotaManager(
1518 const uint64_t limit,
1519 const uint64_t cleanup_threshold,
1520 88 const string &cache_workspace)
1521 88 : shared_(false)
1522 88 , spawned_(false)
1523 88 , limit_(limit)
1524 88 , cleanup_threshold_(cleanup_threshold)
1525 88 , gauge_(0)
1526 88 , pinned_(0)
1527 88 , seq_(0)
1528 88 , cache_dir_() // initialized in body
1529 88 , workspace_dir_() // initialized in body
1530 88 , fd_lock_cachedb_(-1)
1531 88 , async_delete_(true)
1532 88 , database_(NULL)
1533 88 , stmt_touch_(NULL)
1534 88 , stmt_unpin_(NULL)
1535 88 , stmt_block_(NULL)
1536 88 , stmt_unblock_(NULL)
1537 88 , stmt_new_(NULL)
1538 88 , stmt_lru_(NULL)
1539 88 , stmt_size_(NULL)
1540 88 , stmt_rm_(NULL)
1541 88 , stmt_list_(NULL)
1542 88 , stmt_list_pinned_(NULL)
1543 88 , stmt_list_catalogs_(NULL)
1544 88 , stmt_list_volatile_(NULL)
1545 176 , initialized_(false)
1546 {
1547
2/4
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 88 times.
✗ Branch 5 not taken.
88 ParseDirectories(cache_workspace, &cache_dir_, &workspace_dir_);
1548 88 pipe_lru_[0] = pipe_lru_[1] = -1;
1549
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 cleanup_recorder_.AddRecorder(1, 90); // last 1.5 min with second resolution
1550 // last 1.5 h with minute resolution
1551
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 cleanup_recorder_.AddRecorder(60, 90*60);
1552 // last 18 hours with 20 min resolution
1553
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 cleanup_recorder_.AddRecorder(20*60, 60*60*18);
1554 // last 4 days with hour resolution
1555
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 cleanup_recorder_.AddRecorder(60*60, 60*60*24*4);
1556 88 }
1557
1558
1559 348 PosixQuotaManager::~PosixQuotaManager() {
1560
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 83 times.
174 if (!initialized_) return;
1561
1562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
166 if (shared_) {
1563 // Most of cleanup is done elsewhen by shared cache manager
1564 close(pipe_lru_[1]);
1565 return;
1566 }
1567
1568
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 53 times.
166 if (spawned_) {
1569 60 char fin = 0;
1570 60 WritePipe(pipe_lru_[1], &fin, 1);
1571 60 close(pipe_lru_[1]);
1572 60 pthread_join(thread_lru_, NULL);
1573 } else {
1574 106 ClosePipe(pipe_lru_);
1575 }
1576
1577 166 CloseDatabase();
1578
10/10
✓ Branch 1 taken 83 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 83 times.
✓ Branch 5 taken 4 times.
✓ Branch 7 taken 83 times.
✓ Branch 8 taken 4 times.
✓ Branch 10 taken 83 times.
✓ Branch 11 taken 4 times.
✓ Branch 13 taken 83 times.
✓ Branch 14 taken 4 times.
380 }
1579
1580
1581 105 void PosixQuotaManager::ProcessCommandBunch(
1582 const unsigned num,
1583 const LruCommand *commands,
1584 const char *descriptions)
1585 {
1586 105 int retval = sqlite3_exec(database_, "BEGIN", NULL, NULL, NULL);
1587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 assert(retval == SQLITE_OK);
1588
1589
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 105 times.
172 for (unsigned i = 0; i < num; ++i) {
1590
1/2
✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
67 const shash::Any hash = commands[i].RetrieveHash();
1591
1/2
✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
67 const string hash_str = hash.ToString();
1592 67 const unsigned size = commands[i].GetSize();
1593
1/2
✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
67 LogCvmfs(kLogQuota, kLogDebug, "processing %s (%d)",
1594 67 hash_str.c_str(), commands[i].command_type);
1595
1596 bool exists;
1597
3/4
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
67 switch (commands[i].command_type) {
1598 19 case kTouch:
1599
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 sqlite3_bind_int64(stmt_touch_, 1, seq_++);
1600
1/2
✓ Branch 3 taken 19 times.
✗ Branch 4 not taken.
19 sqlite3_bind_text(stmt_touch_, 2, &hash_str[0], hash_str.length(),
1601 SQLITE_STATIC);
1602
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 retval = sqlite3_step(stmt_touch_);
1603
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 LogCvmfs(kLogQuota, kLogDebug, "touching %s (%ld): %d",
1604 19 hash_str.c_str(), seq_-1, retval);
1605
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
19 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1606 PANIC(kLogSyslogErr, "failed to update %s in cachedb, error %d",
1607 hash_str.c_str(), retval);
1608 }
1609
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 sqlite3_reset(stmt_touch_);
1610 19 break;
1611 2 case kUnpin:
1612
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 sqlite3_bind_text(stmt_unpin_, 1, &hash_str[0], hash_str.length(),
1613 SQLITE_STATIC);
1614
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 retval = sqlite3_step(stmt_unpin_);
1615
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 LogCvmfs(kLogQuota, kLogDebug, "unpinning %s: %d",
1616 hash_str.c_str(), retval);
1617
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1618 PANIC(kLogSyslogErr, "failed to unpin %s in cachedb, error %d",
1619 hash_str.c_str(), retval);
1620 }
1621
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 sqlite3_reset(stmt_unpin_);
1622 2 break;
1623 46 case kPin:
1624 case kPinRegular:
1625 case kInsert:
1626 case kInsertVolatile:
1627 // It could already be in, check
1628
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 exists = Contains(hash_str);
1629
1630 // Cleanup, move to trash and unlink
1631
3/4
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 41 times.
46 if (!exists && (gauge_ + size > limit_)) {
1632 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %u",
1633 gauge_, size);
1634 retval = DoCleanup(cleanup_threshold_);
1635 assert(retval != 0);
1636 }
1637
1638 // Insert or replace
1639
1/2
✓ Branch 3 taken 46 times.
✗ Branch 4 not taken.
46 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1640 SQLITE_STATIC);
1641
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 sqlite3_bind_int64(stmt_new_, 2, size);
1642
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 42 times.
46 if (commands[i].command_type == kInsertVolatile) {
1643
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 sqlite3_bind_int64(stmt_new_, 3, (seq_++) | kVolatileFlag);
1644 } else {
1645
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1646 }
1647 46 sqlite3_bind_text(stmt_new_, 4, &descriptions[i*kMaxDescription],
1648
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 commands[i].desc_length, SQLITE_STATIC);
1649
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 44 times.
✓ Branch 3 taken 46 times.
✗ Branch 4 not taken.
46 sqlite3_bind_int64(stmt_new_, 5, (commands[i].command_type == kPin) ?
1650 kFileCatalog : kFileRegular);
1651
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 sqlite3_bind_int64(stmt_new_, 6,
1652
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 2 times.
46 ((commands[i].command_type == kPin) ||
1653
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 33 times.
44 (commands[i].command_type == kPinRegular)) ? 1 : 0);
1654
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 retval = sqlite3_step(stmt_new_);
1655
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 LogCvmfs(kLogQuota, kLogDebug, "insert or replace %s, method %d: %d",
1656 46 hash_str.c_str(), commands[i].command_type, retval);
1657
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)) {
1658 PANIC(kLogSyslogErr, "failed to insert %s in cachedb, error %d",
1659 hash_str.c_str(), retval);
1660 }
1661
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 sqlite3_reset(stmt_new_);
1662
1663
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 5 times.
46 if (!exists) gauge_ += size;
1664 46 break;
1665 default:
1666 // other types should have been taken care of by event loop
1667 PANIC(NULL);
1668 }
1669 67 }
1670
1671 105 retval = sqlite3_exec(database_, "COMMIT", NULL, NULL, NULL);
1672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (retval != SQLITE_OK) {
1673 PANIC(kLogSyslogErr, "failed to commit to cachedb, error %d", retval);
1674 }
1675 105 }
1676
1677
1678 85 bool PosixQuotaManager::RebuildDatabase() {
1679 85 bool result = false;
1680 85 string sql;
1681 85 sqlite3_stmt *stmt_select = NULL;
1682 85 sqlite3_stmt *stmt_insert = NULL;
1683 int sqlerr;
1684 85 int seq = 0;
1685 char hex[4];
1686 struct stat info;
1687 platform_dirent64 *d;
1688 85 DIR *dirp = NULL;
1689 85 string path;
1690
1691
1/2
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
85 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug, "re-building cache database");
1692
1693 // Empty cache catalog and fscache
1694
1/2
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
85 sql = "DELETE FROM cache_catalog; DELETE FROM fscache;";
1695
1/2
✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
85 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if (sqlerr != SQLITE_OK) {
1697 LogCvmfs(kLogQuota, kLogDebug, "could not clear cache database");
1698 goto build_return;
1699 }
1700
1701 85 gauge_ = 0;
1702
1703 // Insert files from cache sub-directories 00 - ff
1704 // TODO(jblomer): fs_traversal
1705
1/2
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
85 sqlite3_prepare_v2(database_, "INSERT INTO fscache (sha1, size, actime) "
1706 "VALUES (:sha1, :s, :t);", -1, &stmt_insert, NULL);
1707
1708
2/2
✓ Branch 0 taken 20995 times.
✓ Branch 1 taken 82 times.
21077 for (int i = 0; i <= 0xff; i++) {
1709 20995 snprintf(hex, sizeof(hex), "%02x", i);
1710
3/6
✓ Branch 2 taken 20995 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20995 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 20995 times.
✗ Branch 9 not taken.
20995 path = cache_dir_ + "/" + string(hex);
1711
3/4
✓ Branch 2 taken 20995 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 20992 times.
20995 if ((dirp = opendir(path.c_str())) == NULL) {
1712
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1713 "failed to open directory %s (tmpwatch interfering?)",
1714 path.c_str());
1715 3 goto build_return;
1716 }
1717
3/4
✓ Branch 1 taken 62978 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 41986 times.
✓ Branch 4 taken 20992 times.
62978 while ((d = platform_readdir(dirp)) != NULL) {
1718
3/6
✓ Branch 2 taken 41986 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 41986 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 41986 times.
✗ Branch 9 not taken.
83972 string file_path = path + "/" + string(d->d_name);
1719
1/2
✓ Branch 2 taken 41986 times.
✗ Branch 3 not taken.
41986 if (stat(file_path.c_str(), &info) == 0) {
1720
2/2
✓ Branch 0 taken 41984 times.
✓ Branch 1 taken 2 times.
41986 if (!S_ISREG(info.st_mode))
1721 41985 continue;
1722
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (info.st_size == 0) {
1723
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
1724 "removing empty file %s during automatic cache db rebuild",
1725 file_path.c_str());
1726 1 unlink(file_path.c_str());
1727 1 continue;
1728 }
1729
1730
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
2 string hash = string(hex) + string(d->d_name);
1731
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 sqlite3_bind_text(stmt_insert, 1, hash.data(), hash.length(),
1732 SQLITE_STATIC);
1733
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_bind_int64(stmt_insert, 2, info.st_size);
1734
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_bind_int64(stmt_insert, 3, info.st_atime);
1735
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (sqlite3_step(stmt_insert) != SQLITE_DONE) {
1736 LogCvmfs(kLogQuota, kLogDebug, "could not insert into temp table");
1737 goto build_return;
1738 }
1739
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_reset(stmt_insert);
1740
1741 1 gauge_ += info.st_size;
1742
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 } else {
1743 LogCvmfs(kLogQuota, kLogDebug, "could not stat %s", file_path.c_str());
1744 }
1745
2/3
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 41985 times.
✗ Branch 3 not taken.
41986 }
1746
1/2
✓ Branch 1 taken 20992 times.
✗ Branch 2 not taken.
20992 closedir(dirp);
1747 20992 dirp = NULL;
1748 }
1749
1/2
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
82 sqlite3_finalize(stmt_insert);
1750 82 stmt_insert = NULL;
1751
1752 // Transfer from temp table in cache catalog
1753
1/2
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
82 sqlite3_prepare_v2(database_,
1754 "SELECT sha1, size FROM fscache ORDER BY actime;",
1755 -1, &stmt_select, NULL);
1756
1/2
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
82 sqlite3_prepare_v2(database_,
1757 "INSERT INTO cache_catalog (sha1, size, acseq, path, type, pinned) "
1758 "VALUES (:sha1, :s, :seq, 'unknown (automatic rebuild)', :t, 0);",
1759 -1, &stmt_insert, NULL);
1760
3/4
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 82 times.
83 while (sqlite3_step(stmt_select) == SQLITE_ROW) {
1761 const string hash = string(
1762
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 reinterpret_cast<const char *>(sqlite3_column_text(stmt_select, 0)));
1763
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 sqlite3_bind_text(stmt_insert, 1, &hash[0], hash.length(), SQLITE_STATIC);
1764
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 sqlite3_bind_int64(stmt_insert, 2, sqlite3_column_int64(stmt_select, 1));
1765
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_bind_int64(stmt_insert, 3, seq++);
1766 // Might also be a catalog (information is lost)
1767
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_bind_int64(stmt_insert, 4, kFileRegular);
1768
1769
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 int retval = sqlite3_step(stmt_insert);
1770
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (retval != SQLITE_DONE) {
1771 // If the file system hosting the cache is full, we'll likely notice here
1772 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1773 "could not insert into cache catalog (%d - %s)",
1774 retval, sqlite3_errstr(retval));
1775 goto build_return;
1776 }
1777
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 sqlite3_reset(stmt_insert);
1778
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 }
1779
1780 // Delete temporary table
1781
1/2
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
82 sql = "DELETE FROM fscache;";
1782
1/2
✓ Branch 2 taken 82 times.
✗ Branch 3 not taken.
82 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1783
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (sqlerr != SQLITE_OK) {
1784 LogCvmfs(kLogQuota, kLogDebug, "could not clear temporary table (%d)",
1785 sqlerr);
1786 goto build_return;
1787 }
1788
1789 82 seq_ = seq;
1790 82 result = true;
1791
1/2
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
82 LogCvmfs(kLogQuota, kLogDebug,
1792 "rebuilding finished, sequence %" PRIu64 ", gauge %" PRIu64,
1793 seq_, gauge_);
1794
1795 85 build_return:
1796
2/4
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 85 times.
✗ Branch 4 not taken.
85 if (stmt_insert) sqlite3_finalize(stmt_insert);
1797
3/4
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 82 times.
✗ Branch 4 not taken.
85 if (stmt_select) sqlite3_finalize(stmt_select);
1798
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
85 if (dirp) closedir(dirp);
1799 85 return result;
1800 85 }
1801
1802
1803 /**
1804 * Register a channel that allows the cache manager to trigger action to its
1805 * clients. Currently used for releasing pinned catalogs.
1806 */
1807 4 void PosixQuotaManager::RegisterBackChannel(
1808 int back_channel[2],
1809 const string &channel_id)
1810 {
1811
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (protocol_revision_ >= 1) {
1812
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
1813
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 MakeReturnPipe(back_channel);
1814
1815 4 LruCommand cmd;
1816 4 cmd.command_type = kRegisterBackChannel;
1817 4 cmd.return_pipe = back_channel[1];
1818 // Not StoreHash(). This is an MD5 hash.
1819 4 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
1820
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1821
1822 char success;
1823
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 ReadHalfPipe(back_channel[0], &success, sizeof(success));
1824 // At this point, the named FIFO is unlinked, so don't use CloseReturnPipe
1825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (success != 'S') {
1826 PANIC(kLogDebug | kLogSyslogErr,
1827 "failed to register quota back channel (%c)", success);
1828 }
1829 } else {
1830 // Dummy pipe to return valid file descriptors
1831 MakePipe(back_channel);
1832 }
1833 4 }
1834
1835
1836 /**
1837 * Removes a chunk from cache, if it exists.
1838 */
1839 3 void PosixQuotaManager::Remove(const shash::Any &hash) {
1840
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 string hash_str = hash.ToString();
1841
1842 int pipe_remove[2];
1843
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MakeReturnPipe(pipe_remove);
1844
1845 3 LruCommand cmd;
1846 3 cmd.command_type = kRemove;
1847 3 cmd.return_pipe = pipe_remove[1];
1848
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 cmd.StoreHash(hash);
1849
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1850
1851 bool success;
1852
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 ReadHalfPipe(pipe_remove[0], &success, sizeof(success));
1853
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 CloseReturnPipe(pipe_remove);
1854
1855
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 unlink((cache_dir_ + "/" + hash.MakePathWithoutSuffix()).c_str());
1856 3 }
1857
1858
1859 32 void PosixQuotaManager::Spawn() {
1860
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 30 times.
32 if (spawned_)
1861 2 return;
1862
1863 30 if (pthread_create(&thread_lru_, NULL, MainCommandServer,
1864
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 static_cast<void *>(this)) != 0)
1865 {
1866 PANIC(kLogDebug, "could not create lru thread");
1867 }
1868
1869 30 spawned_ = true;
1870 }
1871
1872
1873 /**
1874 * Updates the sequence number of the file specified by the hash.
1875 */
1876 23 void PosixQuotaManager::Touch(const shash::Any &hash) {
1877 23 LruCommand cmd;
1878 23 cmd.command_type = kTouch;
1879
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 cmd.StoreHash(hash);
1880
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1881 23 }
1882
1883
1884 85 void PosixQuotaManager::UnbindReturnPipe(int pipe_wronly) {
1885
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 84 times.
85 if (shared_)
1886 1 close(pipe_wronly);
1887 85 }
1888
1889
1890 7 void PosixQuotaManager::UnlinkReturnPipe(int pipe_wronly) {
1891
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if (shared_)
1892
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 unlink((workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str());
1893 7 }
1894
1895
1896 19 void PosixQuotaManager::Unpin(const shash::Any &hash) {
1897
2/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 19 times.
✗ Branch 6 not taken.
19 LogCvmfs(kLogQuota, kLogDebug, "Unpin %s", hash.ToString().c_str());
1898
1899 19 LruCommand cmd;
1900 19 cmd.command_type = kUnpin;
1901
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 cmd.StoreHash(hash);
1902
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1903 19 }
1904
1905
1906 2 void PosixQuotaManager::UnregisterBackChannel(
1907 int back_channel[2],
1908 const string &channel_id)
1909 {
1910
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (protocol_revision_ >= 1) {
1911
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
1912
1913 2 LruCommand cmd;
1914 2 cmd.command_type = kUnregisterBackChannel;
1915 // Not StoreHash(). This is an MD5 hash.
1916 2 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
1917
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1918
1919 // Writer's end will be closed by cache manager, FIFO is already unlinked
1920
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 close(back_channel[0]);
1921 } else {
1922 ClosePipe(back_channel);
1923 }
1924 2 }
1925