1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
* |
4 |
|
|
* The cache module maintains the local file cache. Files are |
5 |
|
|
* staged into the cache by Fetch(). The cache stores files with a name |
6 |
|
|
* according to their content hash. |
7 |
|
|
* |
8 |
|
|
* The procedure is |
9 |
|
|
* -# Look in the catalog for content hash |
10 |
|
|
* -# If it is in local cache: return file descriptor |
11 |
|
|
* -# Otherwise download, store in cache and return fd |
12 |
|
|
* |
13 |
|
|
* Each running CVMFS instance has to have a separate cache directory. |
14 |
|
|
* The local cache directory (directories 00..ff) can be accessed |
15 |
|
|
* in parallel to a running CVMFS, i.e. files can be deleted for instance |
16 |
|
|
* anytime. However, this will confuse the cache database managed by the lru |
17 |
|
|
* module. |
18 |
|
|
* |
19 |
|
|
* Files are created in txn directory first. At the very latest |
20 |
|
|
* point they are renamed into their "real" content hash names atomically by |
21 |
|
|
* rename(). This concept is taken over from GROW-FS. |
22 |
|
|
* |
23 |
|
|
* Identical URLs won't be concurrently downloaded. The first thread performs |
24 |
|
|
* the download and informs the other, waiting threads on pipes. |
25 |
|
|
*/ |
26 |
|
|
|
27 |
|
|
#define __STDC_FORMAT_MACROS |
28 |
|
|
|
29 |
|
|
#include "cvmfs_config.h" |
30 |
|
|
#include "cache_posix.h" |
31 |
|
|
|
32 |
|
|
#include <dirent.h> |
33 |
|
|
#include <errno.h> |
34 |
|
|
#include <fcntl.h> |
35 |
|
|
#include <inttypes.h> |
36 |
|
|
#include <pthread.h> |
37 |
|
|
#include <sys/stat.h> |
38 |
|
|
#include <sys/types.h> |
39 |
|
|
#ifndef __APPLE__ |
40 |
|
|
#include <sys/statfs.h> |
41 |
|
|
#endif |
42 |
|
|
#include <unistd.h> |
43 |
|
|
|
44 |
|
|
#include <algorithm> |
45 |
|
|
#include <cassert> |
46 |
|
|
#include <cstdio> |
47 |
|
|
#include <cstdlib> |
48 |
|
|
#include <cstring> |
49 |
|
|
#include <map> |
50 |
|
|
#include <vector> |
51 |
|
|
|
52 |
|
|
#include "atomic.h" |
53 |
|
|
#include "directory_entry.h" |
54 |
|
|
#include "download.h" |
55 |
|
|
#include "hash.h" |
56 |
|
|
#include "logging.h" |
57 |
|
|
#include "manifest.h" |
58 |
|
|
#include "manifest_fetch.h" |
59 |
|
|
#include "platform.h" |
60 |
|
|
#include "quota.h" |
61 |
|
|
#include "shortstring.h" |
62 |
|
|
#include "signature.h" |
63 |
|
|
#include "smalloc.h" |
64 |
|
|
#include "statistics.h" |
65 |
|
|
#include "util/posix.h" |
66 |
|
|
|
67 |
|
|
#ifndef NFS_SUPER_MAGIC |
68 |
|
|
#define NFS_SUPER_MAGIC 0x6969 |
69 |
|
|
#endif |
70 |
|
|
#ifndef BEEGFS_SUPER_MAGIC |
71 |
|
|
#define BEEGFS_SUPER_MAGIC 0x19830326 |
72 |
|
|
#endif |
73 |
|
|
|
74 |
|
|
using namespace std; // NOLINT |
75 |
|
|
|
76 |
|
|
namespace { |
77 |
|
|
|
78 |
|
|
/** |
79 |
|
|
* A CallGuard object can be placed at the beginning of a function. It counts |
80 |
|
|
* the number of so-annotated functions that are in flight. The Drainout() call |
81 |
|
|
* will wait until all functions that have been called so far are finished. |
82 |
|
|
* |
83 |
|
|
* The class is used in order to wait for remaining calls when switching into |
84 |
|
|
* the read-only cache mode. |
85 |
|
|
*/ |
86 |
|
|
class CallGuard { |
87 |
|
|
public: |
88 |
|
|
CallGuard() { |
89 |
|
|
int32_t global_drainout = atomic_read32(&global_drainout_); |
90 |
|
|
drainout_ = (global_drainout != 0); |
91 |
|
|
if (!drainout_) |
92 |
|
|
atomic_inc32(&num_inflight_calls_); |
93 |
|
|
} |
94 |
|
|
~CallGuard() { |
95 |
|
|
if (!drainout_) |
96 |
|
|
atomic_dec32(&num_inflight_calls_); |
97 |
|
|
} |
98 |
|
|
static void Drainout() { |
99 |
|
|
atomic_cas32(&global_drainout_, 0, 1); |
100 |
|
|
while (atomic_read32(&num_inflight_calls_) != 0) |
101 |
|
|
SafeSleepMs(50); |
102 |
|
|
} |
103 |
|
|
private: |
104 |
|
|
bool drainout_; |
105 |
|
|
static atomic_int32 global_drainout_; |
106 |
|
|
static atomic_int32 num_inflight_calls_; |
107 |
|
|
}; |
108 |
|
|
atomic_int32 CallGuard::num_inflight_calls_ = 0; |
109 |
|
|
atomic_int32 CallGuard::global_drainout_ = 0; |
110 |
|
|
|
111 |
|
|
} // anonymous namespace |
112 |
|
|
|
113 |
|
|
|
114 |
|
|
//------------------------------------------------------------------------------ |
115 |
|
|
|
116 |
|
|
|
117 |
|
|
const uint64_t PosixCacheManager::kBigFile = 25 * 1024 * 1024; // 25M |
118 |
|
|
|
119 |
|
|
|
120 |
|
41 |
int PosixCacheManager::AbortTxn(void *txn) { |
121 |
|
41 |
Transaction *transaction = reinterpret_cast<Transaction *>(txn); |
122 |
|
41 |
LogCvmfs(kLogCache, kLogDebug, "abort %s", transaction->tmp_path.c_str()); |
123 |
|
41 |
close(transaction->fd); |
124 |
|
41 |
int result = unlink(transaction->tmp_path.c_str()); |
125 |
|
41 |
transaction->~Transaction(); |
126 |
|
41 |
atomic_dec32(&no_inflight_txns_); |
127 |
✓✓ |
41 |
if (result == -1) |
128 |
|
2 |
return -errno; |
129 |
|
39 |
return 0; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
|
133 |
|
|
/** |
134 |
|
|
* This should only be used to replace the default NoopQuotaManager by a |
135 |
|
|
* PosixQuotaManager. The cache manager takes the ownership of the passed |
136 |
|
|
* quota manager. |
137 |
|
|
*/ |
138 |
|
55 |
bool PosixCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) { |
139 |
✗✓ |
55 |
if (quota_mgr == NULL) |
140 |
|
|
return false; |
141 |
✓✗ |
55 |
delete quota_mgr_; |
142 |
|
55 |
quota_mgr_ = quota_mgr; |
143 |
|
55 |
return true; |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
|
147 |
|
140 |
int PosixCacheManager::Close(int fd) { |
148 |
|
140 |
int retval = close(fd); |
149 |
✓✓ |
140 |
if (retval != 0) |
150 |
|
1 |
return -errno; |
151 |
|
139 |
return 0; |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
|
155 |
|
176 |
int PosixCacheManager::CommitTxn(void *txn) { |
156 |
|
176 |
Transaction *transaction = reinterpret_cast<Transaction *>(txn); |
157 |
|
|
int result; |
158 |
|
|
LogCvmfs(kLogCache, kLogDebug, "commit %s %s", |
159 |
|
176 |
transaction->final_path.c_str(), transaction->tmp_path.c_str()); |
160 |
|
|
|
161 |
|
176 |
result = Flush(transaction); |
162 |
|
176 |
close(transaction->fd); |
163 |
✓✓ |
176 |
if (result < 0) { |
164 |
|
1 |
unlink(transaction->tmp_path.c_str()); |
165 |
|
1 |
transaction->~Transaction(); |
166 |
|
1 |
atomic_dec32(&no_inflight_txns_); |
167 |
|
1 |
return result; |
168 |
|
|
} |
169 |
|
|
|
170 |
|
|
// To support debugging, move files into quarantine on file size mismatch |
171 |
✓✓ |
175 |
if (transaction->size != transaction->expected_size) { |
172 |
|
|
// Allow size to be zero if alien cache, because hadoop-fuse-dfs returns |
173 |
|
|
// size zero for a while |
174 |
✓✓✗✓ ✗✗ |
56 |
if ( (transaction->expected_size != kSizeUnknown) && |
175 |
|
|
(reports_correct_filesize_ || (transaction->size != 0)) ) |
176 |
|
|
{ |
177 |
|
|
LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr, |
178 |
|
|
"size check failure for %s, expected %lu, got %lu", |
179 |
|
|
transaction->id.ToString().c_str(), |
180 |
|
9 |
transaction->expected_size, transaction->size); |
181 |
|
|
CopyPath2Path(transaction->tmp_path, |
182 |
|
9 |
cache_path_ + "/quarantaine/" + transaction->id.ToString()); |
183 |
|
9 |
unlink(transaction->tmp_path.c_str()); |
184 |
|
9 |
transaction->~Transaction(); |
185 |
|
9 |
atomic_dec32(&no_inflight_txns_); |
186 |
|
9 |
return -EIO; |
187 |
|
|
} |
188 |
|
|
} |
189 |
|
|
|
190 |
✓✓✓✓
|
166 |
if ((transaction->object_info.type == kTypePinned) || |
191 |
|
|
(transaction->object_info.type == kTypeCatalog)) |
192 |
|
|
{ |
193 |
|
|
bool retval = quota_mgr_->Pin( |
194 |
|
|
transaction->id, transaction->size, transaction->object_info.description, |
195 |
|
37 |
(transaction->object_info.type == kTypeCatalog)); |
196 |
✓✓ |
37 |
if (!retval) { |
197 |
|
|
LogCvmfs(kLogCache, kLogDebug, "commit failed: cannot pin %s", |
198 |
|
1 |
transaction->id.ToString().c_str()); |
199 |
|
1 |
unlink(transaction->tmp_path.c_str()); |
200 |
|
1 |
transaction->~Transaction(); |
201 |
|
1 |
atomic_dec32(&no_inflight_txns_); |
202 |
|
1 |
return -ENOSPC; |
203 |
|
|
} |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
// Move the temporary file into its final location |
207 |
✓✓ |
165 |
if (alien_cache_) { |
208 |
|
1 |
int retval = chmod(transaction->tmp_path.c_str(), 0660); |
209 |
✗✓ |
1 |
assert(retval == 0); |
210 |
|
|
} |
211 |
|
|
result = |
212 |
|
165 |
Rename(transaction->tmp_path.c_str(), transaction->final_path.c_str()); |
213 |
✓✓ |
165 |
if (result < 0) { |
214 |
|
2 |
LogCvmfs(kLogCache, kLogDebug, "commit failed: %s", strerror(errno)); |
215 |
|
2 |
unlink(transaction->tmp_path.c_str()); |
216 |
✓✗✓✓
|
2 |
if ((transaction->object_info.type == kTypePinned) || |
217 |
|
|
(transaction->object_info.type == kTypeCatalog)) |
218 |
|
|
{ |
219 |
|
1 |
quota_mgr_->Remove(transaction->id); |
220 |
|
|
} |
221 |
|
|
} else { |
222 |
|
|
// Success, inform quota manager |
223 |
✓✓ |
163 |
if (transaction->object_info.type == kTypeVolatile) { |
224 |
|
|
quota_mgr_->InsertVolatile(transaction->id, transaction->size, |
225 |
|
1 |
transaction->object_info.description); |
226 |
✓✓ |
162 |
} else if (transaction->object_info.type == kTypeRegular) { |
227 |
|
|
quota_mgr_->Insert(transaction->id, transaction->size, |
228 |
|
127 |
transaction->object_info.description); |
229 |
|
|
} |
230 |
|
|
} |
231 |
|
165 |
transaction->~Transaction(); |
232 |
|
165 |
atomic_dec32(&no_inflight_txns_); |
233 |
|
165 |
return result; |
234 |
|
|
} |
235 |
|
|
|
236 |
|
|
|
237 |
|
236 |
PosixCacheManager *PosixCacheManager::Create( |
238 |
|
|
const string &cache_path, |
239 |
|
|
const bool alien_cache, |
240 |
|
|
const RenameWorkarounds rename_workaround) |
241 |
|
|
{ |
242 |
|
|
UniquePtr<PosixCacheManager> cache_manager( |
243 |
|
236 |
new PosixCacheManager(cache_path, alien_cache)); |
244 |
✗✓ |
236 |
assert(cache_manager.IsValid()); |
245 |
|
|
|
246 |
|
236 |
cache_manager->rename_workaround_ = rename_workaround; |
247 |
✓✓ |
236 |
if (cache_manager->alien_cache_) { |
248 |
✓✓ |
40 |
if (!MakeCacheDirectories(cache_path, 0770)) { |
249 |
|
1 |
return NULL; |
250 |
|
|
} |
251 |
|
|
LogCvmfs(kLogCache, kLogDebug | kLogSyslog, |
252 |
|
39 |
"Cache directory structure created."); |
253 |
|
|
struct statfs cache_buf; |
254 |
|
39 |
int retval = statfs(cache_path.c_str(), &cache_buf); |
255 |
✓✗ |
39 |
if (retval == 0) { |
256 |
✗✗✓ |
39 |
switch (cache_buf.f_type) { |
257 |
|
|
case NFS_SUPER_MAGIC: |
258 |
|
|
cache_manager->rename_workaround_ = kRenameLink; |
259 |
|
|
LogCvmfs(kLogCache, kLogDebug | kLogSyslog, |
260 |
|
|
"Alien cache is on NFS."); |
261 |
|
|
break; |
262 |
|
|
case BEEGFS_SUPER_MAGIC: |
263 |
|
|
cache_manager->rename_workaround_ = kRenameSamedir; |
264 |
|
|
LogCvmfs(kLogCache, kLogDebug | kLogSyslog, |
265 |
|
|
"Alien cache is on BeeGFS."); |
266 |
|
|
break; |
267 |
|
|
} |
268 |
|
|
} |
269 |
|
|
} else { |
270 |
✓✓ |
196 |
if (!MakeCacheDirectories(cache_path, 0700)) |
271 |
|
1 |
return NULL; |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
// TODO(jblomer): we might not need to look anymore for cvmfs 2.0 relicts |
275 |
✓✓ |
234 |
if (FileExists(cache_path + "/cvmfscatalog.cache")) { |
276 |
|
|
LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr, |
277 |
|
1 |
"Not mounting on cvmfs 2.0.X cache"); |
278 |
|
1 |
return NULL; |
279 |
|
|
} |
280 |
|
|
|
281 |
|
233 |
return cache_manager.Release(); |
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
|
285 |
|
191 |
void PosixCacheManager::CtrlTxn( |
286 |
|
|
const ObjectInfo &object_info, |
287 |
|
|
const int flags, |
288 |
|
|
void *txn) |
289 |
|
|
{ |
290 |
|
191 |
Transaction *transaction = reinterpret_cast<Transaction *>(txn); |
291 |
|
191 |
transaction->object_info = object_info; |
292 |
|
191 |
} |
293 |
|
|
|
294 |
|
|
|
295 |
|
|
string PosixCacheManager::Describe() { |
296 |
|
|
return "Posix cache manager (cache directory: " + cache_path_ + ")\n"; |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
|
300 |
|
|
/** |
301 |
|
|
* Nothing to do, the kernel keeps the state of open file descriptors. Return |
302 |
|
|
* a dummy memory location. |
303 |
|
|
*/ |
304 |
|
1 |
void *PosixCacheManager::DoSaveState() { |
305 |
|
1 |
char *c = reinterpret_cast<char *>(smalloc(1)); |
306 |
|
1 |
*c = '\0'; |
307 |
|
1 |
return c; |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
|
311 |
|
1 |
bool PosixCacheManager::DoRestoreState(void *data) { |
312 |
✗✓ |
1 |
assert(data); |
313 |
|
1 |
char *c = reinterpret_cast<char *>(data); |
314 |
✗✓ |
1 |
assert(*c == '\0'); |
315 |
|
1 |
return true; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
|
319 |
|
1 |
bool PosixCacheManager::DoFreeState(void *data) { |
320 |
|
1 |
free(data); |
321 |
|
1 |
return true; |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
|
325 |
|
|
|
326 |
|
10 |
int PosixCacheManager::Dup(int fd) { |
327 |
|
10 |
int new_fd = dup(fd); |
328 |
✓✓ |
10 |
if (new_fd < 0) |
329 |
|
5 |
return -errno; |
330 |
|
5 |
return new_fd; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
|
334 |
|
364 |
int PosixCacheManager::Flush(Transaction *transaction) { |
335 |
✓✓ |
364 |
if (transaction->buf_pos == 0) |
336 |
|
100 |
return 0; |
337 |
|
|
int written = |
338 |
|
264 |
write(transaction->fd, transaction->buffer, transaction->buf_pos); |
339 |
✓✓ |
264 |
if (written < 0) |
340 |
|
3 |
return -errno; |
341 |
✗✓ |
261 |
if (static_cast<unsigned>(written) != transaction->buf_pos) { |
342 |
|
|
transaction->buf_pos -= written; |
343 |
|
|
return -EIO; |
344 |
|
|
} |
345 |
|
261 |
transaction->buf_pos = 0; |
346 |
|
261 |
return 0; |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
|
350 |
|
508 |
inline string PosixCacheManager::GetPathInCache(const shash::Any &id) { |
351 |
|
508 |
return cache_path_ + "/" + id.MakePathWithoutSuffix(); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
|
355 |
|
88 |
int64_t PosixCacheManager::GetSize(int fd) { |
356 |
|
|
platform_stat64 info; |
357 |
|
88 |
int retval = platform_fstat(fd, &info); |
358 |
✓✓ |
88 |
if (retval != 0) |
359 |
|
1 |
return -errno; |
360 |
|
87 |
return info.st_size; |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
|
364 |
|
286 |
int PosixCacheManager::Open(const BlessedObject &object) { |
365 |
|
286 |
const string path = GetPathInCache(object.id); |
366 |
|
286 |
int result = open(path.c_str(), O_RDONLY); |
367 |
|
|
|
368 |
✓✓ |
286 |
if (result >= 0) { |
369 |
|
69 |
LogCvmfs(kLogCache, kLogDebug, "hit %s", path.c_str()); |
370 |
|
|
// platform_disable_kcache(result); |
371 |
|
69 |
quota_mgr_->Touch(object.id); |
372 |
|
|
} else { |
373 |
|
217 |
result = -errno; |
374 |
|
217 |
LogCvmfs(kLogCache, kLogDebug, "miss %s (%d)", path.c_str(), result); |
375 |
|
|
} |
376 |
|
286 |
return result; |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
|
380 |
|
67 |
int PosixCacheManager::OpenFromTxn(void *txn) { |
381 |
|
67 |
Transaction *transaction = reinterpret_cast<Transaction *>(txn); |
382 |
|
67 |
int retval = Flush(transaction); |
383 |
✓✓ |
67 |
if (retval < 0) |
384 |
|
1 |
return retval; |
385 |
|
66 |
int fd_rdonly = open(transaction->tmp_path.c_str(), O_RDONLY); |
386 |
✓✓ |
66 |
if (fd_rdonly == -1) |
387 |
|
1 |
return -errno; |
388 |
|
65 |
return fd_rdonly; |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
|
392 |
|
743 |
int64_t PosixCacheManager::Pread( |
393 |
|
|
int fd, |
394 |
|
|
void *buf, |
395 |
|
|
uint64_t size, |
396 |
|
|
uint64_t offset) |
397 |
|
|
{ |
398 |
|
|
int64_t result; |
399 |
✓✓✗✓ ✗✓ |
743 |
do { |
400 |
|
743 |
errno = 0; |
401 |
|
743 |
result = pread(fd, buf, size, offset); |
402 |
|
|
} while ((result == -1) && (errno == EINTR)); |
403 |
✓✓ |
743 |
if (result < 0) |
404 |
|
2 |
return -errno; |
405 |
|
741 |
return result; |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
|
409 |
|
171 |
int PosixCacheManager::Rename(const char *oldpath, const char *newpath) { |
410 |
|
|
int result; |
411 |
✓✓ |
171 |
if (rename_workaround_ != kRenameLink) { |
412 |
|
168 |
result = rename(oldpath, newpath); |
413 |
✓✓ |
168 |
if (result < 0) |
414 |
|
3 |
return -errno; |
415 |
|
165 |
return 0; |
416 |
|
|
} |
417 |
|
|
|
418 |
|
3 |
result = link(oldpath, newpath); |
419 |
✓✓ |
3 |
if (result < 0) { |
420 |
✓✓ |
2 |
if (errno == EEXIST) |
421 |
|
1 |
LogCvmfs(kLogCache, kLogDebug, "%s already existed, ignoring", newpath); |
422 |
|
|
else |
423 |
|
1 |
return -errno; |
424 |
|
|
} |
425 |
|
2 |
result = unlink(oldpath); |
426 |
✗✓ |
2 |
if (result < 0) |
427 |
|
|
return -errno; |
428 |
|
2 |
return 0; |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
|
432 |
|
|
/** |
433 |
|
|
* Used by the sqlite vfs in order to preload file catalogs into the file system |
434 |
|
|
* buffers. |
435 |
|
|
*/ |
436 |
|
52 |
int PosixCacheManager::Readahead(int fd) { |
437 |
|
|
unsigned char *buf[4096]; |
438 |
|
|
int nbytes; |
439 |
|
52 |
uint64_t pos = 0; |
440 |
✓✓ |
244 |
do { |
441 |
|
244 |
nbytes = Pread(fd, buf, 4096, pos); |
442 |
|
244 |
pos += nbytes; |
443 |
|
|
} while (nbytes == 4096); |
444 |
|
52 |
LogCvmfs(kLogCache, kLogDebug, "read-ahead %d, %" PRIu64, fd, pos); |
445 |
✗✓ |
52 |
if (nbytes < 0) |
446 |
|
|
return nbytes; |
447 |
|
52 |
return 0; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
|
451 |
|
5 |
int PosixCacheManager::Reset(void *txn) { |
452 |
|
5 |
Transaction *transaction = reinterpret_cast<Transaction *>(txn); |
453 |
|
5 |
transaction->buf_pos = 0; |
454 |
|
5 |
transaction->size = 0; |
455 |
|
5 |
int retval = lseek(transaction->fd, 0, SEEK_SET); |
456 |
✓✓ |
5 |
if (retval < 0) |
457 |
|
1 |
return -errno; |
458 |
|
4 |
retval = ftruncate(transaction->fd, 0); |
459 |
✗✓ |
4 |
if (retval < 0) |
460 |
|
|
return -errno; |
461 |
|
4 |
return 0; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
|
465 |
|
224 |
int PosixCacheManager::StartTxn( |
466 |
|
|
const shash::Any &id, |
467 |
|
|
uint64_t size, |
468 |
|
|
void *txn) |
469 |
|
|
{ |
470 |
|
224 |
atomic_inc32(&no_inflight_txns_); |
471 |
✓✓ |
224 |
if (cache_mode_ == kCacheReadOnly) { |
472 |
|
1 |
atomic_dec32(&no_inflight_txns_); |
473 |
|
1 |
return -EROFS; |
474 |
|
|
} |
475 |
|
|
|
476 |
✓✓ |
223 |
if (size != kSizeUnknown) { |
477 |
✓✓ |
144 |
if (size > quota_mgr_->GetMaxFileSize()) { |
478 |
|
|
LogCvmfs(kLogCache, kLogDebug, "file too big for lru cache (%" PRIu64 " " |
479 |
|
|
"requested but only %" PRIu64 " bytes free)", |
480 |
|
1 |
size, quota_mgr_->GetMaxFileSize()); |
481 |
|
1 |
atomic_dec32(&no_inflight_txns_); |
482 |
|
1 |
return -ENOSPC; |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
// For large files, ensure enough free cache space before writing the chunk |
486 |
✓✓ |
143 |
if (size > kBigFile) { |
487 |
|
1 |
uint64_t cache_size = quota_mgr_->GetSize(); |
488 |
|
1 |
uint64_t cache_capacity = quota_mgr_->GetCapacity(); |
489 |
✗✓ |
1 |
assert(cache_capacity >= size); |
490 |
✓✗ |
1 |
if ((cache_size + size) > cache_capacity) { |
491 |
|
|
uint64_t leave_size = |
492 |
|
1 |
std::min(cache_capacity / 2, cache_capacity - size); |
493 |
|
1 |
quota_mgr_->Cleanup(leave_size); |
494 |
|
|
} |
495 |
|
|
} |
496 |
|
|
} |
497 |
|
|
|
498 |
|
222 |
string path_in_cache = GetPathInCache(id); |
499 |
✓✗ |
222 |
Transaction *transaction = new (txn) Transaction(id, path_in_cache); |
500 |
|
|
|
501 |
|
222 |
char *template_path = NULL; |
502 |
|
222 |
unsigned temp_path_len = 0; |
503 |
✓✓ |
222 |
if (rename_workaround_ == kRenameSamedir) { |
504 |
|
2 |
temp_path_len = path_in_cache.length() + 6; |
505 |
|
2 |
template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1)); |
506 |
|
2 |
memcpy(template_path, path_in_cache.data(), path_in_cache.length()); |
507 |
|
2 |
memset(template_path + path_in_cache.length(), 'X', 6); |
508 |
|
|
} else { |
509 |
|
220 |
temp_path_len = txn_template_path_.length(); |
510 |
|
220 |
template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1)); |
511 |
|
220 |
memcpy(template_path, &txn_template_path_[0], temp_path_len); |
512 |
|
|
} |
513 |
|
222 |
template_path[temp_path_len] = '\0'; |
514 |
|
|
|
515 |
|
222 |
transaction->fd = mkstemp(template_path); |
516 |
✓✓ |
222 |
if (transaction->fd == -1) { |
517 |
|
5 |
transaction->~Transaction(); |
518 |
|
5 |
atomic_dec32(&no_inflight_txns_); |
519 |
|
5 |
return -errno; |
520 |
|
|
} |
521 |
|
|
|
522 |
|
|
LogCvmfs(kLogCache, kLogDebug, "start transaction on %s has result %d", |
523 |
|
217 |
template_path, transaction->fd); |
524 |
|
217 |
transaction->tmp_path = template_path; |
525 |
|
217 |
transaction->expected_size = size; |
526 |
|
217 |
return transaction->fd; |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
|
530 |
|
4 |
void PosixCacheManager::TearDown2ReadOnly() { |
531 |
|
4 |
cache_mode_ = kCacheReadOnly; |
532 |
✓✓ |
22 |
while (atomic_read32(&no_inflight_txns_) != 0) |
533 |
|
15 |
SafeSleepMs(50); |
534 |
|
|
|
535 |
|
3 |
QuotaManager *old_manager = quota_mgr_; |
536 |
|
3 |
quota_mgr_ = new NoopQuotaManager(); |
537 |
✓✗ |
3 |
delete old_manager; |
538 |
|
3 |
} |
539 |
|
|
|
540 |
|
|
|
541 |
|
208 |
int64_t PosixCacheManager::Write(const void *buf, uint64_t size, void *txn) { |
542 |
|
208 |
Transaction *transaction = reinterpret_cast<Transaction *>(txn); |
543 |
|
|
|
544 |
✓✓ |
208 |
if (transaction->expected_size != kSizeUnknown) { |
545 |
✓✓ |
132 |
if (transaction->size + size > transaction->expected_size) { |
546 |
|
|
LogCvmfs(kLogCache, kLogDebug, |
547 |
|
|
"Transaction size (%" PRIu64 ") > expected size (%" PRIu64 ")", |
548 |
|
1 |
transaction->size + size, transaction->expected_size); |
549 |
|
1 |
return -EFBIG; |
550 |
|
|
} |
551 |
|
|
} |
552 |
|
|
|
553 |
|
207 |
uint64_t written = 0; |
554 |
|
207 |
const unsigned char *read_pos = reinterpret_cast<const unsigned char *>(buf); |
555 |
✓✓ |
680 |
while (written < size) { |
556 |
✓✓ |
267 |
if (transaction->buf_pos == sizeof(transaction->buffer)) { |
557 |
|
121 |
int retval = Flush(transaction); |
558 |
✓✓ |
121 |
if (retval != 0) { |
559 |
|
1 |
transaction->size += written; |
560 |
|
1 |
return retval; |
561 |
|
|
} |
562 |
|
|
} |
563 |
|
266 |
uint64_t remaining = size - written; |
564 |
|
|
uint64_t space_in_buffer = |
565 |
|
266 |
sizeof(transaction->buffer) - transaction->buf_pos; |
566 |
|
266 |
uint64_t batch_size = std::min(remaining, space_in_buffer); |
567 |
|
266 |
memcpy(transaction->buffer + transaction->buf_pos, read_pos, batch_size); |
568 |
|
266 |
transaction->buf_pos += batch_size; |
569 |
|
266 |
written += batch_size; |
570 |
|
266 |
read_pos += batch_size; |
571 |
|
|
} |
572 |
|
206 |
transaction->size += written; |
573 |
|
206 |
return written; |
574 |
|
|
} |