Add confirmation when deleting more than 500 files (#2612)

* Add confirmation when deleting more than 500 files

* Delete repo and reclone it

* Don't need to cancel task

Co-authored-by: heran yang <heran.yang@seafile.com>
This commit is contained in:
feiniks 2022-10-21 14:10:31 +08:00 committed by GitHub
parent e356ee476d
commit 187800c185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 292 additions and 2 deletions

View File

@ -988,3 +988,9 @@ seafile_sync_error_id_to_str (int error_id, GError **error)
{
return g_strdup(sync_error_id_to_str (error_id));
}
int
seafile_add_del_confirmation (const char *confirmation_id, int resync, GError **error)
{
return seaf_sync_manager_add_del_confirmation (seaf->sync_mgr, confirmation_id, resync);
}

View File

@ -92,6 +92,11 @@ register_rpc_service ()
"seafile_sync_error_id_to_str",
searpc_signature_string__int());
searpc_server_register_function ("seafile-rpcserver",
seafile_add_del_confirmation,
"seafile_add_del_confirmation",
searpc_signature_int__string_int());
searpc_server_register_function ("seafile-rpcserver",
seafile_get_config,
"seafile_get_config",

View File

@ -168,6 +168,9 @@ seafile_session_config_set_int (SeafileSession *session,
if (g_strcmp0(key, KEY_PROXY_PORT) == 0) {
session->http_proxy_port = value;
}
if (g_strcmp0(key, KEY_DELETE_CONFIRM_THRESHOLD) == 0) {
session->delete_confirm_threshold = value;
}
return 0;
}

View File

@ -37,6 +37,7 @@
#define KEY_PROXY_PASSWORD "proxy_password"
#define PROXY_TYPE_HTTP "http"
#define PROXY_TYPE_SOCKS "socks"
#define KEY_DELETE_CONFIRM_THRESHOLD "delete_confirm_threshold"
gboolean
seafile_session_config_exists (SeafileSession *session, const char *key);

View File

@ -174,6 +174,11 @@ static SyncErrorInfo sync_error_info_tbl[] = {
SYNC_ERROR_LEVEL_REPO,
"Library is too large to sync"
},
{
SYNC_ERROR_ID_DEL_CONFIRMATION_PENDING,
SYNC_ERROR_LEVEL_REPO,
"Waiting for confirmation to delete files"
},
};
const char *

View File

@ -348,6 +348,8 @@ out:
g_key_file_free (key_file);
}
#define MAX_DELETED_FILES_NUM 500
void
seafile_session_prepare (SeafileSession *session)
{
@ -415,6 +417,10 @@ seafile_session_prepare (SeafileSession *session)
seafile_session_config_get_string(session, KEY_PROXY_PASSWORD);
}
session->delete_confirm_threshold = seafile_session_config_get_int (session, KEY_DELETE_CONFIRM_THRESHOLD, NULL);
if (session->delete_confirm_threshold <= 0)
session->delete_confirm_threshold = MAX_DELETED_FILES_NUM;
int block_size = seafile_session_config_get_int(session, KEY_CDC_AVERAGE_BLOCK_SIZE, NULL);
if (block_size >= 1024) {
session->cdc_average_block_size = block_size;

View File

@ -88,6 +88,7 @@ struct _SeafileSession {
int http_proxy_port;
char *http_proxy_username;
char *http_proxy_password;
int delete_confirm_threshold;
};
struct _SeafileSessionClass

View File

@ -15,6 +15,7 @@
#include "vc-utils.h"
#include "sync-status-tree.h"
#include "diff-simple.h"
#ifdef WIN32
#include <shlobj.h>
@ -62,6 +63,10 @@ struct _HttpServerState {
};
typedef struct _HttpServerState HttpServerState;
typedef struct DelConfirmationResult {
gboolean resync;
} DelConfirmationResult;
struct _SeafSyncManagerPriv {
struct SeafTimer *check_sync_timer;
struct SeafTimer *update_tx_state_timer;
@ -77,6 +82,9 @@ struct _SeafSyncManagerPriv {
GAsyncQueue *refresh_paths;
struct SeafTimer *refresh_windows_timer;
#endif
pthread_mutex_t del_confirmation_lock;
GHashTable *del_confirmation_tasks;
};
struct _ActivePathsInfo {
@ -165,6 +173,11 @@ seaf_sync_manager_new (SeafileSession *seaf)
mgr->priv->refresh_paths = g_async_queue_new ();
#endif
mgr->priv->del_confirmation_tasks = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
g_free);
pthread_mutex_init (&mgr->priv->del_confirmation_lock, NULL);
return mgr;
}
@ -970,6 +983,11 @@ commit_job (void *vtask)
return res;
}
static char *
exceed_max_deleted_files (SeafRepo *repo);
static void
notify_delete_confirmation (const char *repo_name, const char *desc, const char *confirmation_id);
static void
commit_job_done (void *vres)
{
@ -998,8 +1016,20 @@ commit_job_done (void *vres)
return;
}
if (res->changed)
if (res->changed) {
char *desc = NULL;
desc = exceed_max_deleted_files (repo);
if (desc) {
notify_delete_confirmation (repo->name, desc, repo->head->commit_id);
seaf_warning ("Delete more than %d files, add delete confirmation.\n", seaf->delete_confirm_threshold);
task->info->del_confirmation_pending = TRUE;
set_task_error (res->task, SYNC_ERROR_ID_DEL_CONFIRMATION_PENDING);
g_free (desc);
g_free (res);
return;
}
start_upload_if_necessary (res->task);
}
else if (task->is_manual_sync || task->is_initial_commit)
check_head_commit_http (task);
else
@ -1186,6 +1216,194 @@ out:
return ret;
}
#define MAX_DELETED_FILES_NUM 100
inline static char *
get_basename (char *path)
{
char *slash;
slash = strrchr (path, '/');
if (!slash)
return path;
return (slash + 1);
}
static char *
exceed_max_deleted_files (SeafRepo *repo)
{
SeafBranch *master = NULL, *local = NULL;
SeafCommit *local_head = NULL, *master_head = NULL;
GList *diff_results = NULL;
char *deleted_file = NULL;
GString *desc = NULL;
char *ret = NULL;
local = seaf_branch_manager_get_branch (seaf->branch_mgr, repo->id, "local");
if (!local) {
seaf_warning ("No local branch found for repo %s(%.8s).\n",
repo->name, repo->id);
goto out;
}
master = seaf_branch_manager_get_branch (seaf->branch_mgr, repo->id, "master");
if (!master) {
seaf_warning ("No master branch found for repo %s(%.8s).\n",
repo->name, repo->id);
goto out;
}
local_head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version,
local->commit_id);
if (!local_head) {
seaf_warning ("Failed to get head of local branch for repo %s.\n", repo->id);
goto out;
}
master_head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version,
master->commit_id);
if (!master_head) {
seaf_warning ("Failed to get head of master branch for repo %s.\n", repo->id);
goto out;
}
diff_commit_roots (repo->id, repo->version, master_head->root_id, local_head->root_id, &diff_results, TRUE);
if (!diff_results) {
goto out;
}
GList *p;
DiffEntry *de;
int n_deleted = 0;
for (p = diff_results; p != NULL; p = p->next) {
de = p->data;
switch (de->status) {
case DIFF_STATUS_DELETED:
if (n_deleted == 0)
deleted_file = get_basename(de->name);
n_deleted++;
break;
}
}
if (n_deleted >= seaf->delete_confirm_threshold) {
desc = g_string_new ("");
g_string_append_printf (desc, "Deleted \"%s\" and %d more files.\n",
deleted_file, n_deleted - 1);
ret = g_string_free (desc, FALSE);
}
out:
seaf_branch_unref (local);
seaf_branch_unref (master);
seaf_commit_unref (local_head);
seaf_commit_unref (master_head);
g_list_free_full (diff_results, (GDestroyNotify)diff_entry_free);
return ret;
}
static void
notify_delete_confirmation (const char *repo_name, const char *desc, const char *confirmation_id)
{
json_t *obj = json_object ();
json_object_set_string_member (obj, "repo_name", repo_name);
json_object_set_string_member (obj, "delete_files", desc);
json_object_set_string_member (obj, "confirmation_id", confirmation_id);
char *msg = json_dumps (obj, JSON_COMPACT);
seaf_mq_manager_publish_notification (seaf->mq_mgr, "sync.del_confirmation", msg);
json_decref (obj);
g_free (msg);
}
int
seaf_sync_manager_add_del_confirmation (SeafSyncManager *mgr,
const char *confirmation_id,
gboolean resync)
{
SeafSyncManagerPriv *priv = seaf->sync_mgr->priv;
DelConfirmationResult *result = NULL;
result = g_new0 (DelConfirmationResult, 1);
result->resync = resync;
pthread_mutex_lock (&priv->del_confirmation_lock);
g_hash_table_insert (priv->del_confirmation_tasks, g_strdup (confirmation_id), result);
pthread_mutex_unlock (&priv->del_confirmation_lock);
return 0;
}
static DelConfirmationResult *
get_del_confirmation_result (const char *confirmation_id)
{
SeafSyncManagerPriv *priv = seaf->sync_mgr->priv;
DelConfirmationResult *result, *copy = NULL;
pthread_mutex_lock (&priv->del_confirmation_lock);
result = g_hash_table_lookup (priv->del_confirmation_tasks, confirmation_id);
if (result) {
copy = g_new0 (DelConfirmationResult, 1);
copy->resync = result->resync;
g_hash_table_remove (priv->del_confirmation_tasks, confirmation_id);
}
pthread_mutex_unlock (&priv->del_confirmation_lock);
return copy;
}
static void
resync_repo (SeafRepo *repo)
{
GError *error = NULL;
char *repo_id = g_strdup (repo->id);
int repo_version = repo->version;
char *repo_name = g_strdup (repo->name);
char *token = g_strdup (repo->token);
char *magic = g_strdup (repo->magic);
int enc_version = repo->enc_version;
char *random_key = g_strdup (repo->random_key);
char *worktree = g_strdup (repo->worktree);
char *email = g_strdup (repo->email);
char *more_info = NULL;
json_t *obj = json_object ();
json_object_set_int_member (obj, "is_readonly", repo->is_readonly);
json_object_set_string_member (obj, "repo_salt", repo->salt);
json_object_set_string_member (obj, "server_url", repo->server_url);
more_info = json_dumps (obj, 0);
if (repo->auto_sync && (repo->sync_interval == 0))
seaf_wt_monitor_unwatch_repo (seaf->wt_monitor, repo->id);
seaf_repo_manager_del_repo (seaf->repo_mgr, repo);
char *ret = seaf_clone_manager_add_task (seaf->clone_mgr, repo_id,
repo_version, repo_name,
token, NULL,
magic, enc_version,
random_key, worktree,
email, more_info, &error);
if (error) {
seaf_warning ("Failed to clone repo %s: %s\n", repo_id, error->message);
g_clear_error (&error);
}
g_free (ret);
g_free (repo_id);
g_free (repo_name);
g_free (token);
g_free (magic);
g_free (random_key);
g_free (worktree);
g_free (email);
json_decref (obj);
g_free (more_info);
}
static int
sync_repo_v2 (SeafSyncManager *manager, SeafRepo *repo, gboolean is_manual_sync)
{
@ -1224,6 +1442,36 @@ sync_repo_v2 (SeafSyncManager *manager, SeafRepo *repo, gboolean is_manual_sync)
if (strcmp (master->commit_id, local->commit_id) != 0) {
if (is_manual_sync || can_schedule_repo (manager, repo)) {
task = create_sync_task_v2 (manager, repo, is_manual_sync, FALSE);
if (!task->info->del_confirmation_pending) {
char *desc = NULL;
desc = exceed_max_deleted_files (repo);
if (desc) {
notify_delete_confirmation (repo->name, desc, local->commit_id);
seaf_warning ("Delete more than %d files, add delete confirmation.\n", seaf->delete_confirm_threshold);
task->info->del_confirmation_pending = TRUE;
set_task_error (task, SYNC_ERROR_ID_DEL_CONFIRMATION_PENDING);
g_free (desc);
goto out;
}
} else {
DelConfirmationResult *result = get_del_confirmation_result (local->commit_id);
if (!result) {
// User has not confirmed whether to continue syncing.
set_task_error (task, SYNC_ERROR_ID_DEL_CONFIRMATION_PENDING);
goto out;
} else if (result->resync) {
// User chooses to resync.
g_free (result);
task->info->del_confirmation_pending = FALSE;
set_task_error (task, SYNC_ERROR_ID_DEL_CONFIRMATION_PENDING);
// Delete this repo and resync this repo by adding clone task.
resync_repo (repo);
goto out;
}
// User chooes to continue syncing.
g_free (result);
task->info->del_confirmation_pending = FALSE;
}
start_upload_if_necessary (task);
}
/* Do nothing if the client still has something to upload

View File

@ -35,6 +35,7 @@ struct _SyncInfo {
gboolean end_multipart_upload;
gint sync_perm_err_cnt;
gboolean del_confirmation_pending;
};
enum {
@ -129,6 +130,11 @@ SyncInfo *
seaf_sync_manager_get_sync_info (SeafSyncManager *mgr,
const char *repo_id);
int
seaf_sync_manager_add_del_confirmation (SeafSyncManager *mgr,
const char *confirmation_id,
gboolean resync);
int
seaf_sync_manager_disable_auto_sync (SeafSyncManager *mgr);

View File

@ -62,6 +62,7 @@
#define SYNC_ERROR_ID_REMOVE_UNCOMMITTED_FOLDER 30
#define SYNC_ERROR_ID_INVALID_PATH_ON_WINDOWS 31
#define SYNC_ERROR_ID_LIBRARY_TOO_LARGE 32
#define N_SYNC_ERROR_ID 33
#define SYNC_ERROR_ID_DEL_CONFIRMATION_PENDING 33
#define N_SYNC_ERROR_ID 34
#endif

View File

@ -216,4 +216,7 @@ seafile_shutdown (GError **error);
char*
seafile_sync_error_id_to_str (int error_id, GError **error);
int
seafile_add_del_confirmation (const char *confirmation_id, int resync, GError **error);
#endif

View File

@ -211,3 +211,8 @@ class SeafileRpcClient(NamedPipeClient):
def seafile_shutdown():
pass
shutdown = seafile_shutdown
@searpc_func("int", ["string", "int"])
def seafile_add_del_confirmation(key, value):
pass
add_del_confirmation = seafile_add_del_confirmation