mirror of
https://github.com/xorgy/mediafire-fuse
synced 2026-01-13 13:14:29 -08:00
on startup, check integrity of cached files and remove old cached files
This commit is contained in:
2
TODO
2
TODO
@@ -15,9 +15,7 @@
|
|||||||
- make h_entry more efficient in memory (for example by making filenames zero terminated)
|
- make h_entry more efficient in memory (for example by making filenames zero terminated)
|
||||||
- when handling device/get_changes, make sure to only use the latest revision of the same file-/folderkey
|
- when handling device/get_changes, make sure to only use the latest revision of the same file-/folderkey
|
||||||
- allow different cache directory (useful for running test suite)
|
- allow different cache directory (useful for running test suite)
|
||||||
- delete old files in cache that have been updated
|
|
||||||
- delete patches in cache that have been applied
|
- delete patches in cache that have been applied
|
||||||
- check the hash of all files in the cache on startup
|
|
||||||
- after uploading a file it is immediately downloaded - instead, the existing local file should be used by checking the remote hash
|
- after uploading a file it is immediately downloaded - instead, the existing local file should be used by checking the remote hash
|
||||||
- add an option to only call device/get_status in configurable intervals
|
- add an option to only call device/get_status in configurable intervals
|
||||||
- write man pages
|
- write man pages
|
||||||
|
|||||||
228
fuse/hashtbl.c
228
fuse/hashtbl.c
@@ -36,6 +36,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "hashtbl.h"
|
#include "hashtbl.h"
|
||||||
#include "filecache.h"
|
#include "filecache.h"
|
||||||
@@ -147,6 +149,9 @@ static struct h_entry *folder_tree_add_folder(folder_tree * tree,
|
|||||||
static void folder_tree_remove(folder_tree * tree, const char *key);
|
static void folder_tree_remove(folder_tree * tree, const char *key);
|
||||||
static bool folder_tree_is_parent_of(struct h_entry *parent,
|
static bool folder_tree_is_parent_of(struct h_entry *parent,
|
||||||
struct h_entry *child);
|
struct h_entry *child);
|
||||||
|
static bool is_valid_cache_filename(const char *name, char key[],
|
||||||
|
uint64_t *revision);
|
||||||
|
static int atime_compare(const void *a, const void *b);
|
||||||
|
|
||||||
/* functions with remote access */
|
/* functions with remote access */
|
||||||
static struct h_entry *folder_tree_lookup_path(folder_tree * tree,
|
static struct h_entry *folder_tree_lookup_path(folder_tree * tree,
|
||||||
@@ -793,6 +798,9 @@ int folder_tree_open_file(folder_tree * tree, mfconn * conn, const char *path,
|
|||||||
entry->local_revision = entry->remote_revision;
|
entry->local_revision = entry->remote_revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// however the file was opened, its access time has to be updated
|
||||||
|
entry->atime = time(NULL);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1746,3 +1754,223 @@ void folder_tree_debug(folder_tree * tree)
|
|||||||
{
|
{
|
||||||
folder_tree_debug_helper(tree, NULL, 0);
|
folder_tree_debug_helper(tree, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int atime_compare(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return ((struct h_entry *)a)->atime - ((struct h_entry *)b)->atime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* to be a valid cache file, the first 15 bytes have to be letters
|
||||||
|
* from a-z and numbers from 0-9, the 16th has to be an underscore,
|
||||||
|
* the 17th has to be a number from 1-9 and the remaining characters
|
||||||
|
* (if any) be a number from 0-9
|
||||||
|
*/
|
||||||
|
static bool is_valid_cache_filename(const char *name, char key[],
|
||||||
|
uint64_t *revision)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 15; i++) {
|
||||||
|
if (!islower(name[i]) && !isdigit(name[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (name[i] != '_')
|
||||||
|
return false;
|
||||||
|
i++;
|
||||||
|
if (name[i] < 49 || name[i] > 57)
|
||||||
|
return false;
|
||||||
|
for (; name[i] != '\0'; i++) {
|
||||||
|
if (!isdigit(name[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now copy the first 15 bytes from the name to the key
|
||||||
|
memcpy(key, name, 15);
|
||||||
|
key[15] = '\0';
|
||||||
|
|
||||||
|
*revision = atoll(name+16);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* go through all files in the filecache and check:
|
||||||
|
*
|
||||||
|
* - does the filename match the known pattern?
|
||||||
|
* (do not act on other files to avoid accidentally touching user
|
||||||
|
* files)
|
||||||
|
* - is the quickkey known by the hashtable?
|
||||||
|
* - if no, delete
|
||||||
|
* - check if its revision is equal the remote revision
|
||||||
|
* - if no, delete
|
||||||
|
* - check if its size and hash verifies
|
||||||
|
* - if no, delete
|
||||||
|
* - once all files in the cache have been processed this way, check if
|
||||||
|
* the sum of their sizes is greater than X and delete the oldest
|
||||||
|
*/
|
||||||
|
void folder_tree_cleanup_filecache(folder_tree *tree, uint64_t allowed_size)
|
||||||
|
{
|
||||||
|
struct dirent *endp;
|
||||||
|
struct dirent *entryp;
|
||||||
|
DIR *dirp;
|
||||||
|
int retval;
|
||||||
|
long name_max;
|
||||||
|
char *filepath;
|
||||||
|
char key[MFAPI_MAX_LEN_KEY + 1];
|
||||||
|
uint64_t revision;
|
||||||
|
struct h_entry *entry;
|
||||||
|
size_t num_cachefiles;
|
||||||
|
size_t i;
|
||||||
|
uint64_t sum_size;
|
||||||
|
struct h_entry **cachefiles;
|
||||||
|
|
||||||
|
// from the readdir_r man page
|
||||||
|
name_max = pathconf(tree->filecache, _PC_NAME_MAX);
|
||||||
|
if (name_max == -1) /* Limit not defined, or error */
|
||||||
|
name_max = 255; /* Take a guess */
|
||||||
|
entryp = malloc(offsetof(struct dirent, d_name) + name_max + 1);
|
||||||
|
|
||||||
|
dirp = opendir(tree->filecache);
|
||||||
|
if (dirp == NULL) {
|
||||||
|
fprintf(stderr, "cannot open filecache\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_cachefiles = 0;
|
||||||
|
cachefiles = NULL;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
endp = NULL;
|
||||||
|
retval = readdir_r(dirp, entryp, &endp);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "readdir_r failed\n");
|
||||||
|
free(entryp);
|
||||||
|
closedir(dirp);
|
||||||
|
if (cachefiles != NULL)
|
||||||
|
free(cachefiles);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (endp == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strcmp(entryp->d_name, ".") == 0 ||
|
||||||
|
strcmp(entryp->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!is_valid_cache_filename(entryp->d_name, key, &revision)) {
|
||||||
|
fprintf(stderr, "not a valid cachefile: %s (ignoring)\n",
|
||||||
|
entryp->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
filepath = strdup_printf("%s/%s", tree->filecache, entryp->d_name);
|
||||||
|
|
||||||
|
entry = folder_tree_lookup_key(tree, key);
|
||||||
|
if (entry == NULL) {
|
||||||
|
fprintf(stderr, "delete file not in hashtable: %s\n",
|
||||||
|
entryp->d_name);
|
||||||
|
retval = unlink(filepath);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "unlink failed\n");
|
||||||
|
}
|
||||||
|
free(filepath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (revision != entry->remote_revision) {
|
||||||
|
fprintf(stderr, "delete file with revision %" PRIu64
|
||||||
|
" different from remote %" PRIu64 ": %s\n", revision,
|
||||||
|
entry->remote_revision, entryp->d_name);
|
||||||
|
retval = unlink(filepath);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "unlink failed\n");
|
||||||
|
}
|
||||||
|
entry->local_revision = 0;
|
||||||
|
free(filepath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (revision != entry->local_revision) {
|
||||||
|
fprintf(stderr, "delete file with revision %" PRIu64
|
||||||
|
" different from local %" PRIu64 ": %s\n", revision,
|
||||||
|
entry->local_revision, entryp->d_name);
|
||||||
|
retval = unlink(filepath);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "unlink failed\n");
|
||||||
|
}
|
||||||
|
entry->local_revision = 0;
|
||||||
|
free(filepath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = file_check_integrity(filepath, entry->fsize, entry->hash);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "delete file with invalid content: %s\n",
|
||||||
|
entryp->d_name);
|
||||||
|
retval = unlink(filepath);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "unlink failed\n");
|
||||||
|
}
|
||||||
|
entry->local_revision = 0;
|
||||||
|
free(filepath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free(filepath);
|
||||||
|
|
||||||
|
// everything is okay with this one, so append it to the list of files
|
||||||
|
// in the cache
|
||||||
|
num_cachefiles++;
|
||||||
|
cachefiles =
|
||||||
|
(struct h_entry **)realloc(cachefiles,
|
||||||
|
num_cachefiles *
|
||||||
|
sizeof(struct h_entry *));
|
||||||
|
if (cachefiles == NULL) {
|
||||||
|
fprintf(stderr, "realloc failed\n");
|
||||||
|
free(entryp);
|
||||||
|
closedir(dirp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cachefiles[num_cachefiles - 1] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(entryp);
|
||||||
|
closedir(dirp);
|
||||||
|
|
||||||
|
// return if there are no files in the cache
|
||||||
|
if (num_cachefiles == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// now calculate the sum of valid files in the cache and check whether it
|
||||||
|
// is larger than allowed
|
||||||
|
sum_size = 0;
|
||||||
|
for (i = 0; i < num_cachefiles; i++) {
|
||||||
|
sum_size += cachefiles[i]->fsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the summed size is below the allowed, return
|
||||||
|
if (sum_size <= allowed_size) {
|
||||||
|
free(cachefiles);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the files in the cache by their access time
|
||||||
|
qsort(cachefiles, num_cachefiles, sizeof(struct h_entry *), atime_compare);
|
||||||
|
|
||||||
|
// delete the oldest file until sum is below allowed size
|
||||||
|
for (i = 0; i < num_cachefiles && sum_size > allowed_size; i++) {
|
||||||
|
entry = cachefiles[i];
|
||||||
|
fprintf(stderr, "delete file to free space: %s_%" PRIu64 "\n",
|
||||||
|
entry->key, entry->remote_revision);
|
||||||
|
filepath = strdup_printf("%s/%s_%" PRIu64, tree->filecache, entry->key,
|
||||||
|
entry->remote_revision);
|
||||||
|
retval = unlink(filepath);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "unlink failed\n");
|
||||||
|
}
|
||||||
|
entry->local_revision = 0;
|
||||||
|
free(filepath);
|
||||||
|
sum_size -= entry->fsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cachefiles);
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ int folder_tree_store(folder_tree * tree, FILE * stream);
|
|||||||
|
|
||||||
folder_tree *folder_tree_load(FILE * stream, const char *filecache);
|
folder_tree *folder_tree_load(FILE * stream, const char *filecache);
|
||||||
|
|
||||||
|
void folder_tree_cleanup_filecache(folder_tree *tree,
|
||||||
|
uint64_t allowed_size);
|
||||||
|
|
||||||
bool folder_tree_path_exists(folder_tree * tree, mfconn * conn,
|
bool folder_tree_path_exists(folder_tree * tree, mfconn * conn,
|
||||||
const char *path);
|
const char *path);
|
||||||
|
|
||||||
|
|||||||
@@ -319,6 +319,10 @@ static void open_hashtbl(const char *dircache, const char *filecache,
|
|||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
|
// TODO: make the maximum cache size configurable
|
||||||
|
// size is given in bytes and current default is 1 GiB
|
||||||
|
folder_tree_cleanup_filecache(*tree, 1073741824);
|
||||||
|
|
||||||
folder_tree_update(*tree, conn, false);
|
folder_tree_update(*tree, conn, false);
|
||||||
} else {
|
} else {
|
||||||
// file doesn't exist
|
// file doesn't exist
|
||||||
@@ -456,7 +460,7 @@ int main(int argc, char *argv[])
|
|||||||
ctx->sv_writefiles = stringv_alloc();
|
ctx->sv_writefiles = stringv_alloc();
|
||||||
ctx->sv_readonlyfiles = stringv_alloc();
|
ctx->sv_readonlyfiles = stringv_alloc();
|
||||||
ctx->last_status_check = 0;
|
ctx->last_status_check = 0;
|
||||||
ctx->interval_status_check = 60;
|
ctx->interval_status_check = 60; // TODO: make this configurable
|
||||||
|
|
||||||
pthread_mutex_init(&(ctx->mutex), NULL);
|
pthread_mutex_init(&(ctx->mutex), NULL);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user