static void index_files_done (void *result) { IndexAux *aux = result; CloneTask *task = aux->task; if (!aux->success) { transition_to_error (task, CLONE_ERROR_INDEX); goto out; } if (task->state == CLONE_STATE_CANCEL_PENDING) { transition_state (task, CLONE_STATE_CANCELED); goto out; } if (add_transfer_task (aux->mgr, task, NULL) < 0) { transition_to_error (task, CLONE_ERROR_FETCH); goto out; } transition_state (task, CLONE_STATE_FETCH); out: g_free (aux); return; }
static int start_index_or_transfer (SeafCloneManager *mgr, CloneTask *task, GError **error) { IndexAux *aux; int ret = 0; if (is_non_empty_directory (task->worktree)) { transition_state (task, CLONE_STATE_INDEX); aux = g_new0 (IndexAux, 1); aux->mgr = mgr; aux->task = task; ccnet_job_manager_schedule_job (seaf->job_mgr, index_files_job, index_files_done, aux); } else { ret = add_transfer_task (mgr, task, error); if (ret == 0) transition_state (task, CLONE_STATE_FETCH); else transition_to_error (task, CLONE_ERROR_FETCH); } return ret; }
static void merge_job_done (void *data) { MergeAux *aux = data; CloneTask *task = aux->task; SeafRepo *repo = aux->repo; if (!aux->success) { g_free (aux); transition_to_error (task, CLONE_ERROR_MERGE); return; } seaf_repo_manager_set_repo_worktree (aux->repo->manager, aux->repo, task->worktree); if (task->state == CLONE_STATE_CANCEL_PENDING) transition_state (task, CLONE_STATE_CANCELED); else if (task->state == CLONE_STATE_MERGE) { /* Save repo head if for GC. */ seaf_repo_manager_set_repo_property (seaf->repo_mgr, repo->id, REPO_REMOTE_HEAD, repo->head->commit_id); transition_state (task, CLONE_STATE_DONE); } else g_assert (0); g_free (aux); }
static void start_connect_task_relay (CloneTask *task, GError **error) { CcnetPeer *peer = ccnet_get_peer (seaf->ccnetrpc_client, task->peer_id); if (!peer) { /* clone from a new relay */ GString *buf = NULL; seaf_message ("add relay before clone, %s:%s\n", task->peer_addr, task->peer_port); buf = g_string_new(NULL); g_string_append_printf (buf, "add-relay --id %s --addr %s:%s", task->peer_id, task->peer_addr, task->peer_port); ccnet_send_command (seaf->session, buf->str, NULL, NULL); transition_state (task, CLONE_STATE_CONNECT); g_string_free (buf, TRUE); } else { /* The peer is added to ccnet already and will be connected, * only need to transition the state */ transition_state (task, CLONE_STATE_CONNECT); } if (peer) g_object_unref (peer); }
static void merge_job_done (void *data) { MergeAux *aux = data; CloneTask *task = aux->task; if (!aux->success) { g_free (aux); transition_to_error (task, CLONE_ERROR_MERGE); return; } seaf_repo_manager_set_repo_worktree (aux->repo->manager, aux->repo, task->worktree); if (task->state == CLONE_STATE_CANCEL_PENDING) transition_state (task, CLONE_STATE_CANCELED); else if (task->state == CLONE_STATE_MERGE) transition_state (task, CLONE_STATE_DONE); else g_assert (0); g_free (aux); }
static void start_checkout (SeafRepo *repo, CloneTask *task) { if (repo->encrypted && task->passwd != NULL) { /* keep this password check to be compatible with old servers. */ if (repo->enc_version >= 1 && seaf_repo_verify_passwd (repo->id, task->passwd, repo->magic) < 0) { seaf_warning ("[Clone mgr] incorrect password.\n"); transition_to_error (task, CLONE_ERROR_PASSWD); return; } if (seaf_repo_manager_set_repo_passwd (seaf->repo_mgr, repo, task->passwd) < 0) { seaf_warning ("[Clone mgr] failed to set passwd for %s.\n", repo->id); transition_to_error (task, CLONE_ERROR_INTERNAL); return; } } else if (repo->encrypted) { seaf_warning ("[Clone mgr] Password is empty for encrypted repo %s.\n", repo->id); transition_to_error (task, CLONE_ERROR_PASSWD); return; } if (g_access (task->worktree, F_OK) != 0 && g_mkdir_with_parents (task->worktree, 0777) < 0) { seaf_warning ("[clone mgr] Failed to create worktree %s.\n", task->worktree); transition_to_error (task, CLONE_ERROR_CHECKOUT); return; } if (!is_non_empty_directory (task->worktree)) { transition_state (task, CLONE_STATE_CHECKOUT); seaf_repo_manager_add_checkout_task (seaf->repo_mgr, repo, task->worktree, on_checkout_done, task->manager); } else { MergeAux *aux = g_new0 (MergeAux, 1); aux->task = task; aux->repo = repo; transition_state (task, CLONE_STATE_MERGE); ccnet_job_manager_schedule_job (seaf->job_mgr, merge_job, merge_job_done, aux); } }
static void start_clone_v2 (CloneTask *task) { GError *error = NULL; if (g_access (task->worktree, F_OK) != 0 && g_mkdir_with_parents (task->worktree, 0777) < 0) { seaf_warning ("[clone mgr] Failed to create worktree %s.\n", task->worktree); transition_to_error (task, CLONE_ERROR_FETCH); return; } SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr, task->repo_id); if (repo != NULL) { seaf_repo_manager_set_repo_token (seaf->repo_mgr, repo, task->token); seaf_repo_manager_set_repo_email (seaf->repo_mgr, repo, task->email); seaf_repo_manager_set_repo_relay_info (seaf->repo_mgr, repo->id, task->peer_addr, task->peer_port); if (task->server_url) { seaf_repo_manager_set_repo_property (seaf->repo_mgr, repo->id, REPO_PROP_SERVER_URL, task->server_url); } mark_clone_done_v2 (repo, task); return; } if (add_transfer_task (task, &error) == 0) transition_state (task, CLONE_STATE_FETCH); else transition_to_error (task, CLONE_ERROR_FETCH); }
static void check_http_protocol_done (HttpProtocolVersion *result, void *user_data) { CloneTask *task = user_data; if (task->state == CLONE_STATE_CANCEL_PENDING) { transition_state (task, CLONE_STATE_CANCELED); return; } if (result->check_success && !result->not_supported) { task->http_protocol_version = result->version; task->effective_url = g_strdup(task->server_url); http_check_head_commit (task); } else if (strncmp(task->server_url, "https", 5) != 0) { char *host_fileserver = http_fileserver_url(task->server_url); if (http_tx_manager_check_protocol_version (seaf->http_tx_mgr, host_fileserver, TRUE, check_http_fileserver_protocol_done, task) < 0) transition_to_error (task, CLONE_ERROR_CHECK_SERVER); g_free (host_fileserver); } else { /* Wait for periodic retry. */ transition_to_error (task, CLONE_ERROR_CHECK_SERVER); if (result->error_code != 0) task->err_detail = g_strdup(http_task_error_str(result->error_code)); } }
int seaf_clone_manager_cancel_task (SeafCloneManager *mgr, const char *repo_id) { CloneTask *task; if (!seaf->started) { seaf_message ("System not started, skip canceling clone task.\n"); return -1; } task = g_hash_table_lookup (mgr->tasks, repo_id); if (!task) return -1; switch (task->state) { case CLONE_STATE_INIT: case CLONE_STATE_CONNECT: case CLONE_STATE_ERROR: transition_state (task, CLONE_STATE_CANCELED); break; case CLONE_STATE_CHECK_SERVER: transition_state (task, CLONE_STATE_CANCEL_PENDING); case CLONE_STATE_FETCH: http_tx_manager_cancel_task (seaf->http_tx_mgr, task->repo_id, HTTP_TASK_TYPE_DOWNLOAD); transition_state (task, CLONE_STATE_CANCEL_PENDING); break; case CLONE_STATE_INDEX: case CLONE_STATE_CHECKOUT: case CLONE_STATE_MERGE: case CLONE_STATE_CHECK_PROTOCOL: /* We cannot cancel an in-progress checkout, just * wait until it finishes. */ transition_state (task, CLONE_STATE_CANCEL_PENDING); break; case CLONE_STATE_CANCEL_PENDING: break; default: seaf_warning ("[Clone mgr] cannot cancel a not-running task.\n"); return -1; } return 0; }
static gboolean restart_task (sqlite3_stmt *stmt, void *data) { SeafCloneManager *mgr = data; const char *repo_id, *repo_name, *token, *peer_id, *worktree, *passwd; const char *peer_addr, *peer_port, *email; CloneTask *task; SeafRepo *repo; repo_id = (const char *)sqlite3_column_text (stmt, 0); repo_name = (const char *)sqlite3_column_text (stmt, 1); token = (const char *)sqlite3_column_text (stmt, 2); peer_id = (const char *)sqlite3_column_text (stmt, 3); worktree = (const char *)sqlite3_column_text (stmt, 4); passwd = (const char *)sqlite3_column_text (stmt, 5); peer_addr = (const char *)sqlite3_column_text (stmt, 6); peer_port = (const char *)sqlite3_column_text (stmt, 7); email = (const char *)sqlite3_column_text (stmt, 8); task = clone_task_new (repo_id, peer_id, repo_name, token, worktree, passwd, peer_addr, peer_port, email); task->manager = mgr; /* Default to 1. */ task->enc_version = 1; if (passwd && load_clone_enc_info (task) < 0) { clone_task_free (task); return TRUE; } task->repo_version = 0; load_clone_repo_version_info (task); load_clone_more_info (task); repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (repo != NULL && repo->head != NULL) { transition_state (task, CLONE_STATE_DONE); return TRUE; } if (task->repo_version > 0) { if (task->server_url) { check_http_protocol (task); } else { transition_to_error (task, CLONE_ERROR_CHECK_SERVER); return TRUE; } } g_hash_table_insert (mgr->tasks, g_strdup(task->repo_id), task); return TRUE; }
static void on_checkout_done (CheckoutTask *ctask, SeafRepo *repo, void *data) { SeafCloneManager *mgr = data; CloneTask *task = g_hash_table_lookup (mgr->tasks, repo->id); g_assert (task != NULL); if (!ctask->success) { transition_to_error (task, CLONE_ERROR_CHECKOUT); return; } if (task->state == CLONE_STATE_CANCEL_PENDING) transition_state (task, CLONE_STATE_CANCELED); else if (task->state == CLONE_STATE_CHECKOUT) transition_state (task, CLONE_STATE_DONE); else g_assert (0); }
static gboolean restart_task (sqlite3_stmt *stmt, void *data) { SeafCloneManager *mgr = data; const char *repo_id, *repo_name, *token, *peer_id, *worktree, *passwd; const char *peer_addr, *peer_port, *email; CloneTask *task; SeafRepo *repo; repo_id = (const char *)sqlite3_column_text (stmt, 0); repo_name = (const char *)sqlite3_column_text (stmt, 1); token = (const char *)sqlite3_column_text (stmt, 2); peer_id = (const char *)sqlite3_column_text (stmt, 3); worktree = (const char *)sqlite3_column_text (stmt, 4); passwd = (const char *)sqlite3_column_text (stmt, 5); peer_addr = (const char *)sqlite3_column_text (stmt, 6); peer_port = (const char *)sqlite3_column_text (stmt, 7); email = (const char *)sqlite3_column_text (stmt, 8); task = clone_task_new (repo_id, peer_id, repo_name, token, worktree, passwd, peer_addr, peer_port, email); task->manager = mgr; repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (repo != NULL) { if (repo->head != NULL) { /* If repo exists and its head is set, we are done actually. * The task will be removed from db but still left in memory. */ transition_state (task, CLONE_STATE_DONE); g_hash_table_insert (mgr->tasks, g_strdup(task->repo_id), task); } else { /* If head is not set, we haven't finished checkout. */ g_hash_table_insert (mgr->tasks, g_strdup(task->repo_id), task); start_checkout (repo, task); } } else { /* Repo was not created last time. In this case, we just * restart from the very beginning. */ if (!ccnet_peer_is_ready (seaf->ccnetrpc_client, task->peer_id)) { /* the relay is not ready yet */ start_connect_task_relay (task, NULL); } else { start_index_or_transfer (mgr, task, NULL); } g_hash_table_insert (mgr->tasks, g_strdup(task->repo_id), task); } return TRUE; }
int seaf_clone_manager_cancel_task (SeafCloneManager *mgr, const char *repo_id) { CloneTask *task; task = g_hash_table_lookup (mgr->tasks, repo_id); if (!task) return -1; switch (task->state) { case CLONE_STATE_INIT: case CLONE_STATE_CONNECT: transition_state (task, CLONE_STATE_CANCELED); break; case CLONE_STATE_FETCH: seaf_transfer_manager_cancel_task (seaf->transfer_mgr, task->tx_id, TASK_TYPE_DOWNLOAD); transition_state (task, CLONE_STATE_CANCEL_PENDING); break; case CLONE_STATE_INDEX: case CLONE_STATE_CHECKOUT: case CLONE_STATE_MERGE: /* We cannot cancel an in-progress checkout, just * wait until it finishes. */ transition_state (task, CLONE_STATE_CANCEL_PENDING); break; case CLONE_STATE_CANCEL_PENDING: break; default: seaf_warning ("[Clone mgr] cannot cancel a not-running task.\n"); return -1; } return 0; }
static void check_http_protocol (CloneTask *task) { if (http_tx_manager_check_protocol_version (seaf->http_tx_mgr, task->server_url, FALSE, check_http_protocol_done, task) < 0) { transition_to_error (task, CLONE_ERROR_CHECK_SERVER); return; } transition_state (task, CLONE_STATE_CHECK_SERVER); }
static void on_checkout_done (CheckoutTask *ctask, SeafRepo *repo, void *data) { SeafCloneManager *mgr = data; CloneTask *task = g_hash_table_lookup (mgr->tasks, repo->id); g_assert (task != NULL); if (!ctask->success) { transition_to_error (task, CLONE_ERROR_CHECKOUT); return; } if (task->state == CLONE_STATE_CANCEL_PENDING) transition_state (task, CLONE_STATE_CANCELED); else if (task->state == CLONE_STATE_CHECKOUT) { /* Save repo head if for GC. */ seaf_repo_manager_set_repo_property (seaf->repo_mgr, repo->id, REPO_REMOTE_HEAD, repo->head->commit_id); transition_state (task, CLONE_STATE_DONE); } else g_assert (0); }
static void on_repo_http_fetched (SeafileSession *seaf, HttpTxTask *tx_task, SeafCloneManager *mgr) { CloneTask *task; /* Only handle clone task. */ if (!tx_task->is_clone) return; task = g_hash_table_lookup (mgr->tasks, tx_task->repo_id); g_return_if_fail (task != NULL); if (tx_task->state == HTTP_TASK_STATE_CANCELED) { /* g_assert (task->state == CLONE_STATE_CANCEL_PENDING); */ transition_state (task, CLONE_STATE_CANCELED); return; } else if (tx_task->state == HTTP_TASK_STATE_ERROR) { transition_to_error (task, CLONE_ERROR_FETCH); task->err_detail = g_strdup(http_task_error_str(tx_task->error)); return; } SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr, tx_task->repo_id); if (repo == NULL) { seaf_warning ("[Clone mgr] cannot find repo %s after fetched.\n", tx_task->repo_id); transition_to_error (task, CLONE_ERROR_INTERNAL); return; } seaf_repo_manager_set_repo_token (seaf->repo_mgr, repo, task->token); seaf_repo_manager_set_repo_email (seaf->repo_mgr, repo, task->email); seaf_repo_manager_set_repo_relay_info (seaf->repo_mgr, repo->id, task->peer_addr, task->peer_port); if (task->server_url) { seaf_repo_manager_set_repo_property (seaf->repo_mgr, repo->id, REPO_PROP_SERVER_URL, task->server_url); } check_folder_permissions (task); }
static void check_head_commit_done (HttpHeadCommit *result, void *user_data) { CloneTask *task = user_data; if (task->state == CLONE_STATE_CANCEL_PENDING) { transition_state (task, CLONE_STATE_CANCELED); return; } if (result->check_success && !result->is_corrupt && !result->is_deleted) { memcpy (task->server_head_id, result->head_commit, 40); start_clone_v2 (task); } else { transition_to_error (task, CLONE_ERROR_CHECK_SERVER); if (result->error_code != 0) task->err_detail = g_strdup(http_task_error_str(result->error_code)); } }
static void on_repo_fetched (SeafileSession *seaf, TransferTask *tx_task, SeafCloneManager *mgr) { CloneTask *task; /* Only handle clone task. */ if (!tx_task->is_clone) return; task = g_hash_table_lookup (mgr->tasks, tx_task->repo_id); g_assert (task != NULL); if (tx_task->state == TASK_STATE_CANCELED) { /* g_assert (task->state == CLONE_STATE_CANCEL_PENDING); */ transition_state (task, CLONE_STATE_CANCELED); return; } else if (tx_task->state == TASK_STATE_ERROR) { transition_to_error (task, CLONE_ERROR_FETCH); return; } SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr, tx_task->repo_id); if (repo == NULL) { seaf_warning ("[Clone mgr] cannot find repo %s after fetched.\n", tx_task->repo_id); transition_to_error (task, CLONE_ERROR_INTERNAL); return; } seaf_repo_manager_set_repo_token (seaf->repo_mgr, repo, task->token); seaf_repo_manager_set_repo_email (seaf->repo_mgr, repo, task->email); seaf_repo_manager_set_repo_relay_info (seaf->repo_mgr, repo->id, task->peer_addr, task->peer_port); start_checkout (repo, task); }
static void check_http_fileserver_protocol_done (HttpProtocolVersion *result, void *user_data) { CloneTask *task = user_data; if (task->state == CLONE_STATE_CANCEL_PENDING) { transition_state (task, CLONE_STATE_CANCELED); return; } if (result->check_success && !result->not_supported) { task->http_protocol_version = result->version; task->effective_url = http_fileserver_url (task->server_url); task->use_fileserver_port = TRUE; http_check_head_commit (task); } else { /* Wait for periodic retry. */ transition_to_error (task, CLONE_ERROR_CHECK_SERVER); if (result->error_code != 0) task->err_detail = g_strdup(http_task_error_str(result->error_code)); } }
static void mark_clone_done_v2 (SeafRepo *repo, CloneTask *task) { SeafBranch *local = NULL; seaf_repo_manager_set_repo_worktree (repo->manager, repo, task->worktree); local = seaf_branch_manager_get_branch (seaf->branch_mgr, repo->id, "local"); if (!local) { seaf_warning ("Cannot get branch local for repo %s(%.10s).\n", repo->name, repo->id); transition_to_error (task, CLONE_ERROR_INTERNAL); return; } /* Set repo head to mark checkout done. */ seaf_repo_set_head (repo, local); seaf_branch_unref (local); if (repo->encrypted) { if (seaf_repo_manager_set_repo_passwd (seaf->repo_mgr, repo, task->passwd) < 0) { seaf_warning ("[Clone mgr] failed to set passwd for %s.\n", repo->id); transition_to_error (task, CLONE_ERROR_INTERNAL); return; } } if (task->is_readonly) { seaf_repo_set_readonly (repo); } if (task->sync_wt_name) { seaf_repo_manager_set_repo_property (seaf->repo_mgr, repo->id, REPO_SYNC_WORKTREE_NAME, "true"); } if (task->server_url) repo->server_url = g_strdup(task->server_url); if (repo->auto_sync && (repo->sync_interval == 0)) { if (seaf_wt_monitor_watch_repo (seaf->wt_monitor, repo->id, repo->worktree) < 0) { seaf_warning ("failed to watch repo %s(%.10s).\n", repo->name, repo->id); transition_to_error (task, CLONE_ERROR_INTERNAL); return; } } /* For compatibility, still set these two properties. * So that if we downgrade to an old version, the syncing can still work. */ seaf_repo_manager_set_repo_property (seaf->repo_mgr, repo->id, REPO_REMOTE_HEAD, repo->head->commit_id); seaf_repo_manager_set_repo_property (seaf->repo_mgr, repo->id, REPO_LOCAL_HEAD, repo->head->commit_id); transition_state (task, CLONE_STATE_DONE); }