static gboolean traverse_commit (SeafCommit *commit, void *vdata, gboolean *stop) { VerifyData *data = vdata; SeafRepo *repo = data->repo; int ret; if (data->truncate_time == 0) { *stop = TRUE; /* Stop after traversing the head commit. */ } else if (data->truncate_time > 0 && (gint64)(commit->ctime) < data->truncate_time && data->traversed_head) { *stop = TRUE; return TRUE; } if (!data->traversed_head) data->traversed_head = TRUE; ret = seaf_fs_manager_traverse_tree (seaf->fs_mgr, repo->store_id, repo->version, commit->root_id, fs_callback, vdata, FALSE); if (ret < 0) return FALSE; return TRUE; }
static gboolean validate_commit (SeafCommit *commit, void *data, gboolean *stop) { g_message ("Commit %s", commit->commit_id); *stop = FALSE; if (g_tree_lookup(already_traversed_commits, commit->commit_id)) { g_message (" [Skipped]\n"); return TRUE; } g_message ("\n"); if (seaf_fs_manager_traverse_tree (seaf->fs_mgr, commit->root_id, validate_block, NULL) < 0) { return FALSE; } char *id = g_strdup(commit->commit_id); g_tree_insert (already_traversed_commits, id, id); return TRUE; }
static gboolean traverse_commit (SeafCommit *commit, void *vdata, gboolean *stop) { MigrationData *data = vdata; SeafRepo *repo = data->repo; int ret; if (data->truncate_time > 0 && (gint64)(commit->ctime) < data->truncate_time && data->traversed_head && !data->stop_copy_blocks) { data->stop_copy_blocks = TRUE; } if (!data->traversed_head) data->traversed_head = TRUE; if (seaf_obj_store_copy_obj (seaf->commit_mgr->obj_store, repo->id, repo->version, repo->id, 1, commit->commit_id) < 0) { seaf_warning ("Failed to copy commit %s.\n", commit->commit_id); return FALSE; } ret = seaf_fs_manager_traverse_tree (seaf->fs_mgr, data->repo->store_id, data->repo->version, commit->root_id, fs_callback, data, FALSE); if (ret < 0) return FALSE; if (data->truncate_time == 0 && !data->stop_copy_blocks) { data->stop_copy_blocks = TRUE; /* Stop after traversing the head commit. */ } return TRUE; }
static gboolean check_fs_integrity (SeafCommit *commit, void *vdata, gboolean *stop) { FsckRes *res = vdata; /* Stop traversing commits after finding the first consistent commit. */ if (res->consistent_head != NULL) { *stop = TRUE; return TRUE; } int rc = seaf_fs_manager_traverse_tree (seaf->fs_mgr, res->repo->store_id, res->repo->version, commit->root_id, fs_callback, res, FALSE); if (rc == 0) { *stop = TRUE; res->consistent_head = g_strdup (commit->commit_id); } return TRUE; }
static int recover_corrupted_repo_head (char *repo_id) { GList *commit_list = NULL; GList *temp_list = NULL; SeafCommit *temp_commit = NULL; SeafBranch *branch = NULL; SeafRepo *repo = NULL; SeafVirtRepo *vinfo = NULL; FsckRes res; int rc = -1; seaf_message ("Recovering corrupt head commit for repo %.8s.\n", repo_id); seaf_obj_store_foreach_obj (seaf->commit_mgr->obj_store, repo_id, 1, fsck_get_repo_commit, &commit_list); if (commit_list == NULL) return rc; commit_list = g_list_sort (commit_list, compare_commit_by_ctime); memset (&res, 0, sizeof(res)); res.existing_blocks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (temp_list = commit_list; temp_list; temp_list = temp_list->next) { temp_commit = temp_list->data; branch = seaf_branch_new ("master", repo_id, temp_commit->commit_id); if (branch == NULL) { continue; } repo = seaf_repo_new (repo_id, NULL, NULL); if (repo == NULL) { seaf_branch_unref (branch); continue; } repo->head = branch; seaf_repo_from_commit (repo, temp_commit); vinfo = seaf_repo_manager_get_virtual_repo_info (seaf->repo_mgr, repo_id); if (vinfo) { repo->is_virtual = TRUE; memcpy (repo->store_id, vinfo->origin_repo_id, 36); } else { repo->is_virtual = FALSE; memcpy (repo->store_id, repo->id, 36); } seaf_virtual_repo_info_free (vinfo); res.repo = repo; rc = seaf_fs_manager_traverse_tree (seaf->fs_mgr, repo->store_id, repo->version, temp_commit->root_id, fs_callback, &res, FALSE); if (rc < 0) { seaf_repo_unref (repo); } else { break; } } if (rc < 0) { seaf_warning ("Failed to fix head commit of repo %.8s.\n", repo_id); } else { // create new head commit, and set it's parent commit as latest avaliable commit temp_commit = cre_commit_from_parent (repo_id, temp_commit); if (temp_commit) { seaf_branch_set_commit (repo->head, temp_commit->commit_id); // in case of branch col miss, using add_branch instead of update_branch if (seaf_branch_manager_add_branch (seaf->branch_mgr, repo->head) < 0) { seaf_warning ("Failed to fix head commit of repo %.8s.\n", repo_id); rc = -1; } else { seaf_commit_manager_add_commit (seaf->commit_mgr, temp_commit); seaf_message ("Head commit of repo %.8s has been fixed to commit %.8s.\n", repo_id, temp_commit->commit_id); } seaf_commit_unref (temp_commit); } else { seaf_warning ("Failed to fix head commit of repo %.8s.\n", repo_id); rc = -1; } } g_hash_table_destroy (res.existing_blocks); seaf_repo_unref (repo); for (temp_list = commit_list; temp_list; temp_list = temp_list->next) { temp_commit = temp_list->data; seaf_commit_unref (temp_commit); } g_list_free (commit_list); return rc; }