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-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>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
2019-04-21 11:57:00 +02:00
|
|
|
/**
|
|
|
|
* \brief Data file block size
|
|
|
|
* \details The data file block size is set to 128KiB, for convenience. This is
|
|
|
|
* because the maximum requested block size by FUSE seems to be 128KiB under
|
|
|
|
* Debian Stretch. Note that the minimum requested block size appears to be
|
|
|
|
* 4KiB.
|
|
|
|
*
|
|
|
|
* More information regarding block size can be found at:
|
|
|
|
* https://wiki.vuze.com/w/Torrent_Piece_Size
|
2019-04-21 12:10:18 +02:00
|
|
|
*
|
2019-04-22 02:50:05 +02:00
|
|
|
* Note that at the current configuration, a 16GiB file uses 16MiB of memory to
|
|
|
|
* store the bitmap
|
2019-04-21 11:57:00 +02:00
|
|
|
*/
|
|
|
|
#define DATA_BLK_SZ 131072
|
2019-04-21 12:10:18 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief the maximum length of a path
|
2019-04-22 02:50:05 +02:00
|
|
|
* \details This corresponds the maximum path length under Ext4.
|
2019-04-21 12:10:18 +02:00
|
|
|
*/
|
2019-04-21 11:57:00 +02:00
|
|
|
#define MAX_PATH_LEN 4096
|
2019-04-20 03:04:29 +02:00
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
/**
|
|
|
|
* \brief create a metadata file
|
|
|
|
* \details We set the followings here:
|
|
|
|
* - block size
|
|
|
|
* - the number of segments
|
|
|
|
*
|
|
|
|
* The number of segments depends on the block size. The block size is set to
|
|
|
|
* 128KiB for now. In future support for different block size may be
|
|
|
|
* implemented.
|
|
|
|
*/
|
|
|
|
static int Meta_create(Cache *cf);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief write a metadata file
|
|
|
|
* \return
|
|
|
|
* - -1 on error,
|
|
|
|
* - 0 on success
|
|
|
|
*/
|
|
|
|
static int Meta_write(const Cache *cf);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief read a metadata file
|
|
|
|
* \return
|
|
|
|
* - -1 on fread error,
|
|
|
|
* - -2 on metadata internal inconsistency
|
|
|
|
* - 0 on success
|
|
|
|
*/
|
|
|
|
static int Meta_read(Cache *cf);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \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.
|
|
|
|
*/
|
|
|
|
static int Data_create(Cache *cf);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief obtain the data file size
|
|
|
|
*/
|
|
|
|
static long Data_size(const char *fn);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief read a data file
|
|
|
|
* \return
|
|
|
|
* - -1 when the data file does not exist
|
|
|
|
* - otherwise, the number of bytes read.
|
|
|
|
*/
|
|
|
|
static long Data_read(const Cache *cf, long offset, long len,
|
|
|
|
uint8_t *buf);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief write to a data file
|
|
|
|
* \return
|
|
|
|
* - -1 when the data file does not exist
|
|
|
|
* - otherwise, the number of bytes written.
|
|
|
|
*/
|
|
|
|
static long Data_write(const Cache *cf, long offset, long len,
|
|
|
|
const uint8_t *buf);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Allocate a new cache data structure
|
|
|
|
*/
|
|
|
|
static Cache *Cache_alloc();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief free a cache data structure
|
|
|
|
*/
|
|
|
|
static void Cache_free(Cache *cf);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \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
|
|
|
|
*/
|
|
|
|
static int Cache_exist(const char *fn);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief delete a cache file set
|
|
|
|
*/
|
|
|
|
static void Cache_delete(const char *fn);
|
|
|
|
|
2019-04-22 12:08:42 +02:00
|
|
|
int CACHE_SYSTEM_INIT = 0;
|
|
|
|
|
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] == '/') {
|
|
|
|
META_DIR = strndupcat(path, "meta/", MAX_PATH_LEN);
|
|
|
|
DATA_DIR = strndupcat(path, "data/", MAX_PATH_LEN);
|
|
|
|
} else {
|
|
|
|
META_DIR = strndupcat(path, "/meta/", MAX_PATH_LEN);
|
|
|
|
DATA_DIR = strndupcat(path, "/data/", MAX_PATH_LEN);
|
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 */
|
|
|
|
dir = opendir(META_DIR);
|
|
|
|
if (dir) {
|
|
|
|
closedir(dir);
|
|
|
|
} else if (mkdir(META_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
|
|
|
|
fprintf(stderr, "CacheSystem_init(): mkdir(): %s\n",
|
|
|
|
strerror(errno));
|
2019-04-21 01:42:32 +02:00
|
|
|
}
|
2019-04-21 21:48:44 +02:00
|
|
|
dir = opendir(DATA_DIR);
|
|
|
|
if (dir) {
|
|
|
|
closedir(dir);
|
|
|
|
} else if (mkdir(META_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
|
|
|
|
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
|
|
|
|
|
|
|
CACHE_SYSTEM_INIT = 1;
|
2019-04-21 01:42:32 +02:00
|
|
|
}
|
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
int Seg_exist(Cache *cf, long start)
|
2019-04-21 11:57:00 +02:00
|
|
|
{
|
2019-04-21 21:48:44 +02:00
|
|
|
long total_bit = start / cf->blksz;
|
|
|
|
long byte = total_bit / 8;
|
|
|
|
int bit = total_bit % 8;
|
2019-04-21 11:57:00 +02:00
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
return cf->seg[byte] & (1 << bit);
|
2019-04-21 11:57:00 +02:00
|
|
|
}
|
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
void Seg_set(Cache *cf, long start, int i)
|
2019-04-21 00:46:08 +02:00
|
|
|
{
|
2019-04-21 21:48:44 +02:00
|
|
|
long total_bit = start / cf->blksz;
|
|
|
|
long byte = total_bit / 8;
|
|
|
|
int bit = total_bit % 8;
|
2019-04-21 11:57:00 +02:00
|
|
|
|
2019-04-21 21:48:44 +02:00
|
|
|
if (i) {
|
|
|
|
cf->seg[byte] |= (1 << bit);
|
|
|
|
} else {
|
|
|
|
cf->seg[byte] &= ~(1 << bit);
|
2019-04-20 03:04:29 +02:00
|
|
|
}
|
2019-04-12 14:52:11 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
static int Meta_create(Cache *cf)
|
2019-04-21 01:42:32 +02:00
|
|
|
{
|
2019-04-21 11:57:00 +02:00
|
|
|
cf->blksz = DATA_BLK_SZ;
|
2019-04-22 03:43:48 +02:00
|
|
|
cf->segbc = cf->content_length / cf->blksz / 8 + 1;
|
2019-04-21 21:48:44 +02:00
|
|
|
cf->seg = calloc(cf->segbc, sizeof(Seg));
|
2019-04-22 12:08:42 +02:00
|
|
|
if (!cf->seg) {
|
2019-04-21 11:57:00 +02:00
|
|
|
fprintf(stderr, "Meta_create(): calloc failure!\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2019-04-21 01:42:32 +02:00
|
|
|
return Meta_write(cf);
|
|
|
|
}
|
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
static int Meta_read(Cache *cf)
|
2019-04-12 14:52:11 +02:00
|
|
|
{
|
2019-04-18 10:48:40 +02:00
|
|
|
FILE *fp;
|
2019-04-22 03:43:48 +02:00
|
|
|
char *metafn = strndupcat(META_DIR, cf->p_url, MAX_PATH_LEN);
|
2019-04-20 03:04:29 +02:00
|
|
|
fp = fopen(metafn, "r");
|
|
|
|
free(metafn);
|
2019-04-21 00:46:08 +02:00
|
|
|
int res = 0;
|
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-21 00:46:08 +02:00
|
|
|
return -1;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
fread(&(cf->time), sizeof(long), 1, fp);
|
2019-04-22 10:50:53 +02:00
|
|
|
fread(&(cf->content_length), sizeof(off_t), 1, fp);
|
2019-04-22 03:43:48 +02:00
|
|
|
fread(&(cf->blksz), sizeof(int), 1, fp);
|
|
|
|
fread(&(cf->segbc), sizeof(long), 1, fp);
|
2019-04-18 10:48:40 +02:00
|
|
|
|
2019-04-21 11:57:00 +02:00
|
|
|
/* Allocate some memory for the segment */
|
2019-04-21 21:48:44 +02:00
|
|
|
cf->seg = malloc(cf->segbc * sizeof(Seg));
|
2019-04-22 12:08:42 +02:00
|
|
|
if (!cf->seg) {
|
2019-04-21 11:57:00 +02:00
|
|
|
fprintf(stderr, "Meta_read(): malloc failure!\n");
|
|
|
|
exit(EXIT_FAILURE);
|
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-20 03:04:29 +02:00
|
|
|
/* Error checking for fread */
|
2019-04-18 10:48:40 +02:00
|
|
|
if (ferror(fp)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Meta_read(): fread(): encountered error (from ferror)!\n");
|
2019-04-21 00:46:08 +02:00
|
|
|
res = -1;
|
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-21 21:48:44 +02:00
|
|
|
res = -2;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fclose(fp)) {
|
|
|
|
fprintf(stderr, "Meta_read(): fclose(): %s\n", strerror(errno));
|
|
|
|
}
|
2019-04-20 03:04:29 +02:00
|
|
|
return res;
|
2019-04-12 14:52:11 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
static int Meta_write(const Cache *cf)
|
2019-04-18 10:48:40 +02:00
|
|
|
{
|
|
|
|
FILE *fp;
|
2019-04-22 03:43:48 +02:00
|
|
|
char *metafn = strndupcat(META_DIR, cf->p_url, MAX_PATH_LEN);
|
2019-04-20 03:04:29 +02:00
|
|
|
fp = fopen(metafn, "w");
|
|
|
|
free(metafn);
|
2019-04-21 00:46:08 +02:00
|
|
|
int res = 0;
|
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-20 03:04:29 +02:00
|
|
|
fwrite(&(cf->time), sizeof(long), 1, fp);
|
2019-04-22 10:50:53 +02:00
|
|
|
fwrite(&(cf->content_length), sizeof(off_t), 1, fp);
|
2019-04-21 11:57:00 +02:00
|
|
|
fwrite(&(cf->blksz), sizeof(int), 1, fp);
|
2019-04-22 03:43:48 +02:00
|
|
|
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-21 00:46:08 +02:00
|
|
|
res = -1;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fclose(fp)) {
|
|
|
|
fprintf(stderr, "Meta_write(): fclose(): %s\n", strerror(errno));
|
|
|
|
}
|
2019-04-20 03:04:29 +02:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
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-22 03:43:48 +02:00
|
|
|
char *datafn = strndupcat(DATA_DIR, cf->p_url, MAX_PATH_LEN);
|
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 03:33:26 +02:00
|
|
|
static long Data_size(const char *fn)
|
2019-04-21 00:46:08 +02:00
|
|
|
{
|
|
|
|
char *datafn = strndupcat(DATA_DIR, fn, MAX_PATH_LEN);
|
|
|
|
struct stat st;
|
2019-04-21 11:57:00 +02:00
|
|
|
int s = stat(datafn, &st);
|
|
|
|
free(datafn);
|
|
|
|
if (!s) {
|
2019-04-21 00:46:08 +02:00
|
|
|
return st.st_blksize;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "Data_size(): stat(): %s\n", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
static long Data_read(const Cache *cf, long offset, long len,
|
2019-04-21 00:46:08 +02:00
|
|
|
uint8_t *buf)
|
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-17 18:38:27 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *fp;
|
2019-04-22 03:43:48 +02:00
|
|
|
char *datafn = strndupcat(DATA_DIR, cf->p_url, MAX_PATH_LEN);
|
2019-04-20 03:04:29 +02:00
|
|
|
fp = fopen(datafn, "r");
|
|
|
|
free(datafn);
|
|
|
|
long byte_read = -1;
|
2019-04-17 18:38:27 +02:00
|
|
|
|
|
|
|
if (!fp) {
|
2019-04-20 03:04:29 +02:00
|
|
|
/* Failed to open the data file */
|
2019-04-18 10:48:40 +02:00
|
|
|
fprintf(stderr, "Data_read(): fopen(): %s\n", strerror(errno));
|
2019-04-17 18:38:27 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-04-22 10:50:53 +02:00
|
|
|
if (fseeko(fp, offset, SEEK_SET)) {
|
|
|
|
/* fseeko failed */
|
|
|
|
fprintf(stderr, "Data_read(): fseeko(): %s\n", strerror(errno));
|
2019-04-20 03:04:29 +02:00
|
|
|
goto cleanup;
|
2019-04-17 18:38:27 +02:00
|
|
|
}
|
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
byte_read = fread(buf, sizeof(uint8_t), len, fp);
|
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);
|
|
|
|
if (feof(fp)) {
|
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");
|
|
|
|
}
|
|
|
|
if (ferror(fp)) {
|
2019-04-18 10:48:40 +02:00
|
|
|
/* filesystem error */
|
2019-04-17 18:38:27 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"Data_read(): fread(): encountered error (from ferror)!\n");
|
|
|
|
}
|
|
|
|
}
|
2019-04-20 03:04:29 +02:00
|
|
|
|
|
|
|
cleanup:
|
2019-04-17 18:38:27 +02:00
|
|
|
if (fclose(fp)) {
|
|
|
|
fprintf(stderr, "Data_read(): fclose(): %s\n", strerror(errno));
|
|
|
|
}
|
2019-04-20 03:04:29 +02:00
|
|
|
return byte_read;
|
2019-04-12 14:52:11 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
static long Data_write(const Cache *cf, long offset, long len,
|
2019-04-21 00:46:08 +02:00
|
|
|
const uint8_t *buf)
|
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");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *fp;
|
2019-04-22 03:43:48 +02:00
|
|
|
char *datafn = strndupcat(DATA_DIR, cf->p_url, MAX_PATH_LEN);
|
2019-04-20 03:04:29 +02:00
|
|
|
fp = fopen(datafn, "r+");
|
|
|
|
free(datafn);
|
|
|
|
long byte_written = -1;
|
2019-04-18 10:48:40 +02:00
|
|
|
|
|
|
|
if (!fp) {
|
2019-04-20 03:04:29 +02:00
|
|
|
/* Failed to open the data file */
|
|
|
|
fprintf(stderr, "Data_write(): fopen(): %s\n", strerror(errno));
|
2019-04-18 10:48:40 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-04-22 10:50:53 +02:00
|
|
|
if (fseeko(fp, offset, SEEK_SET)) {
|
|
|
|
/* fseeko failed */
|
|
|
|
fprintf(stderr, "Data_write(): fseeko(): %s\n", strerror(errno));
|
2019-04-20 03:04:29 +02:00
|
|
|
goto cleanup;
|
2019-04-18 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-20 03:04:29 +02:00
|
|
|
byte_written = fwrite(buf, sizeof(uint8_t), len, fp);
|
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);
|
|
|
|
if (feof(fp)) {
|
|
|
|
/* reached EOF */
|
|
|
|
fprintf(stderr,
|
|
|
|
"Data_write(): fwrite(): reached the end of the file!\n");
|
|
|
|
}
|
|
|
|
if (ferror(fp)) {
|
|
|
|
/* filesystem error */
|
|
|
|
fprintf(stderr,
|
|
|
|
"Data_write(): fwrite(): encountered error (from ferror)!\n");
|
|
|
|
}
|
|
|
|
}
|
2019-04-20 03:04:29 +02:00
|
|
|
|
|
|
|
cleanup:
|
2019-04-18 10:48:40 +02:00
|
|
|
if (fclose(fp)) {
|
|
|
|
fprintf(stderr, "Data_write(): fclose(): %s\n", strerror(errno));
|
|
|
|
}
|
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)
|
|
|
|
{
|
|
|
|
char *metadirn = strndupcat(META_DIR, dirn, MAX_PATH_LEN);
|
|
|
|
char *datadirn = strndupcat(DATA_DIR, dirn, MAX_PATH_LEN);
|
|
|
|
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 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);
|
|
|
|
}
|
|
|
|
return cf;
|
|
|
|
}
|
|
|
|
|
2019-04-22 12:08:42 +02:00
|
|
|
void Cache_close(Cache *cf)
|
|
|
|
{
|
|
|
|
return Cache_free(cf);
|
|
|
|
}
|
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
static void Cache_free(Cache *cf)
|
2019-04-21 21:48:44 +02:00
|
|
|
{
|
2019-04-22 03:43:48 +02:00
|
|
|
if (cf->p_url) {
|
|
|
|
free(cf->p_url);
|
2019-04-21 21:48:44 +02:00
|
|
|
}
|
|
|
|
if (cf->seg) {
|
|
|
|
free(cf->seg);
|
|
|
|
}
|
|
|
|
free(cf);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
char *metafn = strndupcat(META_DIR, fn, MAX_PATH_LEN);
|
|
|
|
char *datafn = strndupcat(DATA_DIR, fn, MAX_PATH_LEN);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
return !(meta_exists & data_exists);
|
|
|
|
}
|
|
|
|
|
2019-04-22 03:59:53 +02:00
|
|
|
int Cache_create(const char *fn, long len, long time)
|
2019-04-21 21:48:44 +02:00
|
|
|
{
|
|
|
|
Cache *cf = Cache_alloc();
|
|
|
|
|
2019-04-22 03:43:48 +02:00
|
|
|
cf->p_url = strndup(fn, MAX_PATH_LEN);
|
2019-04-21 21:48:44 +02:00
|
|
|
cf->time = time;
|
2019-04-22 03:43:48 +02:00
|
|
|
cf->content_length = len;
|
2019-04-21 21:48:44 +02:00
|
|
|
|
|
|
|
if (Data_create(cf)) {
|
|
|
|
fprintf(stderr, "Cache_create(): Data_create() failed!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Meta_create(cf)) {
|
|
|
|
fprintf(stderr, "Cache_create(): Meta_create() failed!\n");
|
|
|
|
}
|
2019-04-22 03:59:53 +02:00
|
|
|
|
|
|
|
Cache_free(cf);
|
|
|
|
|
|
|
|
return Cache_exist(fn);
|
2019-04-21 21:48:44 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 03:33:26 +02:00
|
|
|
static void Cache_delete(const char *fn)
|
2019-04-21 21:48:44 +02:00
|
|
|
{
|
|
|
|
char *metafn = strndupcat(META_DIR, fn, MAX_PATH_LEN);
|
|
|
|
char *datafn = strndupcat(DATA_DIR, fn, MAX_PATH_LEN);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
Cache *Cache_open(const char *fn)
|
|
|
|
{
|
|
|
|
/* Check if both metadata and data file exist */
|
|
|
|
if (Cache_exist(fn)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the cache in-memory data structure */
|
|
|
|
Cache *cf = Cache_alloc();
|
2019-04-22 03:43:48 +02:00
|
|
|
cf->p_url = strndup(fn, MAX_PATH_LEN);
|
2019-04-21 21:48:44 +02:00
|
|
|
|
|
|
|
/* Internal inconsistency metadata file */
|
|
|
|
if (Meta_read(cf) == -2) {
|
|
|
|
Cache_free(cf);
|
|
|
|
Cache_delete(fn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Inconsistency between metadata and data file */
|
2019-04-22 03:43:48 +02:00
|
|
|
if (cf->content_length != Data_size(fn)) {
|
2019-04-21 21:48:44 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"Cache_open(): metadata is inconsistent with the data file!\n");
|
|
|
|
Cache_free(cf);
|
|
|
|
Cache_delete(fn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cf;
|
|
|
|
}
|