sslbd/diskcache.cpp
#include "diskcache.hpp"
#include <fcntl.h>
DiskCache::DiskCache(const char* path) {
dirfd = open(path, O_RDONLY|O_DIRECTORY);
if (dirfd == -1) {
log_errno(error, "open(%s, O_DIRECTORY)", path);
}
}
DiskCache::~DiskCache() {
if (dirfd != -1) close(dirfd);
}
void DiskCache::id2fn(const char* id, char*& tmp, char*& fn) {
tmp = buf[0];
fn = buf[1];
cachekey_t h = hash(id);
hash2str(h, fn);
hash2str(h, tmp);
strcpy(tmp + CACHEKEY_HEX_LEN, ".tmp");
}
ssize_t DiskCache::get(const char* id, char*& b) {
unless (is_valid()) return -1;
b = NULL;
const size_t id_len = strlen(id);
if (id_len > DISKCACHE_MAX_ID_LEN) return -1;
char* fn;
char* tmp;
id2fn(id, tmp, fn);
int fd = openat(dirfd, fn, O_RDONLY);
if (fd == -1) {
log_errno(debug, "openat(%d, %s)", dirfd, fn);
return -1;
}
off_t size = file_size(fd);
if (size <= (off_t)sizeof(diskcache_header_t)) {
log(error, "truncated header, deleting '%s' (%s)", fn, id);
if (unlinkat(dirfd, fn, 0) == -1) {
log_errno(notice, "unlinkat(%d, %s)", dirfd, fn);
}
EINTR_RETRY(close(fd));
return -1;
}
size -= sizeof(diskcache_header_t);
diskcache_header_t header;
if ((read(fd, &header, sizeof(header)) != sizeof(header)) || header.version != DISKCACHE_VERSION) {
log(error, "cannot validate header, deleting '%s' (%s)", fn, id);
if (unlinkat(dirfd, fn, 0) == -1) {
log_errno(notice, "unlinkat(%d, %s)", dirfd, fn);
}
EINTR_RETRY(close(fd));
return -1;
}
if (memcmp(id, header.id, id_len) != 0) {
log(notice, "id mismatch/collision for '%s' (%s)", fn, id);
EINTR_RETRY(close(fd));
return -1;
}
b = (char*)pool.pop((size_t)size+1);
if (read(fd, b, size) != size) {
log_errno(notice, "cannot read from '%s' (%s)", fn, id); // errno might be incorrect
pool.push(b);
b = NULL;
EINTR_RETRY(close(fd));
return -1;
}
b[size] = '\0';
log(info, "recalled %s for %s", fn, id);
EINTR_RETRY(close(fd));
return size;
}
bool DiskCache::set(const char* id, const iovec* iov, int iovcnt, size_t len) {
unless (is_valid()) return false;
if (strlen(id) > DISKCACHE_MAX_ID_LEN) return false;
char* fn;
char* tmp;
id2fn(id, tmp, fn);
int fd = openat(dirfd, tmp, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
if (fd == -1) {
log_errno(info, "openat(%d, %s)", dirfd, tmp);
return false;
}
diskcache_header_t header;
header.version = DISKCACHE_VERSION;
header.time = NOW;
strncpy(header.id, id, sizeof(header.id)); // writes additional null bytes to dest to ensure that a total of n bytes are written
if (write(fd, &header, sizeof(header)) != sizeof(header)) {
log_errno(error, "write(%s)", tmp); // errno might be incorrect
EINTR_RETRY(close(fd));
return false;
}
if (writev(fd, iov, iovcnt) != (ssize_t)len) {
log_errno(error, "cannot write(%s)", tmp);
EINTR_RETRY(close(fd));
return false;
}
if (renameat(dirfd, tmp, dirfd, fn) == -1) {
log_errno(error, "renameat(%d, %s, %s)", dirfd, tmp, fn);
EINTR_RETRY(close(fd));
return false;
}
log(info, "updated %s for %s", fn, id);
EINTR_RETRY(close(fd));
return true;
}
bool DiskCache::set(const char* id, const char* b, size_t len) {
iovec iov;
iov.iov_base = (void*)b;
iov.iov_len = len;
return set(id, &iov, 1, len);
}