cloud sync: don't always trust the server hash

This commit is contained in:
Eric Warmenhoven 2025-05-17 21:58:57 -04:00
parent 97bc2da36c
commit b632a1373e
1 changed files with 52 additions and 15 deletions

View File

@ -541,20 +541,35 @@ static void task_cloud_sync_backup_file(struct item_file *file)
filestream_rename(file->path, new_path);
}
typedef struct
{
task_cloud_sync_state_t *sync_state;
struct item_file *server_file;
} task_cloud_sync_fetch_state_t;
static void task_cloud_sync_fetch_cb(void *user_data, const char *path, bool success, RFILE *file)
{
task_cloud_sync_state_t *sync_state = (task_cloud_sync_state_t *)user_data;
char *hash = NULL;
task_cloud_sync_fetch_state_t *fetch_state = (task_cloud_sync_fetch_state_t *)user_data;
task_cloud_sync_state_t *sync_state;
struct item_file *server_file;
char *hash = NULL;
if (!sync_state)
if (!fetch_state)
return;
sync_state = fetch_state->sync_state;
server_file = fetch_state->server_file;
if (success && file)
{
hash = task_cloud_sync_md5_rfile(file);
filestream_close(file);
RARCH_LOG(CSPFX "successfully fetched %s\n", path);
task_cloud_sync_add_to_updated_manifest(sync_state, path, hash, false);
task_cloud_sync_add_to_updated_manifest(sync_state, path, hash, true);
/* if the file fetched from the server does not match its own hash, the manifest needs to be updated */
if (!string_is_equal(hash, CS_FILE_HASH(server_file)))
sync_state->need_manifest_uploaded = true;
sync_state->downloads++;
}
else
@ -564,34 +579,43 @@ static void task_cloud_sync_fetch_cb(void *user_data, const char *path, bool suc
RARCH_WARN(CSPFX "failed to fetch %s\n", path);
else
RARCH_WARN(CSPFX "failed to write file from server: %s\n", path);
task_cloud_sync_add_to_updated_manifest(sync_state, path, CS_FILE_HASH(server_file), true);
sync_state->failures = true;
}
slock_lock(tcs_running_lock);
sync_state->waiting--;
slock_unlock(tcs_running_lock);
free(fetch_state);
}
static void task_cloud_sync_fetch_server_file(task_cloud_sync_state_t *sync_state)
{
size_t i;
char filename[PATH_MAX_LENGTH];
char directory[DIR_MAX_LENGTH];
struct string_list *dirlist = task_cloud_sync_directory_map();
struct item_file *server_file = &sync_state->server_manifest->list[sync_state->server_idx];
const char *key = CS_FILE_KEY(server_file);
size_t i;
char filename[PATH_MAX_LENGTH];
char directory[DIR_MAX_LENGTH];
struct string_list *dirlist = task_cloud_sync_directory_map();
struct item_file *server_file = &sync_state->server_manifest->list[sync_state->server_idx];
const char *key = CS_FILE_KEY(server_file);
/* the key from the server file is in "portable" format, use '/' */
const char *path = strchr(key, '/') + 1;
settings_t *settings = config_get_ptr();
const char *path = strchr(key, '/') + 1;
settings_t *settings = config_get_ptr();
task_cloud_sync_fetch_state_t *fetch_state;
/* we're just fetching a file the server has, we can update this now */
task_cloud_sync_add_to_updated_manifest(sync_state, key, CS_FILE_HASH(server_file), true);
/* no need to mark need_manifest_uploaded, nothing changed */
/* there is a weird thing that can happen, where the server file changes but
* the manifest does not have the updated hash. in that case when the file is
* downloaded we have an opportunity to check that the hash matches and
* update the server manifest, and correct the issue. to do this the server
* manifest cannot be updated early; only on error or after the download is
* successful. on error just add to the updated manifest without marking
* need_manifest_uplaod. */
if (task_cloud_sync_should_ignore_file(key))
{
/* don't fetch a file we're supposed to ignore, even if the server has it */
RARCH_LOG(CSPFX "ignoring %s\n", key);
task_cloud_sync_add_to_updated_manifest(sync_state, key, CS_FILE_HASH(server_file), true);
return;
}
RARCH_LOG(CSPFX "fetching %s\n", key);
@ -609,6 +633,7 @@ static void task_cloud_sync_fetch_server_file(task_cloud_sync_state_t *sync_stat
{
/* how did this end up here? we don't know where to put it... */
RARCH_WARN(CSPFX "don't know where to put %s!\n", key);
task_cloud_sync_add_to_updated_manifest(sync_state, key, CS_FILE_HASH(server_file), true);
return;
}
@ -621,12 +646,24 @@ static void task_cloud_sync_fetch_server_file(task_cloud_sync_state_t *sync_stat
fill_pathname_basedir(directory, filename, sizeof(directory));
path_mkdir(directory);
if (cloud_sync_read(key, filename, task_cloud_sync_fetch_cb, sync_state))
fetch_state = malloc(sizeof(task_cloud_sync_fetch_state_t));
if (!fetch_state)
{
RARCH_WARN(CSPFX "wanted to fetch %s but failed to malloc\n", key);
task_cloud_sync_add_to_updated_manifest(sync_state, key, CS_FILE_HASH(server_file), true);
sync_state->failures = true;
return;
}
fetch_state->sync_state = sync_state;
fetch_state->server_file = server_file;
if (cloud_sync_read(key, filename, task_cloud_sync_fetch_cb, fetch_state))
sync_state->waiting++;
else
{
RARCH_WARN(CSPFX "wanted to fetch %s but failed\n", key);
task_cloud_sync_add_to_updated_manifest(sync_state, key, CS_FILE_HASH(server_file), true);
sync_state->failures = true;
free(fetch_state);
}
}