mirror of
https://github.com/xorgy/mediafire-fuse
synced 2026-01-13 21:24:28 -08:00
make synchronization with remote more robust
This commit is contained in:
527
fuse/hashtbl.c
527
fuse/hashtbl.c
@@ -136,6 +136,34 @@ struct folder_tree {
|
|||||||
struct h_entry root;
|
struct h_entry root;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* static functions local to this file */
|
||||||
|
|
||||||
|
static void folder_tree_free_entries(folder_tree * tree);
|
||||||
|
static struct h_entry *folder_tree_lookup_key(folder_tree * tree,
|
||||||
|
const char *key);
|
||||||
|
static struct h_entry *folder_tree_lookup_path(folder_tree * tree,
|
||||||
|
const char *path);
|
||||||
|
static bool folder_tree_is_root(struct h_entry *entry);
|
||||||
|
static struct h_entry *folder_tree_allocate_entry(folder_tree * tree,
|
||||||
|
const char *key,
|
||||||
|
struct h_entry *new_parent);
|
||||||
|
static struct h_entry *folder_tree_add_file(folder_tree * tree, mffile * file,
|
||||||
|
struct h_entry *new_parent);
|
||||||
|
static struct h_entry *folder_tree_add_folder(folder_tree * tree,
|
||||||
|
mfconn * conn,
|
||||||
|
mffolder * folder,
|
||||||
|
struct h_entry *new_parent);
|
||||||
|
static int folder_tree_rebuild_helper(folder_tree * tree, mfconn * conn,
|
||||||
|
struct h_entry *curr_entry,
|
||||||
|
bool recurse);
|
||||||
|
static void folder_tree_remove(folder_tree * tree, const char *key);
|
||||||
|
static bool folder_tree_is_parent_of(struct h_entry *parent,
|
||||||
|
struct h_entry *child);
|
||||||
|
static int folder_tree_update_file_info(folder_tree * tree, mfconn * conn,
|
||||||
|
const char *key);
|
||||||
|
static int folder_tree_update_folder_info(folder_tree * tree,
|
||||||
|
mfconn * conn, char *key);
|
||||||
|
|
||||||
/* persistant storage file layout:
|
/* persistant storage file layout:
|
||||||
*
|
*
|
||||||
* byte 0: 0x4D -> ASCII M
|
* byte 0: 0x4D -> ASCII M
|
||||||
@@ -728,7 +756,7 @@ static struct h_entry *folder_tree_allocate_entry(folder_tree * tree,
|
|||||||
}
|
}
|
||||||
tree->buckets[bucket_id][tree->bucket_lens[bucket_id] - 1] = entry;
|
tree->buckets[bucket_id][tree->bucket_lens[bucket_id] - 1] = entry;
|
||||||
|
|
||||||
/* since this entry is new, add it to the children of its parent
|
/* since this entry is new, just add it to the children of its parent
|
||||||
*
|
*
|
||||||
* since the key of this file or folder did not exist in the
|
* since the key of this file or folder did not exist in the
|
||||||
* hashtable, we do not have to check whether the parent already has
|
* hashtable, we do not have to check whether the parent already has
|
||||||
@@ -748,15 +776,10 @@ static struct h_entry *folder_tree_allocate_entry(folder_tree * tree,
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Entry was found so check if the old parent is different from the
|
/* Entry was found, so remove the entry from the children of the old
|
||||||
* new parent. If yes, we need to adjust the children of the old and new
|
* parent and add it to the children of the new parent */
|
||||||
* parent.
|
|
||||||
*/
|
|
||||||
|
|
||||||
old_parent = entry->parent;
|
old_parent = entry->parent;
|
||||||
/* parent stays the same - nothing to do */
|
|
||||||
if (old_parent == new_parent)
|
|
||||||
return entry;
|
|
||||||
|
|
||||||
/* check whether entry does not have a parent (this is the case for the
|
/* check whether entry does not have a parent (this is the case for the
|
||||||
* root node) */
|
* root node) */
|
||||||
@@ -765,12 +788,12 @@ static struct h_entry *folder_tree_allocate_entry(folder_tree * tree,
|
|||||||
for (i = 0; i < old_parent->num_children; i++) {
|
for (i = 0; i < old_parent->num_children; i++) {
|
||||||
if (old_parent->children[i] == entry) {
|
if (old_parent->children[i] == entry) {
|
||||||
/* move the entries on the right one place to the left */
|
/* move the entries on the right one place to the left */
|
||||||
memmove(old_parent->children[i], old_parent->children[i + 1],
|
memmove(old_parent->children + i, old_parent->children + i + 1,
|
||||||
sizeof(struct h_entry *) * (old_parent->num_children -
|
sizeof(struct h_entry *) * (old_parent->num_children -
|
||||||
i - 1));
|
i - 1));
|
||||||
old_parent->num_children--;
|
old_parent->num_children--;
|
||||||
/* change the children size */
|
/* change the children size */
|
||||||
if (old_parent->num_children) {
|
if (old_parent->num_children == 0) {
|
||||||
free(old_parent->children);
|
free(old_parent->children);
|
||||||
old_parent->children = NULL;
|
old_parent->children = NULL;
|
||||||
} else {
|
} else {
|
||||||
@@ -829,7 +852,7 @@ static struct h_entry *folder_tree_allocate_entry(folder_tree * tree,
|
|||||||
* Return the inserted or updated key
|
* Return the inserted or updated key
|
||||||
*/
|
*/
|
||||||
static struct h_entry *folder_tree_add_file(folder_tree * tree, mffile * file,
|
static struct h_entry *folder_tree_add_file(folder_tree * tree, mffile * file,
|
||||||
struct h_entry *parent)
|
struct h_entry *new_parent)
|
||||||
{
|
{
|
||||||
struct h_entry *entry;
|
struct h_entry *entry;
|
||||||
const char *key;
|
const char *key;
|
||||||
@@ -844,18 +867,18 @@ static struct h_entry *folder_tree_add_file(folder_tree * tree, mffile * file,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent == NULL) {
|
if (new_parent == NULL) {
|
||||||
fprintf(stderr, "parent cannot be NULL\n");
|
fprintf(stderr, "new parent cannot be NULL\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = file_get_key(file);
|
key = file_get_key(file);
|
||||||
|
|
||||||
entry = folder_tree_allocate_entry(tree, key, parent);
|
entry = folder_tree_allocate_entry(tree, key, new_parent);
|
||||||
|
|
||||||
strncpy(entry->key, key, sizeof(entry->key));
|
strncpy(entry->key, key, sizeof(entry->key));
|
||||||
strncpy(entry->name, file_get_name(file), sizeof(entry->name));
|
strncpy(entry->name, file_get_name(file), sizeof(entry->name));
|
||||||
entry->parent = parent;
|
entry->parent = new_parent;
|
||||||
entry->revision = file_get_revision(file);
|
entry->revision = file_get_revision(file);
|
||||||
entry->ctime = file_get_created(file);
|
entry->ctime = file_get_created(file);
|
||||||
entry->fsize = file_get_size(file);
|
entry->fsize = file_get_size(file);
|
||||||
@@ -870,15 +893,21 @@ static struct h_entry *folder_tree_add_file(folder_tree * tree, mffile * file,
|
|||||||
/* given an mffolder, add its information to a new h_entry struct, or update an
|
/* given an mffolder, add its information to a new h_entry struct, or update an
|
||||||
* existing h_entry struct in the hashtable
|
* existing h_entry struct in the hashtable
|
||||||
*
|
*
|
||||||
|
* if the revision of the existing entry was found to be less than the new
|
||||||
|
* entry, also update its contents
|
||||||
|
*
|
||||||
* returns a pointer to the added or updated h_entry struct
|
* returns a pointer to the added or updated h_entry struct
|
||||||
*/
|
*/
|
||||||
static struct h_entry *folder_tree_add_folder(folder_tree * tree,
|
static struct h_entry *folder_tree_add_folder(folder_tree * tree,
|
||||||
|
mfconn * conn,
|
||||||
mffolder * folder,
|
mffolder * folder,
|
||||||
struct h_entry *parent)
|
struct h_entry *new_parent)
|
||||||
{
|
{
|
||||||
struct h_entry *entry;
|
struct h_entry *new_entry;
|
||||||
const char *key;
|
const char *key;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
uint64_t old_revision;
|
||||||
|
struct h_entry *old_entry;
|
||||||
|
|
||||||
if (tree == NULL) {
|
if (tree == NULL) {
|
||||||
fprintf(stderr, "tree cannot be NULL\n");
|
fprintf(stderr, "tree cannot be NULL\n");
|
||||||
@@ -890,27 +919,140 @@ static struct h_entry *folder_tree_add_folder(folder_tree * tree,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent == NULL) {
|
if (new_parent == NULL) {
|
||||||
fprintf(stderr, "parent cannot be NULL\n");
|
fprintf(stderr, "new parent cannot be NULL\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = folder_get_key(folder);
|
key = folder_get_key(folder);
|
||||||
|
|
||||||
entry = folder_tree_allocate_entry(tree, key, parent);
|
/* if the folder already existed in the hashtable, store its old revision
|
||||||
|
* so that we can schedule an update of its content at the end of this
|
||||||
|
* function */
|
||||||
|
old_entry = folder_tree_lookup_key(tree, key);
|
||||||
|
if (old_entry != NULL) {
|
||||||
|
old_revision = old_entry->revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_entry = folder_tree_allocate_entry(tree, key, new_parent);
|
||||||
|
|
||||||
/* can be NULL for root */
|
/* can be NULL for root */
|
||||||
if (key != NULL)
|
if (key != NULL)
|
||||||
strncpy(entry->key, key, sizeof(entry->key));
|
strncpy(new_entry->key, key, sizeof(new_entry->key));
|
||||||
/* can be NULL for root */
|
/* can be NULL for root */
|
||||||
name = folder_get_name(folder);
|
name = folder_get_name(folder);
|
||||||
if (name != NULL)
|
if (name != NULL)
|
||||||
strncpy(entry->name, name, sizeof(entry->name));
|
strncpy(new_entry->name, name, sizeof(new_entry->name));
|
||||||
entry->revision = folder_get_revision(folder);
|
new_entry->revision = folder_get_revision(folder);
|
||||||
entry->ctime = folder_get_created(folder);
|
new_entry->ctime = folder_get_created(folder);
|
||||||
entry->parent = parent;
|
new_entry->parent = new_parent;
|
||||||
|
|
||||||
return entry;
|
/* if the revisions of the old and new entry differ, we have to
|
||||||
|
* update its content */
|
||||||
|
if (old_entry != NULL && old_revision < new_entry->revision) {
|
||||||
|
folder_tree_rebuild_helper(tree, conn, new_entry, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* given a h_entry struct of a folder, this function gets the remote content
|
||||||
|
* of that folder and fills its children
|
||||||
|
*
|
||||||
|
* it then recurses for each child that is a directory and does the same in a
|
||||||
|
* full remote directory walk
|
||||||
|
*/
|
||||||
|
static int folder_tree_rebuild_helper(folder_tree * tree, mfconn * conn,
|
||||||
|
struct h_entry *curr_entry, bool recurse)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
mffolder **folder_result;
|
||||||
|
mffile **file_result;
|
||||||
|
struct h_entry *entry;
|
||||||
|
int i;
|
||||||
|
const char *key;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free the old children array of this folder to make sure that any
|
||||||
|
* entries that do not exist on the remote are removed locally
|
||||||
|
*
|
||||||
|
* we don't free the children it references because they might be
|
||||||
|
* referenced by someone else
|
||||||
|
*
|
||||||
|
* this action will leave all those entries dangling (with a reference to
|
||||||
|
* this folder as their parent) which have been completely removed remotely
|
||||||
|
* (including from the trash) and thus did not show up in a
|
||||||
|
* device/get_changes call. All these entries will be cleaned up by the
|
||||||
|
* housekeeping function
|
||||||
|
*/
|
||||||
|
free(curr_entry->children);
|
||||||
|
curr_entry->children = NULL;
|
||||||
|
curr_entry->num_children = 0;
|
||||||
|
|
||||||
|
/* first folders */
|
||||||
|
folder_result = NULL;
|
||||||
|
retval =
|
||||||
|
mfconn_api_folder_get_content(conn, 0, curr_entry->key, &folder_result,
|
||||||
|
NULL);
|
||||||
|
mfconn_update_secret_key(conn);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "folder/get_content failed\n");
|
||||||
|
if (folder_result != NULL) {
|
||||||
|
for (i = 0; folder_result[i] != NULL; i++) {
|
||||||
|
free(folder_result[i]);
|
||||||
|
}
|
||||||
|
free(folder_result);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; folder_result[i] != NULL; i++) {
|
||||||
|
key = folder_get_key(folder_result[i]);
|
||||||
|
if (key == NULL) {
|
||||||
|
fprintf(stderr, "folder_get_key returned NULL\n");
|
||||||
|
folder_free(folder_result[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry =
|
||||||
|
folder_tree_add_folder(tree, conn, folder_result[i], curr_entry);
|
||||||
|
/* recurse */
|
||||||
|
if (recurse)
|
||||||
|
folder_tree_rebuild_helper(tree, conn, entry, true);
|
||||||
|
folder_free(folder_result[i]);
|
||||||
|
}
|
||||||
|
free(folder_result);
|
||||||
|
|
||||||
|
/* then files */
|
||||||
|
file_result = NULL;
|
||||||
|
retval =
|
||||||
|
mfconn_api_folder_get_content(conn, 1, curr_entry->key, NULL,
|
||||||
|
&file_result);
|
||||||
|
mfconn_update_secret_key(conn);
|
||||||
|
if (retval != 0) {
|
||||||
|
fprintf(stderr, "folder/get_content failed\n");
|
||||||
|
if (file_result != NULL) {
|
||||||
|
for (i = 0; file_result[i] != NULL; i++) {
|
||||||
|
file_free(file_result[i]);
|
||||||
|
}
|
||||||
|
free(file_result);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; file_result[i] != NULL; i++) {
|
||||||
|
key = file_get_key(file_result[i]);
|
||||||
|
if (key == NULL) {
|
||||||
|
fprintf(stderr, "file_get_key returned NULL\n");
|
||||||
|
file_free(file_result[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = folder_tree_add_file(tree, file_result[i], curr_entry);
|
||||||
|
file_free(file_result[i]);
|
||||||
|
}
|
||||||
|
free(file_result);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When trying to delete a non-existing key, nothing happens */
|
/* When trying to delete a non-existing key, nothing happens */
|
||||||
@@ -919,9 +1061,11 @@ static void folder_tree_remove(folder_tree * tree, const char *key)
|
|||||||
int bucket_id;
|
int bucket_id;
|
||||||
int found;
|
int found;
|
||||||
uint64_t i;
|
uint64_t i;
|
||||||
|
struct h_entry *entry;
|
||||||
|
struct h_entry *parent;
|
||||||
|
|
||||||
if (key == NULL) {
|
if (key == NULL) {
|
||||||
fprintf(stderr, "cannot remove root");
|
fprintf(stderr, "cannot remove root\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,16 +1080,16 @@ static void folder_tree_remove(folder_tree * tree, const char *key)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
fprintf(stderr, "key was not found, removing nothing\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* if found, use the last value of i to adjust the bucket */
|
/* if found, use the last value of i to adjust the bucket */
|
||||||
if (found) {
|
entry = tree->buckets[bucket_id][i];
|
||||||
/* remove its possible children */
|
|
||||||
free(tree->buckets[bucket_id][i]->children);
|
|
||||||
/* remove entry */
|
|
||||||
free(tree->buckets[bucket_id][i]);
|
|
||||||
/* move the items on the right one place to the left */
|
/* move the items on the right one place to the left */
|
||||||
memmove(tree->buckets[bucket_id][i], tree->buckets[bucket_id][i + 1],
|
memmove(tree->buckets[bucket_id] + i, tree->buckets[bucket_id] + i + 1,
|
||||||
sizeof(struct h_entry *) * (tree->bucket_lens[bucket_id] - i -
|
sizeof(struct h_entry *) * (tree->bucket_lens[bucket_id] - i - 1));
|
||||||
1));
|
|
||||||
/* change bucket size */
|
/* change bucket size */
|
||||||
tree->bucket_lens[bucket_id]--;
|
tree->bucket_lens[bucket_id]--;
|
||||||
if (tree->bucket_lens[bucket_id] == 0) {
|
if (tree->bucket_lens[bucket_id] == 0) {
|
||||||
@@ -960,7 +1104,47 @@ static void folder_tree_remove(folder_tree * tree, const char *key)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if it is a folder, then we have to recurse into its children which
|
||||||
|
* reference this folder as their parent because otherwise their parent
|
||||||
|
* pointers will reference unallocated memory */
|
||||||
|
if (entry->num_children > 0) {
|
||||||
|
for (i = 0; i < entry->num_children; i++) {
|
||||||
|
if (entry->children[i]->parent == entry) {
|
||||||
|
folder_tree_remove(tree, entry->children[i]->key);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove the entry from its parent */
|
||||||
|
parent = entry->parent;
|
||||||
|
for (i = 0; i < parent->num_children; i++) {
|
||||||
|
if (entry->parent->children[i] == entry) {
|
||||||
|
/* move the entries on the right one place to the left */
|
||||||
|
memmove(parent->children + i, parent->children + i + 1,
|
||||||
|
sizeof(struct h_entry *) * (parent->num_children - i - 1));
|
||||||
|
parent->num_children--;
|
||||||
|
/* change the children size */
|
||||||
|
if (parent->num_children == 0) {
|
||||||
|
free(parent->children);
|
||||||
|
parent->children = NULL;
|
||||||
|
} else {
|
||||||
|
parent->children =
|
||||||
|
(struct h_entry **)realloc(parent->children,
|
||||||
|
parent->num_children *
|
||||||
|
sizeof(struct h_entry *));
|
||||||
|
if (parent->children == NULL) {
|
||||||
|
fprintf(stderr, "realloc failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove its possible children */
|
||||||
|
free(entry->children);
|
||||||
|
/* remove entry */
|
||||||
|
free(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -992,150 +1176,11 @@ static bool folder_tree_is_parent_of(struct h_entry *parent,
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* given a h_entry struct of a folder, this function gets the remote content
|
|
||||||
* of that folder and fills its children
|
|
||||||
*
|
|
||||||
* it then recurses for each child that is a directory and does the same in a
|
|
||||||
* full remote directory walk
|
|
||||||
*/
|
|
||||||
static int folder_tree_rebuild_helper(folder_tree * tree, mfconn * conn,
|
|
||||||
struct h_entry *curr_entry, bool recurse)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
mffolder **folder_result;
|
|
||||||
mffile **file_result;
|
|
||||||
struct h_entry *entry;
|
|
||||||
int i;
|
|
||||||
const char *key;
|
|
||||||
bool found;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* free the old children array of this folder to make sure that any
|
|
||||||
* entries that do not exist on the remote are removed locally
|
|
||||||
*
|
|
||||||
* we don't free the children it references because they might be
|
|
||||||
* referenced by someone else
|
|
||||||
*/
|
|
||||||
free(curr_entry->children);
|
|
||||||
curr_entry->children = NULL;
|
|
||||||
curr_entry->num_children = 0;
|
|
||||||
|
|
||||||
/* first folders */
|
|
||||||
folder_result = NULL;
|
|
||||||
retval =
|
|
||||||
mfconn_api_folder_get_content(conn, 0, curr_entry->key, &folder_result,
|
|
||||||
NULL);
|
|
||||||
mfconn_update_secret_key(conn);
|
|
||||||
if (retval != 0) {
|
|
||||||
fprintf(stderr, "folder/get_content failed\n");
|
|
||||||
if (folder_result != NULL) {
|
|
||||||
for (i = 0; folder_result[i] != NULL; i++) {
|
|
||||||
free(folder_result[i]);
|
|
||||||
}
|
|
||||||
free(folder_result);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; folder_result[i] != NULL; i++) {
|
|
||||||
key = folder_get_key(folder_result[i]);
|
|
||||||
if (key == NULL) {
|
|
||||||
fprintf(stderr, "folder_get_key returned NULL\n");
|
|
||||||
folder_free(folder_result[i]);
|
|
||||||
}
|
|
||||||
/* if this folder existed before, then folder_tree_add_folder will not
|
|
||||||
* add it to this folder's children. Thus we do this now */
|
|
||||||
found = false;
|
|
||||||
if (folder_tree_lookup_key(tree, key) != NULL) {
|
|
||||||
curr_entry->num_children++;
|
|
||||||
curr_entry->children =
|
|
||||||
(struct h_entry **)realloc(curr_entry->children,
|
|
||||||
curr_entry->num_children *
|
|
||||||
sizeof(struct h_entry *));
|
|
||||||
if (curr_entry->children == NULL) {
|
|
||||||
fprintf(stderr, "realloc failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
entry = folder_tree_add_folder(tree, folder_result[i], curr_entry);
|
|
||||||
/* we are using a new variable "found" here because after doing a
|
|
||||||
* folder_tree_add_folder, folder_tree_lookup_key will always succeed
|
|
||||||
*
|
|
||||||
* if this entry was not present before, then folder_tree_add_folder
|
|
||||||
* already added it to this folder's children
|
|
||||||
*/
|
|
||||||
if (found) {
|
|
||||||
curr_entry->children[curr_entry->num_children - 1] = entry;
|
|
||||||
}
|
|
||||||
/* recurse */
|
|
||||||
if (recurse)
|
|
||||||
folder_tree_rebuild_helper(tree, conn, entry, true);
|
|
||||||
folder_free(folder_result[i]);
|
|
||||||
}
|
|
||||||
free(folder_result);
|
|
||||||
|
|
||||||
/* then files */
|
|
||||||
file_result = NULL;
|
|
||||||
retval =
|
|
||||||
mfconn_api_folder_get_content(conn, 1, curr_entry->key, NULL,
|
|
||||||
&file_result);
|
|
||||||
mfconn_update_secret_key(conn);
|
|
||||||
if (retval != 0) {
|
|
||||||
fprintf(stderr, "folder/get_content failed\n");
|
|
||||||
if (file_result != NULL) {
|
|
||||||
for (i = 0; file_result[i] != NULL; i++) {
|
|
||||||
file_free(file_result[i]);
|
|
||||||
}
|
|
||||||
free(file_result);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; file_result[i] != NULL; i++) {
|
|
||||||
key = file_get_key(file_result[i]);
|
|
||||||
if (key == NULL) {
|
|
||||||
fprintf(stderr, "file_get_key returned NULL\n");
|
|
||||||
file_free(file_result[i]);
|
|
||||||
}
|
|
||||||
/* if this folder existed before, then folder_tree_add_folder will not
|
|
||||||
* add it to this folder's children. Thus we do this now */
|
|
||||||
found = false;
|
|
||||||
if (folder_tree_lookup_key(tree, key) != NULL) {
|
|
||||||
curr_entry->num_children++;
|
|
||||||
curr_entry->children =
|
|
||||||
(struct h_entry **)realloc(curr_entry->children,
|
|
||||||
curr_entry->num_children *
|
|
||||||
sizeof(struct h_entry *));
|
|
||||||
if (curr_entry->children == NULL) {
|
|
||||||
fprintf(stderr, "realloc failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
entry = folder_tree_add_file(tree, file_result[i], curr_entry);
|
|
||||||
/* we are using a new variable "found" here because after doing a
|
|
||||||
* folder_tree_add_folder, folder_tree_lookup_key will always succeed
|
|
||||||
*
|
|
||||||
* if this entry was not present before, then folder_tree_add_folder
|
|
||||||
* already added it to this folder's children
|
|
||||||
*/
|
|
||||||
if (found) {
|
|
||||||
curr_entry->children[curr_entry->num_children - 1] = entry;
|
|
||||||
}
|
|
||||||
file_free(file_result[i]);
|
|
||||||
}
|
|
||||||
free(file_result);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* update the fields of a file
|
* update the fields of a file
|
||||||
*/
|
*/
|
||||||
static int folder_tree_update_file_info(folder_tree * tree, mfconn * conn,
|
static int folder_tree_update_file_info(folder_tree * tree, mfconn * conn,
|
||||||
char *key)
|
const char *key)
|
||||||
{
|
{
|
||||||
mffile *file;
|
mffile *file;
|
||||||
int retval;
|
int retval;
|
||||||
@@ -1147,10 +1192,12 @@ static int folder_tree_update_file_info(folder_tree * tree, mfconn * conn,
|
|||||||
mfconn_update_secret_key(conn);
|
mfconn_update_secret_key(conn);
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
fprintf(stderr, "api call unsuccessful\n");
|
fprintf(stderr, "api call unsuccessful\n");
|
||||||
/* TODO: check reason. It might be that the remote object does not
|
/* maybe there is a different reason but for now just assume that an
|
||||||
* exist anymore in which case it has to be removed locally */
|
* unsuccessful call to file/get_info means that the remote file
|
||||||
|
* vanished. Thus we remove the object locally */
|
||||||
|
folder_tree_remove(tree, key);
|
||||||
file_free(file);
|
file_free(file);
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent = folder_tree_lookup_key(tree, file_get_parent(file));
|
parent = folder_tree_lookup_key(tree, file_get_parent(file));
|
||||||
@@ -1167,11 +1214,16 @@ static int folder_tree_update_file_info(folder_tree * tree, mfconn * conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* update the fields of a folder without checking its children
|
* update the fields of a folder through a call to folder/get_info
|
||||||
*
|
*
|
||||||
* we identify the folder to update by its key instead of its h_entry struct
|
* we identify the folder to update by its key instead of its h_entry struct
|
||||||
* pointer because this function is to fill the h_entry struct in the first
|
* pointer because this function is to fill the h_entry struct in the first
|
||||||
* place
|
* place
|
||||||
|
*
|
||||||
|
* if the folder key does not exist remote, remove it from the hashtable
|
||||||
|
*
|
||||||
|
* if the folder already existed locally but the remote version was found to
|
||||||
|
* be newer, also update its content
|
||||||
*/
|
*/
|
||||||
static int folder_tree_update_folder_info(folder_tree * tree, mfconn * conn,
|
static int folder_tree_update_folder_info(folder_tree * tree, mfconn * conn,
|
||||||
char *key)
|
char *key)
|
||||||
@@ -1179,8 +1231,9 @@ static int folder_tree_update_folder_info(folder_tree * tree, mfconn * conn,
|
|||||||
mffolder *folder;
|
mffolder *folder;
|
||||||
int retval;
|
int retval;
|
||||||
struct h_entry *parent;
|
struct h_entry *parent;
|
||||||
|
struct h_entry *new_entry;
|
||||||
|
|
||||||
if (key != NULL && strcmp(key, "trash")) {
|
if (key != NULL && strcmp(key, "trash") == 0) {
|
||||||
fprintf(stderr, "cannot get folder info of trash\n");
|
fprintf(stderr, "cannot get folder info of trash\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -1191,10 +1244,12 @@ static int folder_tree_update_folder_info(folder_tree * tree, mfconn * conn,
|
|||||||
mfconn_update_secret_key(conn);
|
mfconn_update_secret_key(conn);
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
fprintf(stderr, "api call unsuccessful\n");
|
fprintf(stderr, "api call unsuccessful\n");
|
||||||
/* TODO: check reason. It might be that the remote object does not
|
/* maybe there is a different reason but for now just assume that an
|
||||||
* exist anymore in which case it has to be removed locally */
|
* unsuccessful call to file/get_info means that the remote file
|
||||||
|
* vanished. Thus we remove the object locally */
|
||||||
|
folder_tree_remove(tree, key);
|
||||||
folder_free(folder);
|
folder_free(folder);
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent = folder_tree_lookup_key(tree, folder_get_parent(folder));
|
parent = folder_tree_lookup_key(tree, folder_get_parent(folder));
|
||||||
@@ -1203,7 +1258,14 @@ static int folder_tree_update_folder_info(folder_tree * tree, mfconn * conn,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
folder_tree_add_folder(tree, folder, parent);
|
/* store the updated entry in the hashtable */
|
||||||
|
new_entry = folder_tree_add_folder(tree, conn, folder, parent);
|
||||||
|
|
||||||
|
if (new_entry == NULL) {
|
||||||
|
fprintf(stderr, "folder_tree_add_folder failed\n");
|
||||||
|
folder_free(folder);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
folder_free(folder);
|
folder_free(folder);
|
||||||
|
|
||||||
@@ -1222,6 +1284,8 @@ void folder_tree_update(folder_tree * tree, mfconn * conn)
|
|||||||
struct mfconn_device_change *changes;
|
struct mfconn_device_change *changes;
|
||||||
int retval;
|
int retval;
|
||||||
struct h_entry *tmp_entry;
|
struct h_entry *tmp_entry;
|
||||||
|
const char *key;
|
||||||
|
uint64_t revision;
|
||||||
|
|
||||||
mfconn_api_device_get_status(conn, &revision_remote);
|
mfconn_api_device_get_status(conn, &revision_remote);
|
||||||
mfconn_update_secret_key(conn);
|
mfconn_update_secret_key(conn);
|
||||||
@@ -1242,16 +1306,6 @@ void folder_tree_update(folder_tree * tree, mfconn * conn)
|
|||||||
* changed.
|
* changed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* we have to manually check the root because it never shows up in the
|
|
||||||
* results from device_get_changes
|
|
||||||
*
|
|
||||||
* we don't need to be recursive here because we rely on
|
|
||||||
* device/get_changes reporting all changes to its children
|
|
||||||
*/
|
|
||||||
|
|
||||||
folder_tree_rebuild_helper(tree, conn, &(tree->root), false);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* changes have to be applied in the right order but the result of
|
* changes have to be applied in the right order but the result of
|
||||||
* mfconn_api_device_get_changes is already sorted by revision
|
* mfconn_api_device_get_changes is already sorted by revision
|
||||||
@@ -1266,6 +1320,8 @@ void folder_tree_update(folder_tree * tree, mfconn * conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; changes[i].change != MFCONN_DEVICE_CHANGE_END; i++) {
|
for (i = 0; changes[i].change != MFCONN_DEVICE_CHANGE_END; i++) {
|
||||||
|
key = changes[i].key;
|
||||||
|
revision = changes[i].revision;
|
||||||
switch (changes[i].change) {
|
switch (changes[i].change) {
|
||||||
case MFCONN_DEVICE_CHANGE_DELETED_FOLDER:
|
case MFCONN_DEVICE_CHANGE_DELETED_FOLDER:
|
||||||
case MFCONN_DEVICE_CHANGE_DELETED_FILE:
|
case MFCONN_DEVICE_CHANGE_DELETED_FILE:
|
||||||
@@ -1278,33 +1334,58 @@ void folder_tree_update(folder_tree * tree, mfconn * conn)
|
|||||||
continue;
|
continue;
|
||||||
if (strcmp(changes[i].parent, "trash") == 0)
|
if (strcmp(changes[i].parent, "trash") == 0)
|
||||||
continue;
|
continue;
|
||||||
/* if a folder has been updated then its name or location
|
/* only do anything if the revision of the change is greater
|
||||||
* might have changed... */
|
* than the revision of the locally stored entry */
|
||||||
folder_tree_update_folder_info(tree, conn, changes[i].key);
|
tmp_entry = folder_tree_lookup_key(tree, key);
|
||||||
/* ...or its contents changed
|
if (tmp_entry != NULL && tmp_entry->revision >= revision) {
|
||||||
* the last call made sure that an entry for this folder has
|
break;
|
||||||
* been added locally, so the following should succeed */
|
|
||||||
tmp_entry = folder_tree_lookup_key(tree, changes[i].key);
|
|
||||||
if (tmp_entry == NULL) {
|
|
||||||
fprintf(stderr, "folder_tree_lookup_key failed\n");
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
/* we don't need to be recursive here because we rely on
|
|
||||||
* device/get_changes reporting all changes to its children */
|
/* if a folder has been updated then its name or location
|
||||||
folder_tree_rebuild_helper(tree, conn, tmp_entry, false);
|
* might have changed...
|
||||||
|
*
|
||||||
|
* folder_tree_update_folder_info will check whether the
|
||||||
|
* new remote revision is higher than the local revision and
|
||||||
|
* will also fetch the content if this is the case
|
||||||
|
* */
|
||||||
|
folder_tree_update_folder_info(tree, conn, changes[i].key);
|
||||||
break;
|
break;
|
||||||
case MFCONN_DEVICE_CHANGE_UPDATED_FILE:
|
case MFCONN_DEVICE_CHANGE_UPDATED_FILE:
|
||||||
/* ignore files updated in trash */
|
/* ignore files updated in trash */
|
||||||
if (strcmp(changes[i].parent, "trash") == 0)
|
if (strcmp(changes[i].parent, "trash") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
/* only do anything if the revision of the change is greater
|
||||||
|
* than the revision of the locally stored entry */
|
||||||
|
tmp_entry = folder_tree_lookup_key(tree, key);
|
||||||
|
if (tmp_entry != NULL && tmp_entry->revision >= revision) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
/* if a file changed, update its info */
|
/* if a file changed, update its info */
|
||||||
folder_tree_update_file_info(tree, conn, changes[i].key);
|
folder_tree_update_file_info(tree, conn, key);
|
||||||
break;
|
break;
|
||||||
case MFCONN_DEVICE_CHANGE_END:
|
case MFCONN_DEVICE_CHANGE_END:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we have to manually check the root because it never shows up in the
|
||||||
|
* results from device_get_changes
|
||||||
|
*
|
||||||
|
* we don't need to be recursive here because we rely on
|
||||||
|
* device/get_changes reporting all changes to its children
|
||||||
|
*
|
||||||
|
* we update the root AFTER evaluating the results of device/get_changes
|
||||||
|
* so that we only have to pull in the remaining changes
|
||||||
|
*
|
||||||
|
* some recursion will be done if the helper detects that some of the
|
||||||
|
* children it updated have a newer revision than the existing ones. This
|
||||||
|
* is necessary because device/get_changes does not report changes to
|
||||||
|
* items which were even removed from the trash
|
||||||
|
*/
|
||||||
|
|
||||||
|
folder_tree_rebuild_helper(tree, conn, &(tree->root), false);
|
||||||
|
|
||||||
/* the new revision of the tree is the revision of the terminating change
|
/* the new revision of the tree is the revision of the terminating change
|
||||||
* */
|
* */
|
||||||
tree->revision = changes[i].revision;
|
tree->revision = changes[i].revision;
|
||||||
@@ -1397,6 +1478,8 @@ void folder_tree_housekeep(folder_tree * tree, mfconn * conn)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* find objects with children who claim to have a different parent
|
* find objects with children who claim to have a different parent
|
||||||
|
*
|
||||||
|
* this should actually never happen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* first check the root as a special case */
|
/* first check the root as a special case */
|
||||||
@@ -1414,6 +1497,14 @@ void folder_tree_housekeep(folder_tree * tree, mfconn * conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* some recursion will be done if the helper detects that some of the
|
||||||
|
* children it updated have a newer revision than the existing ones.
|
||||||
|
* This is necessary because device/get_changes does not report
|
||||||
|
* changes to items which were even removed from the trash
|
||||||
|
*/
|
||||||
|
|
||||||
folder_tree_rebuild_helper(tree, conn, &(tree->root), false);
|
folder_tree_rebuild_helper(tree, conn, &(tree->root), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1436,15 +1527,31 @@ void folder_tree_housekeep(folder_tree * tree, mfconn * conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
|
|
||||||
/* an entry was found that claims to have a different parent,
|
/* an entry was found that claims to have a different parent,
|
||||||
* so ask the remote to retrieve the real list of children */
|
* so ask the remote to retrieve the real list of children
|
||||||
|
*
|
||||||
|
* some recursion will be done if the helper detects that some
|
||||||
|
* of the children it updated have a newer revision than the
|
||||||
|
* existing ones. This is necessary because device/get_changes
|
||||||
|
* does not report changes to items which were even removed
|
||||||
|
* from the trash
|
||||||
|
*/
|
||||||
|
|
||||||
folder_tree_rebuild_helper(tree, conn, tree->buckets[i][j],
|
folder_tree_rebuild_helper(tree, conn, tree->buckets[i][j],
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find objects whose parents do not match their actual parents */
|
/* find objects whose parents do not match their actual parents
|
||||||
|
*
|
||||||
|
* this can happen when entries in the local hashtable do not exist
|
||||||
|
* anymore at the remote but have not been removed locally because they
|
||||||
|
* have not been part of any device/get_changes results. This can happen
|
||||||
|
* if the remote entries have been removed completely (including from the
|
||||||
|
* trash)
|
||||||
|
* */
|
||||||
for (i = 0; i < NUM_BUCKETS; i++) {
|
for (i = 0; i < NUM_BUCKETS; i++) {
|
||||||
for (j = 0; j < tree->bucket_lens[i]; j++) {
|
for (j = 0; j < tree->bucket_lens[i]; j++) {
|
||||||
if (!folder_tree_is_parent_of
|
if (!folder_tree_is_parent_of
|
||||||
|
|||||||
@@ -267,11 +267,11 @@ static int mediafirefs_rmdir(const char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mediafirefs_open(const char * path, struct fuse_file_info * file_info)
|
static int mediafirefs_open(const char *path, struct fuse_file_info *file_info)
|
||||||
{
|
{
|
||||||
(void)path;
|
(void)path;
|
||||||
|
|
||||||
if((file_info->flags & O_ACCMODE) != O_RDONLY) {
|
if ((file_info->flags & O_ACCMODE) != O_RDONLY) {
|
||||||
fprintf(stderr, "can only open read-only");
|
fprintf(stderr, "can only open read-only");
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
@@ -287,7 +287,8 @@ static int mediafirefs_open(const char * path, struct fuse_file_info * file_info
|
|||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mediafirefs_read(const char * path, char * buf, size_t size, off_t offset, struct fuse_file_info * file_info)
|
static int mediafirefs_read(const char *path, char *buf, size_t size,
|
||||||
|
off_t offset, struct fuse_file_info *file_info)
|
||||||
{
|
{
|
||||||
(void)path;
|
(void)path;
|
||||||
(void)buf;
|
(void)buf;
|
||||||
@@ -400,6 +401,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
//folder_tree_housekeep(tree);
|
//folder_tree_housekeep(tree);
|
||||||
|
|
||||||
|
fprintf(stderr, "tree before starting fuse:\n");
|
||||||
folder_tree_debug(tree);
|
folder_tree_debug(tree);
|
||||||
|
|
||||||
return fuse_main(args.argc, args.argv, &mediafirefs_oper, NULL);
|
return fuse_main(args.argc, args.argv, &mediafirefs_oper, NULL);
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ struct mfconn_device_change {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int mfconn_api_file_get_info(mfconn * conn, mffile * file,
|
int mfconn_api_file_get_info(mfconn * conn, mffile * file,
|
||||||
char *quickkey);
|
const char *quickkey);
|
||||||
|
|
||||||
int mfconn_api_file_get_links(mfconn * conn, mffile * file,
|
int mfconn_api_file_get_links(mfconn * conn, mffile * file,
|
||||||
char *quickkey);
|
const char *quickkey);
|
||||||
|
|
||||||
int mfconn_api_folder_create(mfconn * conn, const char *parent,
|
int mfconn_api_folder_create(mfconn * conn, const char *parent,
|
||||||
const char *name);
|
const char *name);
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
|
|
||||||
static int _decode_file_get_info(mfhttp * conn, void *data);
|
static int _decode_file_get_info(mfhttp * conn, void *data);
|
||||||
|
|
||||||
int mfconn_api_file_get_info(mfconn * conn, mffile * file, char *quickkey)
|
int mfconn_api_file_get_info(mfconn * conn, mffile * file,
|
||||||
|
const char *quickkey)
|
||||||
{
|
{
|
||||||
const char *api_call;
|
const char *api_call;
|
||||||
int retval;
|
int retval;
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
|
|
||||||
static int _decode_file_get_links(mfhttp * conn, void *data);
|
static int _decode_file_get_links(mfhttp * conn, void *data);
|
||||||
|
|
||||||
int mfconn_api_file_get_links(mfconn * conn, mffile * file, char *quickkey)
|
int mfconn_api_file_get_links(mfconn * conn, mffile * file,
|
||||||
|
const char *quickkey)
|
||||||
{
|
{
|
||||||
const char *api_call;
|
const char *api_call;
|
||||||
int retval;
|
int retval;
|
||||||
|
|||||||
Reference in New Issue
Block a user