| Line |
Branch |
Exec |
Source |
| 1 |
|
|
/** |
| 2 |
|
|
* This file is part of the CernVM File System. |
| 3 |
|
|
*/ |
| 4 |
|
|
|
| 5 |
|
|
#define _FILE_OFFSET_BITS 64 |
| 6 |
|
|
#define __STDC_FORMAT_MACROS |
| 7 |
|
|
|
| 8 |
|
|
|
| 9 |
|
|
#include "libcvmfs_cache.h" |
| 10 |
|
|
|
| 11 |
|
|
#include <unistd.h> |
| 12 |
|
|
|
| 13 |
|
|
#include <cassert> |
| 14 |
|
|
#include <cstdlib> |
| 15 |
|
|
#include <cstring> |
| 16 |
|
|
#include <string> |
| 17 |
|
|
|
| 18 |
|
|
#include "cache_plugin/channel.h" |
| 19 |
|
|
#include "cache_transport.h" |
| 20 |
|
|
#include "crypto/hash.h" |
| 21 |
|
|
#include "manifest.h" |
| 22 |
|
|
#include "monitor.h" |
| 23 |
|
|
#include "util/pointer.h" |
| 24 |
|
|
|
| 25 |
|
|
using namespace std; // NOLINT |
| 26 |
|
|
|
| 27 |
|
|
namespace { |
| 28 |
|
|
|
| 29 |
|
✗ |
static shash::Any Chash2Cpphash(const struct cvmcache_hash *h) { |
| 30 |
|
✗ |
shash::Any hash; |
| 31 |
|
✗ |
memcpy(hash.digest, h->digest, sizeof(h->digest)); |
| 32 |
|
✗ |
hash.algorithm = static_cast<shash::Algorithms>(h->algorithm); |
| 33 |
|
✗ |
return hash; |
| 34 |
|
|
} |
| 35 |
|
|
|
| 36 |
|
✗ |
static struct cvmcache_hash Cpphash2Chash(const shash::Any &hash) { |
| 37 |
|
|
struct cvmcache_hash h; |
| 38 |
|
✗ |
memcpy(h.digest, hash.digest, sizeof(h.digest)); |
| 39 |
|
✗ |
h.algorithm = hash.algorithm; |
| 40 |
|
✗ |
return h; |
| 41 |
|
|
} |
| 42 |
|
|
|
| 43 |
|
✗ |
static enum cvmcache_object_type ObjectType2CType(cvmfs::EnumObjectType type) { |
| 44 |
|
✗ |
switch (type) { |
| 45 |
|
✗ |
case cvmfs::OBJECT_REGULAR: |
| 46 |
|
✗ |
return CVMCACHE_OBJECT_REGULAR; |
| 47 |
|
✗ |
case cvmfs::OBJECT_CATALOG: |
| 48 |
|
✗ |
return CVMCACHE_OBJECT_CATALOG; |
| 49 |
|
✗ |
case cvmfs::OBJECT_VOLATILE: |
| 50 |
|
✗ |
return CVMCACHE_OBJECT_VOLATILE; |
| 51 |
|
|
} |
| 52 |
|
✗ |
abort(); |
| 53 |
|
|
} |
| 54 |
|
|
|
| 55 |
|
|
class ForwardCachePlugin : public CachePlugin { |
| 56 |
|
|
public: |
| 57 |
|
✗ |
explicit ForwardCachePlugin(struct cvmcache_callbacks *callbacks) |
| 58 |
|
✗ |
: CachePlugin(callbacks->capabilities), callbacks_(*callbacks) { |
| 59 |
|
✗ |
assert(callbacks->cvmcache_chrefcnt != NULL); |
| 60 |
|
✗ |
assert(callbacks->cvmcache_obj_info != NULL); |
| 61 |
|
✗ |
assert(callbacks->cvmcache_pread != NULL); |
| 62 |
|
✗ |
if (callbacks->capabilities & CVMCACHE_CAP_WRITE) { |
| 63 |
|
✗ |
assert(callbacks->cvmcache_start_txn != NULL); |
| 64 |
|
✗ |
assert(callbacks->cvmcache_write_txn != NULL); |
| 65 |
|
✗ |
assert(callbacks->cvmcache_commit_txn != NULL); |
| 66 |
|
✗ |
assert(callbacks->cvmcache_abort_txn != NULL); |
| 67 |
|
|
} |
| 68 |
|
✗ |
if (callbacks->capabilities & CVMCACHE_CAP_INFO) |
| 69 |
|
✗ |
assert(callbacks->cvmcache_info != NULL); |
| 70 |
|
✗ |
if (callbacks->capabilities & CVMCACHE_CAP_SHRINK_RATE) |
| 71 |
|
✗ |
assert(callbacks->capabilities & CVMCACHE_CAP_INFO); |
| 72 |
|
✗ |
if (callbacks->capabilities & CVMCACHE_CAP_SHRINK) |
| 73 |
|
✗ |
assert(callbacks->cvmcache_shrink != NULL); |
| 74 |
|
✗ |
if (callbacks->capabilities & CVMCACHE_CAP_LIST) { |
| 75 |
|
✗ |
assert(callbacks->cvmcache_listing_begin != NULL); |
| 76 |
|
✗ |
assert(callbacks->cvmcache_listing_next != NULL); |
| 77 |
|
✗ |
assert(callbacks->cvmcache_listing_end != NULL); |
| 78 |
|
|
} |
| 79 |
|
✗ |
if (callbacks->capabilities & CVMCACHE_CAP_BREADCRUMB) { |
| 80 |
|
✗ |
assert(callbacks->cvmcache_breadcrumb_store != NULL); |
| 81 |
|
✗ |
assert(callbacks->cvmcache_breadcrumb_load != NULL); |
| 82 |
|
|
} |
| 83 |
|
|
} |
| 84 |
|
✗ |
virtual ~ForwardCachePlugin() { } |
| 85 |
|
|
|
| 86 |
|
|
protected: |
| 87 |
|
✗ |
virtual cvmfs::EnumStatus ChangeRefcount(const shash::Any &id, |
| 88 |
|
|
int32_t change_by) { |
| 89 |
|
✗ |
struct cvmcache_hash c_hash = Cpphash2Chash(id); |
| 90 |
|
✗ |
const int result = callbacks_.cvmcache_chrefcnt(&c_hash, change_by); |
| 91 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 92 |
|
|
} |
| 93 |
|
|
|
| 94 |
|
✗ |
virtual cvmfs::EnumStatus GetObjectInfo(const shash::Any &id, |
| 95 |
|
|
ObjectInfo *info) { |
| 96 |
|
✗ |
struct cvmcache_hash c_hash = Cpphash2Chash(id); |
| 97 |
|
|
cvmcache_object_info c_info; |
| 98 |
|
✗ |
memset(&c_info, 0, sizeof(c_info)); |
| 99 |
|
✗ |
c_info.size = CachePlugin::kSizeUnknown; |
| 100 |
|
✗ |
const int result = callbacks_.cvmcache_obj_info(&c_hash, &c_info); |
| 101 |
|
✗ |
info->size = c_info.size; |
| 102 |
|
✗ |
info->object_type = static_cast<cvmfs::EnumObjectType>(c_info.type); |
| 103 |
|
✗ |
info->pinned = c_info.pinned; |
| 104 |
|
✗ |
if (c_info.description) { |
| 105 |
|
✗ |
info->description = string(c_info.description); |
| 106 |
|
✗ |
free(c_info.description); |
| 107 |
|
|
} |
| 108 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 109 |
|
|
} |
| 110 |
|
|
|
| 111 |
|
✗ |
virtual cvmfs::EnumStatus Pread(const shash::Any &id, |
| 112 |
|
|
uint64_t offset, |
| 113 |
|
|
uint32_t *size, |
| 114 |
|
|
unsigned char *buffer) { |
| 115 |
|
✗ |
struct cvmcache_hash c_hash = Cpphash2Chash(id); |
| 116 |
|
✗ |
const int result = callbacks_.cvmcache_pread(&c_hash, offset, size, buffer); |
| 117 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 118 |
|
|
} |
| 119 |
|
|
|
| 120 |
|
✗ |
virtual cvmfs::EnumStatus StartTxn(const shash::Any &id, |
| 121 |
|
|
const uint64_t txn_id, |
| 122 |
|
|
const ObjectInfo &info) { |
| 123 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_WRITE)) |
| 124 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 125 |
|
|
|
| 126 |
|
✗ |
struct cvmcache_hash c_hash = Cpphash2Chash(id); |
| 127 |
|
|
cvmcache_object_info c_info; |
| 128 |
|
✗ |
memset(&c_info, 0, sizeof(c_info)); |
| 129 |
|
✗ |
c_info.size = info.size; |
| 130 |
|
✗ |
c_info.type = ObjectType2CType(info.object_type); |
| 131 |
|
✗ |
if (info.description.empty()) { |
| 132 |
|
✗ |
c_info.description = NULL; |
| 133 |
|
|
} else { |
| 134 |
|
✗ |
c_info.description = strdup(info.description.c_str()); |
| 135 |
|
|
} |
| 136 |
|
✗ |
const int result = callbacks_.cvmcache_start_txn(&c_hash, txn_id, &c_info); |
| 137 |
|
✗ |
free(c_info.description); |
| 138 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 139 |
|
|
} |
| 140 |
|
|
|
| 141 |
|
✗ |
virtual cvmfs::EnumStatus WriteTxn(const uint64_t txn_id, |
| 142 |
|
|
unsigned char *buffer, |
| 143 |
|
|
uint32_t size) { |
| 144 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_WRITE)) |
| 145 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 146 |
|
|
|
| 147 |
|
✗ |
const int result = callbacks_.cvmcache_write_txn(txn_id, buffer, size); |
| 148 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 149 |
|
|
} |
| 150 |
|
|
|
| 151 |
|
✗ |
virtual cvmfs::EnumStatus CommitTxn(const uint64_t txn_id) { |
| 152 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_WRITE)) |
| 153 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 154 |
|
|
|
| 155 |
|
✗ |
const int result = callbacks_.cvmcache_commit_txn(txn_id); |
| 156 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 157 |
|
|
} |
| 158 |
|
|
|
| 159 |
|
✗ |
virtual cvmfs::EnumStatus AbortTxn(const uint64_t txn_id) { |
| 160 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_WRITE)) |
| 161 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 162 |
|
|
|
| 163 |
|
✗ |
const int result = callbacks_.cvmcache_abort_txn(txn_id); |
| 164 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 165 |
|
|
} |
| 166 |
|
|
|
| 167 |
|
✗ |
virtual cvmfs::EnumStatus GetInfo(Info *info) { |
| 168 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_INFO)) |
| 169 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 170 |
|
|
|
| 171 |
|
|
cvmcache_info c_info; |
| 172 |
|
✗ |
c_info.size_bytes = info->size_bytes; |
| 173 |
|
✗ |
c_info.used_bytes = info->used_bytes; |
| 174 |
|
✗ |
c_info.pinned_bytes = info->pinned_bytes; |
| 175 |
|
✗ |
c_info.no_shrink = info->no_shrink; |
| 176 |
|
✗ |
const int result = callbacks_.cvmcache_info(&c_info); |
| 177 |
|
✗ |
if (result == CVMCACHE_STATUS_OK) { |
| 178 |
|
✗ |
info->size_bytes = c_info.size_bytes; |
| 179 |
|
✗ |
info->used_bytes = c_info.used_bytes; |
| 180 |
|
✗ |
info->pinned_bytes = c_info.pinned_bytes; |
| 181 |
|
✗ |
info->no_shrink = c_info.no_shrink; |
| 182 |
|
|
} |
| 183 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 184 |
|
|
} |
| 185 |
|
|
|
| 186 |
|
✗ |
virtual cvmfs::EnumStatus Shrink(uint64_t shrink_to, uint64_t *used) { |
| 187 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_SHRINK)) |
| 188 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 189 |
|
|
|
| 190 |
|
✗ |
const int result = callbacks_.cvmcache_shrink(shrink_to, used); |
| 191 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 192 |
|
|
} |
| 193 |
|
|
|
| 194 |
|
✗ |
virtual cvmfs::EnumStatus ListingBegin(uint64_t lst_id, |
| 195 |
|
|
cvmfs::EnumObjectType type) { |
| 196 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_LIST)) |
| 197 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 198 |
|
|
|
| 199 |
|
✗ |
const int result = callbacks_.cvmcache_listing_begin( |
| 200 |
|
|
lst_id, ObjectType2CType(type)); |
| 201 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 202 |
|
|
} |
| 203 |
|
|
|
| 204 |
|
✗ |
virtual cvmfs::EnumStatus ListingNext(int64_t lst_id, ObjectInfo *item) { |
| 205 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_LIST)) |
| 206 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 207 |
|
|
|
| 208 |
|
|
struct cvmcache_object_info c_item; |
| 209 |
|
✗ |
memset(&c_item, 0, sizeof(c_item)); |
| 210 |
|
✗ |
const int result = callbacks_.cvmcache_listing_next(lst_id, &c_item); |
| 211 |
|
✗ |
if (result == CVMCACHE_STATUS_OK) { |
| 212 |
|
✗ |
item->id = Chash2Cpphash(&c_item.id); |
| 213 |
|
✗ |
item->size = c_item.size; |
| 214 |
|
✗ |
item->object_type = static_cast<cvmfs::EnumObjectType>(c_item.type); |
| 215 |
|
✗ |
item->pinned = c_item.pinned; |
| 216 |
|
✗ |
if (c_item.description) { |
| 217 |
|
✗ |
item->description = string(c_item.description); |
| 218 |
|
✗ |
free(c_item.description); |
| 219 |
|
|
} |
| 220 |
|
|
} |
| 221 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 222 |
|
|
} |
| 223 |
|
|
|
| 224 |
|
✗ |
virtual cvmfs::EnumStatus ListingEnd(int64_t lst_id) { |
| 225 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_LIST)) |
| 226 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 227 |
|
|
|
| 228 |
|
✗ |
const int result = callbacks_.cvmcache_listing_end(lst_id); |
| 229 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 230 |
|
|
} |
| 231 |
|
|
|
| 232 |
|
✗ |
virtual cvmfs::EnumStatus LoadBreadcrumb(const std::string &fqrn, |
| 233 |
|
|
manifest::Breadcrumb *breadcrumb) { |
| 234 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_BREADCRUMB)) |
| 235 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 236 |
|
|
|
| 237 |
|
|
cvmcache_breadcrumb c_breadcrumb; |
| 238 |
|
✗ |
const int result = callbacks_.cvmcache_breadcrumb_load(fqrn.c_str(), |
| 239 |
|
|
&c_breadcrumb); |
| 240 |
|
✗ |
if (result == CVMCACHE_STATUS_OK) { |
| 241 |
|
✗ |
breadcrumb->catalog_hash = Chash2Cpphash(&c_breadcrumb.catalog_hash); |
| 242 |
|
✗ |
breadcrumb->timestamp = c_breadcrumb.timestamp; |
| 243 |
|
✗ |
breadcrumb->revision = c_breadcrumb.revision; |
| 244 |
|
|
} |
| 245 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 246 |
|
|
} |
| 247 |
|
|
|
| 248 |
|
✗ |
virtual cvmfs::EnumStatus StoreBreadcrumb( |
| 249 |
|
|
const std::string &fqrn, const manifest::Breadcrumb &breadcrumb) { |
| 250 |
|
✗ |
if (!(callbacks_.capabilities & CVMCACHE_CAP_BREADCRUMB)) |
| 251 |
|
✗ |
return cvmfs::STATUS_NOSUPPORT; |
| 252 |
|
|
|
| 253 |
|
|
cvmcache_breadcrumb c_breadcrumb; |
| 254 |
|
✗ |
c_breadcrumb.catalog_hash = Cpphash2Chash(breadcrumb.catalog_hash); |
| 255 |
|
✗ |
c_breadcrumb.timestamp = breadcrumb.timestamp; |
| 256 |
|
✗ |
c_breadcrumb.revision = breadcrumb.revision; |
| 257 |
|
✗ |
const int result = callbacks_.cvmcache_breadcrumb_store(fqrn.c_str(), |
| 258 |
|
|
&c_breadcrumb); |
| 259 |
|
✗ |
return static_cast<cvmfs::EnumStatus>(result); |
| 260 |
|
|
} |
| 261 |
|
|
|
| 262 |
|
|
private: |
| 263 |
|
|
struct cvmcache_callbacks callbacks_; |
| 264 |
|
|
}; |
| 265 |
|
|
|
| 266 |
|
|
Watchdog *g_watchdog = NULL; |
| 267 |
|
|
|
| 268 |
|
|
} // anonymous namespace |
| 269 |
|
|
|
| 270 |
|
|
|
| 271 |
|
|
struct cvmcache_context { |
| 272 |
|
✗ |
explicit cvmcache_context(ForwardCachePlugin *p) : plugin(p) { } |
| 273 |
|
|
UniquePtr<ForwardCachePlugin> plugin; |
| 274 |
|
|
}; |
| 275 |
|
|
|
| 276 |
|
|
|
| 277 |
|
✗ |
int cvmcache_hash_cmp(struct cvmcache_hash *a, struct cvmcache_hash *b) { |
| 278 |
|
✗ |
const shash::Any hash_a = Chash2Cpphash(a); |
| 279 |
|
✗ |
const shash::Any hash_b = Chash2Cpphash(b); |
| 280 |
|
✗ |
if (hash_a < hash_b) |
| 281 |
|
✗ |
return -1; |
| 282 |
|
✗ |
else if (hash_a == hash_b) |
| 283 |
|
✗ |
return 0; |
| 284 |
|
|
else |
| 285 |
|
✗ |
return 1; |
| 286 |
|
|
} |
| 287 |
|
|
|
| 288 |
|
✗ |
char *cvmcache_hash_print(const struct cvmcache_hash *h) { |
| 289 |
|
✗ |
const shash::Any hash = Chash2Cpphash(h); |
| 290 |
|
✗ |
return strdup(hash.ToString().c_str()); |
| 291 |
|
|
} |
| 292 |
|
|
|
| 293 |
|
|
|
| 294 |
|
✗ |
void cvmcache_init_global() { } |
| 295 |
|
|
|
| 296 |
|
|
|
| 297 |
|
✗ |
void cvmcache_cleanup_global() { } |
| 298 |
|
|
|
| 299 |
|
✗ |
int cvmcache_is_supervised() { |
| 300 |
|
✗ |
return getenv(CacheTransport::kEnvReadyNotifyFd) != NULL; |
| 301 |
|
|
} |
| 302 |
|
|
|
| 303 |
|
✗ |
struct cvmcache_context *cvmcache_init(struct cvmcache_callbacks *callbacks) { |
| 304 |
|
✗ |
return new cvmcache_context(new ForwardCachePlugin(callbacks)); |
| 305 |
|
|
} |
| 306 |
|
|
|
| 307 |
|
✗ |
int cvmcache_listen(struct cvmcache_context *ctx, char *locator) { |
| 308 |
|
✗ |
return ctx->plugin->Listen(locator); |
| 309 |
|
|
} |
| 310 |
|
|
|
| 311 |
|
✗ |
void cvmcache_process_requests(struct cvmcache_context *ctx, |
| 312 |
|
|
unsigned nworkers) { |
| 313 |
|
✗ |
ctx->plugin->ProcessRequests(nworkers); |
| 314 |
|
|
} |
| 315 |
|
|
|
| 316 |
|
✗ |
void cvmcache_ask_detach(struct cvmcache_context *ctx) { |
| 317 |
|
✗ |
ctx->plugin->AskToDetach(); |
| 318 |
|
|
} |
| 319 |
|
|
|
| 320 |
|
✗ |
void cvmcache_wait_for(struct cvmcache_context *ctx) { |
| 321 |
|
✗ |
ctx->plugin->WaitFor(); |
| 322 |
|
✗ |
delete ctx; |
| 323 |
|
|
} |
| 324 |
|
|
|
| 325 |
|
✗ |
void cvmcache_terminate(struct cvmcache_context *ctx) { |
| 326 |
|
✗ |
ctx->plugin->Terminate(); |
| 327 |
|
|
} |
| 328 |
|
|
|
| 329 |
|
✗ |
uint32_t cvmcache_max_object_size(struct cvmcache_context *ctx) { |
| 330 |
|
✗ |
return ctx->plugin->max_object_size(); |
| 331 |
|
|
} |
| 332 |
|
|
|
| 333 |
|
✗ |
void cvmcache_get_session(cvmcache_session *session) { |
| 334 |
|
✗ |
assert(session != NULL); |
| 335 |
|
✗ |
SessionCtx *session_ctx = SessionCtx::GetInstance(); |
| 336 |
|
✗ |
assert(session_ctx); |
| 337 |
|
✗ |
session_ctx->Get( |
| 338 |
|
|
&(session->id), &(session->repository_name), &(session->client_instance)); |
| 339 |
|
|
} |
| 340 |
|
|
|
| 341 |
|
✗ |
void cvmcache_spawn_watchdog(const char *crash_dump_file) { |
| 342 |
|
✗ |
if (g_watchdog != NULL) |
| 343 |
|
✗ |
return; |
| 344 |
|
✗ |
g_watchdog = Watchdog::Create(NULL); |
| 345 |
|
✗ |
assert(g_watchdog != NULL); |
| 346 |
|
✗ |
g_watchdog->Spawn((crash_dump_file != NULL) ? string(crash_dump_file) : ""); |
| 347 |
|
|
} |
| 348 |
|
|
|
| 349 |
|
✗ |
void cvmcache_terminate_watchdog() { |
| 350 |
|
✗ |
delete g_watchdog; |
| 351 |
|
✗ |
g_watchdog = NULL; |
| 352 |
|
|
} |
| 353 |
|
|
|