2019-04-09 13:51:15 +02:00
|
|
|
#include "cache.h"
|
2019-04-12 14:52:11 +02:00
|
|
|
|
2019-04-21 00:46:08 +02:00
|
|
|
#include "util.h"
|
|
|
|
|
2019-04-24 00:48:08 +02:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
#include <dirent.h>
|
2019-04-20 03:04:29 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2019-04-24 00:48:08 +02:00
|
|
|
#include <unistd.h>
|
2019-04-20 03:04:29 +02:00
|
|
|
|
2019-04-21 11:57:00 +02:00
|
|
|
/**
|
|
|
|
* \brief Data file block size
|
2019-04-24 07:22:43 +02:00
|
|
|
* \details We set it to 1024*1024*8 = 8MiB
|
2019-04-21 11:57:00 +02:00
|
|
|
*/
|
2019-04-25 02:17:18 +02:00
|
|
|
|
2019-04-25 22:33:22 +02:00
|
|
|
#define DEFAULT_DATA_BLK_SZ 8*1024*1024
|
2019-04-21 12:10:18 +02:00
|
|
|
|
2019-04-23 13:51:10 +02:00
|
|
|
/**
|
|
|
|
* \brief Maximum segment block count
|
2019-04-25 01:58:29 +02:00
|
|
|
* \details This is set to 1024*1024*1024 = 1 GB, which allows the user to
|
|
|
|
* access a 8TB file.
|
2019-04-23 13:51:10 +02:00
|
|
|
*/
|
|
|
|
#define MAX_SEGBC 1073741824
|
|
|
|
|
2019-04-23 12:38:08 +02:00
|
|
|
/**
|
|
|
|
* \brief error associated with metadata
|
|
|
|
*/
|
|
|
|
typedef enum {
|
2019-04-23 13:51:10 +02:00
|
|
|
SUCCESS = 0, /**< Metadata read successful */
|
|
|
|
EFREAD = -1, /**< Fread failed */
|
|
|
|
EINCONSIST = -2, /**< Inconsistency in metadata */
|
|
|
|
EZERO = -3, /**< Unexpected zeros in metadata */
|
|
|
|
EMEM = -4, /**< Memory allocation failure */
|
2019-04-23 12:38:08 +02:00
|
|
|
} MetaError;
|
|
|
|
|
2019-04-25 22:33:22 +02:00
|
|
|
|
2019-04-22 12:08:42 +02:00
|
|
|
int CACHE_SYSTEM_INIT = 0;
|
2019-04-25 22:33:22 +02:00
|
|
|
int DATA_BLK_SZ = 0;
|
2019-04-22 12:08:42 +02:00
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
/**
|
|
|
|
* \brief The metadata directory
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static char *META_DIR;
|
2019-04-20 03:04:29 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief The data directory
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static char *DATA_DIR;
|
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
void CacheSystem_init(const char *path)
|
2019-04-12 14:52:11 +02:00
|
|
|
{
|
2019-04-21 21:48:44 +02:00
|
|
|
DIR* dir;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the top-level cache directory exists, if not, exit the
|
|
|
|
* program. We don't want to unintentionally create a folder
|
|
|
|
*/
|
|
|
|
dir = opendir(path);
|
|
|
|
if (dir) {
|
|
|
|
closedir(dir);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr,
|
|
|
|
"CacheSystem_init(): opendir(): %s\n", strerror(errno));
|
2019-04-21 00:46:08 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2019-04-20 09:31:16 +02:00
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
/* Handle the case of missing '/' */
|
|
|
|
if (path[strnlen(path, MAX_PATH_LEN) - 1] == '/') {
|
2019-04-26 08:39:45 +02:00
|
|
|
META_DIR = path_append(path, "meta/");
|
|
|
|
DATA_DIR = path_append(path, "data/");
|
2019-04-21 21:48:44 +02:00
|
|
|
} else {
|
2019-04-26 08:39:45 +02:00
|
|
|
META_DIR = path_append(path, "/meta/");
|
|
|
|
DATA_DIR = path_append(path, "/data/");
|
2019-04-20 09:31:16 +02:00
|
|
|
}
|
2019-04-21 00:46:08 +02:00
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
/* Check if directories exist, if not, create them */
|
2019-04-22 14:32:15 +02:00
|
|
|
if (mkdir(META_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
|
|
|
|
&& (errno != EEXIST)) {
|
|
|
|
fprintf(stderr, "CacheSystem_init(): mkdir(): %s\n",
|
2019-04-21 21:48:44 +02:00
|
|
|
strerror(errno));
|
2019-04-21 01:42:32 +02:00
|
|
|
}
|
2019-04-25 22:33:22 +02:00
|
|
|
|
2019-04-22 14:32:15 +02:00
|
|
|
if (mkdir(DATA_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
|
|
|
|
&& (errno != EEXIST)) {
|
2019-04-21 21:48:44 +02:00
|
|
|
fprintf(stderr, "CacheSystem_init(): mkdir(): %s\n",
|
|
|
|
strerror(errno));
|
2019-04-21 01:42:32 +02:00
|
|
|
}
|
2019-04-22 12:08:42 +02:00
|
|
|
|
2019-04-25 22:33:22 +02:00
|
|
|
if (!DATA_BLK_SZ) {
|
|
|
|
DATA_BLK_SZ = DEFAULT_DATA_BLK_SZ;
|
|
|
|
}
|
|
|
|
|
2019-04-22 12:08:42 +02:00
|
|
|
CACHE_SYSTEM_INIT = 1;
|
2019-04-21 01:42:32 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief read a metadata file
|
|
|
|
* \return
|
|
|
|
* - -1 on fread error,
|
|
|
|
* - -2 on metadata internal inconsistency
|
|
|
|
* - 0 on success
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static int Meta_read(Cache *cf)
|
2019-04-12 14:52:11 +02:00
|
|
|
{
|
2019-04-24 04:00:47 +02:00
|
|
|
FILE *fp = cf->mfp;
|
|
|
|
rewind(fp);
|
|
|
|
|
2019-04-21 01:42:32 +02:00
|
|
|
int nmemb = 0;
|
2019-04-18 10:48:40 +02:00
|
|
|
|
|
|
|
if (!fp) {
|
|
|
|
/* The metadata file does not exist */
|
|
|
|
fprintf(stderr, "Meta_read(): fopen(): %s\n", strerror(errno));
|
2019-04-23 12:38:08 +02:00
|
|
|
return EFREAD;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 20:37:01 +02:00
|
|
|
fread(&cf->time, sizeof(long), 1, fp);
|
|
|
|
fread(&cf->content_length, sizeof(off_t), 1, fp);
|
|
|
|
fread(&cf->blksz, sizeof(int), 1, fp);
|
|
|
|
fread(&cf->segbc, sizeof(long), 1, fp);
|
2019-04-18 10:48:40 +02:00
|
|
|
|
2019-04-23 12:38:08 +02:00
|
|
|
/* Error checking for fread */
|
|
|
|
if (ferror(fp)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Meta_read(): error reading core metadata!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* These things really should not be zero!!! */
|
2019-04-23 11:26:13 +02:00
|
|
|
if (!cf->content_length || !cf->blksz || !cf->segbc) {
|
|
|
|
fprintf(stderr,
|
2019-04-24 04:00:47 +02:00
|
|
|
"Meta_read(): corrupt metadata: %s, content_length: %ld, \
|
|
|
|
blksz: %d, segbc: %ld\n", cf->path, cf->content_length, cf->blksz, cf->segbc);
|
2019-04-25 14:14:54 +02:00
|
|
|
return EZERO;
|
2019-04-23 11:26:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-21 11:57:00 +02:00
|
|
|
/* Allocate some memory for the segment */
|
2019-04-23 13:51:10 +02:00
|
|
|
if (cf->segbc > MAX_SEGBC) {
|
|
|
|
fprintf(stderr, "Meta_read(): Error: segbc: %ld\n", cf->segbc);
|
2019-04-25 14:14:54 +02:00
|
|
|
return EMEM;
|
2019-04-23 13:51:10 +02:00
|
|
|
}
|
2019-04-23 11:26:13 +02:00
|
|
|
cf->seg = calloc(cf->segbc, sizeof(Seg));
|
2019-04-22 12:08:42 +02:00
|
|
|
if (!cf->seg) {
|
2019-04-23 13:51:10 +02:00
|
|
|
fprintf(stderr, "Meta_read(): calloc failure: %s\n", strerror(errno));
|
2019-04-25 14:14:54 +02:00
|
|
|
return EMEM;
|
2019-04-21 00:46:08 +02:00
|
|
|
}
|
2019-04-21 11:57:00 +02:00
|
|
|
/* Read all the segment */
|
2019-04-21 21:48:44 +02:00
|
|
|
nmemb = fread(cf->seg, sizeof(Seg), cf->segbc, fp);
|
2019-04-18 10:48:40 +02:00
|
|
|
|
2019-04-23 13:55:15 +02:00
|
|
|
/* We shouldn't have gone past the end of the file */
|
|
|
|
if (feof(fp)) {
|
|
|
|
/* reached EOF */
|
|
|
|
fprintf(stderr,
|
|
|
|
"Meta_read(): attempted to read past the end of the file!\n");
|
2019-04-25 14:14:54 +02:00
|
|
|
return EINCONSIST;
|
2019-04-23 13:55:15 +02:00
|
|
|
}
|
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
/* Error checking for fread */
|
2019-04-18 10:48:40 +02:00
|
|
|
if (ferror(fp)) {
|
|
|
|
fprintf(stderr,
|
2019-04-23 12:38:08 +02:00
|
|
|
"Meta_read(): error reading bitmap!\n");
|
2019-04-25 14:14:54 +02:00
|
|
|
return EFREAD;
|
2019-04-20 03:04:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for inconsistent metadata file */
|
2019-04-21 21:48:44 +02:00
|
|
|
if (nmemb != cf-> segbc) {
|
2019-04-20 03:04:29 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"Meta_read(): corrupted metadata!\n");
|
2019-04-25 14:14:54 +02:00
|
|
|
return EINCONSIST;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 14:14:54 +02:00
|
|
|
return 0;
|
2019-04-12 14:52:11 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief write a metadata file
|
|
|
|
* \return
|
|
|
|
* - -1 on error,
|
|
|
|
* - 0 on success
|
|
|
|
*/
|
2019-04-23 20:37:01 +02:00
|
|
|
static int Meta_write(Cache *cf)
|
2019-04-18 10:48:40 +02:00
|
|
|
{
|
2019-04-24 04:00:47 +02:00
|
|
|
FILE *fp = cf->mfp;
|
|
|
|
rewind(fp);
|
2019-04-18 10:48:40 +02:00
|
|
|
|
|
|
|
if (!fp) {
|
2019-04-20 03:04:29 +02:00
|
|
|
/* Cannot create the metadata file */
|
2019-04-18 10:48:40 +02:00
|
|
|
fprintf(stderr, "Meta_write(): fopen(): %s\n", strerror(errno));
|
2019-04-21 00:46:08 +02:00
|
|
|
return -1;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-24 04:00:47 +02:00
|
|
|
/* These things really should not be zero!!! */
|
2019-04-23 11:26:13 +02:00
|
|
|
if (!cf->content_length || !cf->blksz || !cf->segbc) {
|
|
|
|
fprintf(stderr,
|
2019-04-24 04:00:47 +02:00
|
|
|
"Meta_write(): Warning: content_length: %ld, blksz: %d, segbc: \
|
2019-04-23 11:26:13 +02:00
|
|
|
%ld\n", cf->content_length, cf->blksz, cf->segbc);
|
|
|
|
}
|
|
|
|
|
2019-04-23 20:37:01 +02:00
|
|
|
fwrite(&cf->time, sizeof(long), 1, fp);
|
|
|
|
fwrite(&cf->content_length, sizeof(off_t), 1, fp);
|
|
|
|
fwrite(&cf->blksz, sizeof(int), 1, fp);
|
|
|
|
fwrite(&cf->segbc, sizeof(long), 1, fp);
|
2019-04-21 21:48:44 +02:00
|
|
|
fwrite(cf->seg, sizeof(Seg), cf->segbc, fp);
|
2019-04-18 10:48:40 +02:00
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
/* Error checking for fwrite */
|
2019-04-18 10:48:40 +02:00
|
|
|
if (ferror(fp)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Meta_write(): fwrite(): encountered error (from ferror)!\n");
|
2019-04-25 14:14:54 +02:00
|
|
|
return -1;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 14:14:54 +02:00
|
|
|
return 0;
|
2019-04-20 03:04:29 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief create a data file
|
|
|
|
* \details We use sparse creation here
|
|
|
|
* \return
|
|
|
|
* - 0 on successful creation of the data file, note that the result of
|
|
|
|
* the ftruncate() is ignored.
|
|
|
|
* - -1 on failure to create the data file.
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static int Data_create(Cache *cf)
|
2019-04-20 03:04:29 +02:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int mode;
|
|
|
|
|
|
|
|
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
2019-04-26 08:39:45 +02:00
|
|
|
char *datafn = path_append(DATA_DIR, cf->path);
|
2019-04-20 03:04:29 +02:00
|
|
|
fd = open(datafn, O_WRONLY | O_CREAT, mode);
|
|
|
|
free(datafn);
|
|
|
|
if (fd == -1) {
|
|
|
|
fprintf(stderr, "Data_create(): open(): %s\n", strerror(errno));
|
2019-04-21 01:42:32 +02:00
|
|
|
return -1;
|
2019-04-20 03:04:29 +02:00
|
|
|
}
|
2019-04-22 03:43:48 +02:00
|
|
|
if (ftruncate(fd, cf->content_length)) {
|
2019-04-20 03:04:29 +02:00
|
|
|
fprintf(stderr, "Data_create(): ftruncate(): %s\n", strerror(errno));
|
|
|
|
}
|
2019-04-21 01:42:32 +02:00
|
|
|
if (close(fd)) {
|
2019-04-20 03:04:29 +02:00
|
|
|
fprintf(stderr, "Data_create(): close:(): %s\n", strerror(errno));
|
|
|
|
}
|
2019-04-21 01:42:32 +02:00
|
|
|
return 0;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
2019-04-12 14:52:11 +02:00
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief obtain the data file size
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static long Data_size(const char *fn)
|
2019-04-21 00:46:08 +02:00
|
|
|
{
|
2019-04-26 08:39:45 +02:00
|
|
|
char *datafn = path_append(DATA_DIR, fn);
|
2019-04-21 00:46:08 +02:00
|
|
|
struct stat st;
|
2019-04-21 11:57:00 +02:00
|
|
|
int s = stat(datafn, &st);
|
|
|
|
free(datafn);
|
|
|
|
if (!s) {
|
2019-04-22 16:26:25 +02:00
|
|
|
return st.st_size;
|
2019-04-21 00:46:08 +02:00
|
|
|
}
|
|
|
|
fprintf(stderr, "Data_size(): stat(): %s\n", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief read a data file
|
|
|
|
* \param[in] cf the pointer to the cache in-memory data structure
|
|
|
|
* \param[out] buf the output buffer
|
|
|
|
* \param[in] len the length of the segment
|
|
|
|
* \param[in] offset the offset of the segment
|
|
|
|
* \return
|
|
|
|
* - negative values on error,
|
|
|
|
* - otherwise, the number of bytes read.
|
|
|
|
*/
|
2019-04-25 00:58:26 +02:00
|
|
|
static long Data_read(Cache *cf, uint8_t *buf, off_t len, off_t offset)
|
2019-04-12 14:52:11 +02:00
|
|
|
{
|
2019-04-17 18:38:27 +02:00
|
|
|
if (len == 0) {
|
2019-04-18 10:48:40 +02:00
|
|
|
fprintf(stderr, "Data_read(): requested to read 0 byte!\n");
|
2019-04-22 22:20:41 +02:00
|
|
|
return -EINVAL;
|
2019-04-17 18:38:27 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
if (fseeko(cf->dfp, offset, SEEK_SET)) {
|
2019-04-22 10:50:53 +02:00
|
|
|
/* fseeko failed */
|
|
|
|
fprintf(stderr, "Data_read(): fseeko(): %s\n", strerror(errno));
|
2019-04-25 14:14:54 +02:00
|
|
|
return -EIO;
|
2019-04-17 18:38:27 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 14:14:54 +02:00
|
|
|
long byte_read = fread(buf, sizeof(uint8_t), len, cf->dfp);
|
2019-04-17 18:38:27 +02:00
|
|
|
if (byte_read != len) {
|
|
|
|
fprintf(stderr,
|
2019-04-20 03:04:29 +02:00
|
|
|
"Data_read(): fread(): requested %ld, returned %ld!\n",
|
2019-04-17 18:38:27 +02:00
|
|
|
len, byte_read);
|
2019-04-22 22:20:41 +02:00
|
|
|
if (feof(cf->dfp)) {
|
2019-04-18 10:48:40 +02:00
|
|
|
/* reached EOF */
|
2019-04-17 18:38:27 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"Data_read(): fread(): reached the end of the file!\n");
|
|
|
|
}
|
2019-04-22 22:20:41 +02:00
|
|
|
if (ferror(cf->dfp)) {
|
2019-04-18 10:48:40 +02:00
|
|
|
/* filesystem error */
|
2019-04-17 18:38:27 +02:00
|
|
|
fprintf(stderr,
|
2019-04-22 22:20:41 +02:00
|
|
|
"Data_read(): fread(): encountered error (from ferror)!\n");
|
2019-04-17 18:38:27 +02:00
|
|
|
}
|
|
|
|
}
|
2019-04-25 14:14:54 +02:00
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
return byte_read;
|
2019-04-12 14:52:11 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief write to a data file
|
|
|
|
* \param[in] cf the pointer to the cache in-memory data structure
|
|
|
|
* \param[in] buf the input buffer
|
|
|
|
* \param[in] len the length of the segment
|
|
|
|
* \param[in] offset the offset of the segment
|
|
|
|
* \return
|
|
|
|
* - -1 when the data file does not exist
|
|
|
|
* - otherwise, the number of bytes written.
|
|
|
|
*/
|
|
|
|
|
2019-04-25 00:58:26 +02:00
|
|
|
static long Data_write(Cache *cf, const uint8_t *buf, off_t len,
|
2019-04-22 22:20:41 +02:00
|
|
|
off_t offset)
|
2019-04-12 14:52:11 +02:00
|
|
|
{
|
2019-04-18 10:48:40 +02:00
|
|
|
if (len == 0) {
|
|
|
|
fprintf(stderr, "Data_write(): requested to write 0 byte!\n");
|
2019-04-22 22:20:41 +02:00
|
|
|
return -EINVAL;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
if (fseeko(cf->dfp, offset, SEEK_SET)) {
|
2019-04-22 10:50:53 +02:00
|
|
|
/* fseeko failed */
|
|
|
|
fprintf(stderr, "Data_write(): fseeko(): %s\n", strerror(errno));
|
2019-04-25 14:14:54 +02:00
|
|
|
return -EIO;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 14:14:54 +02:00
|
|
|
long byte_written = fwrite(buf, sizeof(uint8_t), len, cf->dfp);
|
2019-04-18 10:48:40 +02:00
|
|
|
if (byte_written != len) {
|
|
|
|
fprintf(stderr,
|
2019-04-20 03:04:29 +02:00
|
|
|
"Data_write(): fwrite(): requested %ld, returned %ld!\n",
|
2019-04-18 10:48:40 +02:00
|
|
|
len, byte_written);
|
2019-04-22 22:20:41 +02:00
|
|
|
if (ferror(cf->dfp)) {
|
2019-04-18 10:48:40 +02:00
|
|
|
/* filesystem error */
|
|
|
|
fprintf(stderr,
|
|
|
|
"Data_write(): fwrite(): encountered error (from ferror)!\n");
|
|
|
|
}
|
|
|
|
}
|
2019-04-25 14:14:54 +02:00
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
return byte_written;
|
2019-04-12 14:52:11 +02:00
|
|
|
}
|
2019-04-21 21:48:44 +02:00
|
|
|
|
2019-04-22 02:50:05 +02:00
|
|
|
int CacheDir_create(const char *dirn)
|
|
|
|
{
|
2019-04-26 08:39:45 +02:00
|
|
|
char *metadirn = path_append(META_DIR, dirn);
|
|
|
|
char *datadirn = path_append(DATA_DIR, dirn);
|
2019-04-22 02:50:05 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
i = -mkdir(metadirn, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
|
|
|
if (i && (errno != EEXIST)) {
|
|
|
|
fprintf(stderr, "CacheDir_create(): mkdir(): %s\n", strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
i |= -mkdir(datadirn, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) << 1;
|
|
|
|
if (i && (errno != EEXIST)) {
|
|
|
|
fprintf(stderr, "CacheDir_create(): mkdir(): %s\n", strerror(errno));
|
|
|
|
}
|
|
|
|
return -i;
|
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief Allocate a new cache data structure
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static Cache *Cache_alloc()
|
2019-04-21 21:48:44 +02:00
|
|
|
{
|
|
|
|
Cache *cf = calloc(1, sizeof(Cache));
|
|
|
|
if (!cf) {
|
|
|
|
fprintf(stderr, "Cache_new(): calloc failure!\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2019-04-23 20:37:01 +02:00
|
|
|
|
2019-04-25 09:50:04 +02:00
|
|
|
if (pthread_mutexattr_init(&cf->rw_lock_attr)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_alloc(): rw_lock_attr initialisation failed!\n");
|
2019-04-25 00:58:26 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 09:50:04 +02:00
|
|
|
if (pthread_mutexattr_setpshared(&cf->rw_lock_attr,
|
|
|
|
PTHREAD_PROCESS_SHARED)) {
|
|
|
|
fprintf(stderr, "Cache_alloc(): could not set rw_lock_attr!\n");
|
2019-04-25 00:58:26 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 09:50:04 +02:00
|
|
|
if (pthread_mutex_init(&cf->rw_lock, &cf->rw_lock_attr)) {
|
|
|
|
fprintf(stderr, "Cache_alloc(): rw_lock initialisation failed!\n");
|
|
|
|
}
|
2019-04-25 00:58:26 +02:00
|
|
|
|
|
|
|
if (pthread_mutexattr_init(&cf->bgt_lock_attr)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_alloc(): bgt_lock_attr initialisation failed!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pthread_mutexattr_setpshared(&cf->bgt_lock_attr,
|
|
|
|
PTHREAD_PROCESS_SHARED)) {
|
|
|
|
fprintf(stderr, "Cache_alloc(): could not set bgt_lock_attr!\n");
|
|
|
|
}
|
|
|
|
|
2019-04-25 01:58:29 +02:00
|
|
|
if (pthread_mutex_init(&cf->bgt_lock, &cf->bgt_lock_attr)) {
|
2019-04-25 00:58:26 +02:00
|
|
|
fprintf(stderr, "Cache_alloc(): bgt_lock initialisation failed!\n");
|
2019-04-23 20:37:01 +02:00
|
|
|
}
|
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
return cf;
|
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief free a cache data structure
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static void Cache_free(Cache *cf)
|
2019-04-21 21:48:44 +02:00
|
|
|
{
|
2019-04-23 20:37:01 +02:00
|
|
|
if (pthread_mutex_destroy(&cf->rw_lock)) {
|
|
|
|
fprintf(stderr, "Cache_free(): could not destroy rw_lock!\n");
|
|
|
|
}
|
|
|
|
|
2019-04-25 09:50:04 +02:00
|
|
|
if (pthread_mutexattr_destroy(&cf->rw_lock_attr)) {
|
|
|
|
fprintf(stderr, "Cache_alloc(): could not destroy rw_lock_attr!\n");
|
|
|
|
}
|
|
|
|
|
2019-04-25 00:58:26 +02:00
|
|
|
if (pthread_mutex_destroy(&cf->bgt_lock)) {
|
|
|
|
fprintf(stderr, "Cache_free(): could not destroy bgt_lock!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pthread_mutexattr_destroy(&cf->bgt_lock_attr)) {
|
|
|
|
fprintf(stderr, "Cache_alloc(): could not destroy bgt_lock_attr!\n");
|
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
if (cf->path) {
|
|
|
|
free(cf->path);
|
2019-04-21 21:48:44 +02:00
|
|
|
}
|
|
|
|
if (cf->seg) {
|
|
|
|
free(cf->seg);
|
|
|
|
}
|
2019-04-25 00:58:26 +02:00
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
free(cf);
|
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief Check if both metadata and data file exist, otherwise perform cleanup.
|
|
|
|
* \details
|
|
|
|
* This function checks if both metadata file and the data file exist. If that
|
|
|
|
* is not the case, clean up is performed - the existing unpaired metadata file
|
|
|
|
* or data file is deleted.
|
|
|
|
* \return
|
|
|
|
* - 0, if both metadata and cache file exist
|
|
|
|
* - -1, otherwise
|
|
|
|
*/
|
2019-04-22 03:33:26 +02:00
|
|
|
static int Cache_exist(const char *fn)
|
2019-04-21 21:48:44 +02:00
|
|
|
{
|
|
|
|
int meta_exists = 1;
|
|
|
|
int data_exists = 1;
|
2019-04-26 08:39:45 +02:00
|
|
|
char *metafn = path_append(META_DIR, fn);
|
|
|
|
char *datafn = path_append(DATA_DIR, fn);
|
2019-04-21 21:48:44 +02:00
|
|
|
|
|
|
|
if (access(metafn, F_OK)) {
|
|
|
|
// fprintf(stderr, "Cache_exist(): access(): %s\n", strerror(errno));
|
|
|
|
meta_exists = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access(datafn, F_OK)) {
|
|
|
|
// fprintf(stderr, "Cache_exist(): access(): %s\n", strerror(errno));
|
|
|
|
data_exists = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (meta_exists ^ data_exists) {
|
|
|
|
if (meta_exists) {
|
|
|
|
if(unlink(metafn)) {
|
|
|
|
fprintf(stderr, "Cache_exist(): unlink(): %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (data_exists) {
|
|
|
|
if(unlink(datafn)) {
|
|
|
|
fprintf(stderr, "Cache_exist(): unlink(): %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(metafn);
|
|
|
|
free(datafn);
|
|
|
|
|
2019-04-23 00:29:59 +02:00
|
|
|
return meta_exists & data_exists;
|
2019-04-21 21:48:44 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief delete a cache file set
|
|
|
|
*/
|
2019-04-23 12:38:08 +02:00
|
|
|
void Cache_delete(const char *fn)
|
2019-04-21 21:48:44 +02:00
|
|
|
{
|
2019-04-24 04:00:47 +02:00
|
|
|
// fprintf(stderr, "Cache_delete(): deleting %s\n", fn);
|
2019-04-26 08:39:45 +02:00
|
|
|
char *metafn = path_append(META_DIR, fn);
|
|
|
|
char *datafn = path_append(DATA_DIR, fn);
|
2019-04-21 21:48:44 +02:00
|
|
|
if (!access(metafn, F_OK)) {
|
|
|
|
if(unlink(metafn)) {
|
|
|
|
fprintf(stderr, "Cache_delete(): unlink(): %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!access(datafn, F_OK)) {
|
|
|
|
if(unlink(datafn)) {
|
|
|
|
fprintf(stderr, "Cache_delete(): unlink(): %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(metafn);
|
|
|
|
free(datafn);
|
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
/**
|
|
|
|
* \brief Open the data file of a cache data set
|
|
|
|
* \return
|
|
|
|
* - 0 on success
|
|
|
|
* - -1 on failure, with appropriate errno set.
|
|
|
|
*/
|
|
|
|
static int Data_open(Cache *cf)
|
|
|
|
{
|
2019-04-26 08:39:45 +02:00
|
|
|
char *datafn = path_append(DATA_DIR, cf->path);
|
2019-04-22 22:20:41 +02:00
|
|
|
cf->dfp = fopen(datafn, "r+");
|
|
|
|
free(datafn);
|
|
|
|
if (!cf->dfp) {
|
|
|
|
/* Failed to open the data file */
|
2019-04-26 08:39:45 +02:00
|
|
|
fprintf(stderr, "Data_open(): fopen(%s): %s\n", datafn,
|
|
|
|
strerror(errno));
|
2019-04-22 22:20:41 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-24 04:00:47 +02:00
|
|
|
/**
|
|
|
|
* \brief Open a metafile
|
|
|
|
* \return
|
|
|
|
* - 0 on success
|
|
|
|
* - -1 on failure, with appropriate errno set.
|
|
|
|
*/
|
|
|
|
static int Meta_open(Cache *cf)
|
2019-04-23 00:29:59 +02:00
|
|
|
{
|
2019-04-26 08:39:45 +02:00
|
|
|
char *metafn = path_append(META_DIR, cf->path);
|
2019-04-24 04:00:47 +02:00
|
|
|
cf->mfp = fopen(metafn, "r+");
|
|
|
|
if (!cf->mfp) {
|
|
|
|
/* Failed to open the data file */
|
|
|
|
fprintf(stderr, "Meta_open(): fopen(%s): %s\n", metafn,
|
|
|
|
strerror(errno));
|
|
|
|
free(metafn);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
free(metafn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Create a metafile
|
|
|
|
* \return
|
|
|
|
* - 0 on success
|
|
|
|
* - -1 on failure, with appropriate errno set.
|
|
|
|
*/
|
|
|
|
static int Meta_create(Cache *cf)
|
|
|
|
{
|
2019-04-26 08:39:45 +02:00
|
|
|
char *metafn = path_append(META_DIR, cf->path);
|
2019-04-24 04:00:47 +02:00
|
|
|
cf->mfp = fopen(metafn, "w");
|
|
|
|
if (!cf->mfp) {
|
|
|
|
/* Failed to open the data file */
|
|
|
|
fprintf(stderr, "Meta_create(): fopen(%s): %s\n", metafn,
|
|
|
|
strerror(errno));
|
|
|
|
free(metafn);
|
|
|
|
return -1;
|
2019-04-23 00:29:59 +02:00
|
|
|
}
|
2019-04-24 04:00:47 +02:00
|
|
|
free(metafn);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-04-23 00:29:59 +02:00
|
|
|
|
2019-04-24 04:00:47 +02:00
|
|
|
int Cache_create(Link *this_link)
|
|
|
|
{
|
|
|
|
char *fn;
|
|
|
|
fn = curl_easy_unescape(NULL, this_link->f_url + ROOT_LINK_OFFSET, 0, NULL);
|
2019-04-23 00:29:59 +02:00
|
|
|
fprintf(stderr, "Cache_create(): Creating cache files for %s.\n", fn);
|
|
|
|
|
|
|
|
Cache *cf = Cache_alloc();
|
|
|
|
cf->path = strndup(fn, MAX_PATH_LEN);
|
2019-04-23 12:38:08 +02:00
|
|
|
cf->time = this_link->time;
|
|
|
|
cf->content_length = this_link->content_length;
|
2019-04-24 04:00:47 +02:00
|
|
|
cf->blksz = DATA_BLK_SZ;
|
|
|
|
cf->segbc = (cf->content_length / cf->blksz) + 1;
|
|
|
|
cf->seg = calloc(cf->segbc, sizeof(Seg));
|
2019-04-23 00:29:59 +02:00
|
|
|
|
2019-04-24 04:00:47 +02:00
|
|
|
if (!cf->seg) {
|
|
|
|
fprintf(stderr, "Cache_create(): cf->seg calloc failure!\n");
|
|
|
|
exit(EXIT_FAILURE);
|
2019-04-23 00:29:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Meta_create(cf)) {
|
2019-04-24 04:00:47 +02:00
|
|
|
fprintf(stderr, "Cache_create(): cannot create metadata.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fclose(cf->mfp)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_create(): cannot close metadata after creation: %s.\n",
|
|
|
|
strerror(errno));
|
2019-04-23 00:29:59 +02:00
|
|
|
}
|
2019-04-23 20:37:01 +02:00
|
|
|
|
2019-04-24 04:00:47 +02:00
|
|
|
if (Meta_open(cf)) {
|
|
|
|
Cache_free(cf);
|
|
|
|
fprintf(stderr, "Cache_create(): cannot open metadata file, %s.\n", fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Meta_write(cf)) {
|
|
|
|
fprintf(stderr, "Cache_create(): Meta_write() failed!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fclose(cf->mfp)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_create(): cannot close metadata after write, %s.\n",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Data_create(cf)) {
|
|
|
|
fprintf(stderr, "Cache_create(): Data_create() failed!\n");
|
|
|
|
}
|
2019-04-23 00:29:59 +02:00
|
|
|
|
|
|
|
Cache_free(cf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cache_exist() returns 1, if cache files exist and valid. Whereas this
|
|
|
|
* function returns 0 on success.
|
|
|
|
*/
|
2019-04-23 12:38:08 +02:00
|
|
|
int res = -(!Cache_exist(fn));
|
|
|
|
curl_free(fn);
|
|
|
|
return res;
|
2019-04-23 00:29:59 +02:00
|
|
|
}
|
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
Cache *Cache_open(const char *fn)
|
|
|
|
{
|
|
|
|
/* Check if both metadata and data file exist */
|
2019-04-23 00:29:59 +02:00
|
|
|
if (!Cache_exist(fn)) {
|
2019-04-21 21:48:44 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the cache in-memory data structure */
|
|
|
|
Cache *cf = Cache_alloc();
|
2019-04-22 22:20:41 +02:00
|
|
|
cf->path = strndup(fn, MAX_PATH_LEN);
|
2019-04-21 21:48:44 +02:00
|
|
|
|
2019-04-23 12:38:08 +02:00
|
|
|
/* Associate the cache structure with a link */
|
|
|
|
cf->link = path_to_Link(fn);
|
|
|
|
if (!cf->link) {
|
|
|
|
Cache_free(cf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-04-24 04:00:47 +02:00
|
|
|
if (Meta_open(cf)) {
|
|
|
|
Cache_free(cf);
|
|
|
|
fprintf(stderr, "Cache_open(): cannot open metadata file %s.\n", fn);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-04-23 20:37:01 +02:00
|
|
|
|
|
|
|
int rtn = Meta_read(cf);
|
|
|
|
|
2019-04-23 00:29:59 +02:00
|
|
|
/*
|
2019-04-23 12:38:08 +02:00
|
|
|
* Internally inconsistent or corrupt metadata
|
2019-04-23 00:29:59 +02:00
|
|
|
*/
|
2019-04-23 13:51:10 +02:00
|
|
|
if ((rtn == EINCONSIST) || (rtn == EZERO) || (rtn == EMEM)) {
|
2019-04-21 21:48:44 +02:00
|
|
|
Cache_free(cf);
|
2019-04-24 04:00:47 +02:00
|
|
|
fprintf(stderr, "Cache_open(): metadata error: %s, %d.\n", fn, rtn);
|
2019-04-21 21:48:44 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-04-23 00:29:59 +02:00
|
|
|
/*
|
|
|
|
* Inconsistency between metadata and data file, note that on disk file
|
|
|
|
* size might be bigger than content_length, due to on-disk filesystem
|
|
|
|
* allocation policy.
|
|
|
|
*/
|
|
|
|
if (cf->content_length > Data_size(fn)) {
|
2019-04-24 04:00:47 +02:00
|
|
|
fprintf(stderr, "Cache_open(): metadata inconsistency %s, \
|
|
|
|
cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length,
|
|
|
|
Data_size(fn));
|
|
|
|
Cache_free(cf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the cache files are not outdated */
|
|
|
|
if (cf->time != cf->link->time) {
|
|
|
|
fprintf(stderr, "Cache_open(): outdated cache file: %s.\n", fn);
|
2019-04-21 21:48:44 +02:00
|
|
|
Cache_free(cf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
if (Data_open(cf)) {
|
|
|
|
Cache_free(cf);
|
2019-04-24 04:00:47 +02:00
|
|
|
fprintf(stderr, "Cache_open(): cannot open data file %s.\n", fn);
|
2019-04-22 22:20:41 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
return cf;
|
|
|
|
}
|
2019-04-22 22:20:41 +02:00
|
|
|
|
|
|
|
void Cache_close(Cache *cf)
|
|
|
|
{
|
2019-04-25 00:58:26 +02:00
|
|
|
/* Must wait for the background download thread to stop */
|
|
|
|
pthread_mutex_lock(&cf->bgt_lock);
|
|
|
|
pthread_mutex_unlock(&cf->bgt_lock);
|
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
if (Meta_write(cf)) {
|
|
|
|
fprintf(stderr, "Cache_close(): Meta_write() error.");
|
|
|
|
}
|
2019-04-24 04:00:47 +02:00
|
|
|
|
|
|
|
if (fclose(cf->mfp)) {
|
|
|
|
fprintf(stderr, "Cache_close(): cannot close metadata: %s.\n",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
2019-04-22 22:20:41 +02:00
|
|
|
|
|
|
|
if (fclose(cf->dfp)) {
|
2019-04-24 04:00:47 +02:00
|
|
|
fprintf(stderr, "Cache_close(): cannot close data file %s.\n",
|
|
|
|
strerror(errno));
|
2019-04-22 22:20:41 +02:00
|
|
|
}
|
2019-04-23 20:37:01 +02:00
|
|
|
|
2019-04-22 22:20:41 +02:00
|
|
|
return Cache_free(cf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Check if a segment exists.
|
|
|
|
* \return 1 if the segment exists
|
|
|
|
*/
|
|
|
|
static int Seg_exist(Cache *cf, off_t offset)
|
|
|
|
{
|
2019-04-23 00:40:43 +02:00
|
|
|
off_t byte = offset / cf->blksz;
|
|
|
|
return cf->seg[byte];
|
2019-04-22 22:20:41 +02:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* \brief Set the existence of a segment
|
2019-04-24 01:33:20 +02:00
|
|
|
* \param[in] cf the cache in-memory data structure
|
2019-04-22 22:20:41 +02:00
|
|
|
* \param[in] offset the starting position of the segment.
|
|
|
|
* \param[in] i 1 for exist, 0 for doesn't exist
|
|
|
|
* \note Call this after downloading a segment.
|
|
|
|
*/
|
|
|
|
static void Seg_set(Cache *cf, off_t offset, int i)
|
|
|
|
{
|
2019-04-23 00:40:43 +02:00
|
|
|
off_t byte = offset / cf->blksz;
|
|
|
|
cf->seg[byte] = i;
|
2019-04-22 22:20:41 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 00:58:26 +02:00
|
|
|
/**
|
|
|
|
* \brief Background download function
|
|
|
|
* \details If we are requesting the data from the second half of the current
|
|
|
|
* segment, we can spawn a pthread using this function to download the next
|
|
|
|
* segment.
|
|
|
|
*/
|
2019-04-25 09:50:04 +02:00
|
|
|
static void *Cache_bgdl(void *arg)
|
2019-04-25 00:58:26 +02:00
|
|
|
{
|
|
|
|
Cache *cf = (Cache *) arg;
|
2019-04-25 09:50:04 +02:00
|
|
|
pthread_mutex_lock(&cf->rw_lock);
|
2019-04-25 01:58:29 +02:00
|
|
|
uint8_t *recv_buf = calloc(DATA_BLK_SZ, sizeof(uint8_t));
|
2019-04-25 09:50:04 +02:00
|
|
|
fprintf(stderr, "Cache_bgdl(): ");
|
2019-04-25 00:58:26 +02:00
|
|
|
long recv = path_download(cf->path, (char *) recv_buf, cf->blksz,
|
|
|
|
cf->next_offset);
|
|
|
|
if ( (recv == cf->blksz) ||
|
|
|
|
(cf->next_offset == (cf->content_length / cf->blksz * cf->blksz)) )
|
|
|
|
{
|
|
|
|
Data_write(cf, recv_buf, cf->blksz, cf->next_offset);
|
|
|
|
Seg_set(cf, cf->next_offset, 1);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr,
|
2019-04-25 09:50:04 +02:00
|
|
|
"Cache_bgdl(): recv (%ld) < cf->blksz! \
|
2019-04-25 00:58:26 +02:00
|
|
|
Possible network error?\n",
|
|
|
|
recv);
|
|
|
|
}
|
2019-04-25 01:58:29 +02:00
|
|
|
free(recv_buf);
|
2019-04-25 00:58:26 +02:00
|
|
|
pthread_mutex_unlock(&cf->bgt_lock);
|
2019-04-25 09:50:04 +02:00
|
|
|
pthread_mutex_unlock(&cf->rw_lock);
|
2019-04-25 00:58:26 +02:00
|
|
|
pthread_exit(NULL);
|
|
|
|
}
|
|
|
|
|
2019-04-23 11:26:13 +02:00
|
|
|
long Cache_read(Cache *cf, char *output_buf, off_t len, off_t offset)
|
2019-04-22 22:20:41 +02:00
|
|
|
{
|
2019-04-25 09:50:04 +02:00
|
|
|
// size_t start = offset;
|
|
|
|
// size_t end = start + len;
|
|
|
|
// char range_str[64];
|
|
|
|
// snprintf(range_str, sizeof(range_str), "%lu-%lu", start, end);
|
|
|
|
// fprintf(stderr, "Cache_read(%s, %s);\n", cf->path, range_str);
|
|
|
|
|
|
|
|
/* SIGFPE prevention, although this shouldn't happen in the first place! */
|
2019-04-23 11:26:13 +02:00
|
|
|
if (!cf->blksz) {
|
2019-04-23 12:38:08 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_read(): Warning: cf->blksz: %d, directly downloading",
|
|
|
|
cf->blksz);
|
2019-04-23 11:26:13 +02:00
|
|
|
return path_download(cf->path, output_buf, len, offset);
|
|
|
|
}
|
2019-04-25 00:58:26 +02:00
|
|
|
|
2019-04-25 01:58:29 +02:00
|
|
|
long send;
|
2019-04-25 00:58:26 +02:00
|
|
|
off_t dl_offset = offset / cf->blksz * cf->blksz;
|
2019-04-25 09:50:04 +02:00
|
|
|
|
|
|
|
/* ------------------ Check if the segment already exists ---------------*/
|
2019-04-22 22:20:41 +02:00
|
|
|
if (Seg_exist(cf, offset)) {
|
2019-04-24 05:05:24 +02:00
|
|
|
send = Data_read(cf, (uint8_t *) output_buf, len, offset);
|
2019-04-25 09:50:04 +02:00
|
|
|
goto bgdl;
|
2019-04-22 22:20:41 +02:00
|
|
|
} else {
|
2019-04-25 09:50:04 +02:00
|
|
|
/* Wait until the background thread finishes, then lock the I/O */
|
|
|
|
pthread_mutex_lock(&cf->rw_lock);
|
|
|
|
if (Seg_exist(cf, offset)) {
|
|
|
|
/* The segment already exists, send it off the unlock the I/O */
|
|
|
|
send = Data_read(cf, (uint8_t *) output_buf, len, offset);
|
|
|
|
pthread_mutex_unlock(&cf->rw_lock);
|
|
|
|
goto bgdl;
|
2019-04-23 03:35:17 +02:00
|
|
|
}
|
2019-04-22 22:20:41 +02:00
|
|
|
}
|
2019-04-25 09:50:04 +02:00
|
|
|
|
|
|
|
/* ------------------------Download the segment -------------------------*/
|
|
|
|
|
|
|
|
uint8_t *recv_buf = calloc(DATA_BLK_SZ, sizeof(uint8_t));
|
|
|
|
fprintf(stderr, "Cache_read(): ");
|
|
|
|
long recv = path_download(cf->path, (char *) recv_buf, cf->blksz,
|
|
|
|
dl_offset);
|
|
|
|
/*
|
|
|
|
* check if we have received enough data
|
|
|
|
* send it off, then write it to the disk
|
|
|
|
*
|
|
|
|
* Condition 1: received the exact amount as the segment size.
|
|
|
|
* Condition 2: offset is the last segment
|
|
|
|
*/
|
|
|
|
if ( (recv == cf->blksz) ||
|
|
|
|
(dl_offset == (cf->content_length / cf->blksz * cf->blksz)) )
|
|
|
|
{
|
|
|
|
memmove(output_buf, recv_buf + (offset - dl_offset), len);
|
|
|
|
send = len;
|
|
|
|
Data_write(cf, recv_buf, cf->blksz, dl_offset);
|
|
|
|
Seg_set(cf, dl_offset, 1);
|
|
|
|
} else {
|
|
|
|
memmove(output_buf, recv_buf + (offset - dl_offset), recv);
|
|
|
|
send = recv;
|
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_read(): recv (%ld) < cf->blksz! Possible network error?\n",
|
|
|
|
recv);
|
|
|
|
}
|
|
|
|
free(recv_buf);
|
2019-04-23 20:37:01 +02:00
|
|
|
pthread_mutex_unlock(&cf->rw_lock);
|
2019-04-25 00:58:26 +02:00
|
|
|
|
2019-04-25 09:50:04 +02:00
|
|
|
/* -----------Download the next segment in background -------------------*/
|
|
|
|
bgdl:
|
2019-04-25 00:58:26 +02:00
|
|
|
cf->next_offset = round_div(offset, cf->blksz) * cf->blksz;
|
|
|
|
if ( (cf->next_offset > dl_offset) && !Seg_exist(cf, cf->next_offset) ) {
|
|
|
|
/* Stop the spawning of multiple background pthreads */
|
|
|
|
if(!pthread_mutex_trylock(&cf->bgt_lock)) {
|
2019-04-25 09:50:04 +02:00
|
|
|
if (pthread_create(&cf->bgt, NULL, Cache_bgdl, cf)) {
|
2019-04-25 00:58:26 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_read(): Error creating background download thread\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-24 05:05:24 +02:00
|
|
|
return send;
|
2019-04-22 22:20:41 +02:00
|
|
|
}
|