GList * seaf_branch_manager_get_branch_list (SeafBranchManager *mgr, const char *repo_id) { sqlite3 *db = mgr->priv->db; int result; sqlite3_stmt *stmt; char sql[256]; char *name; char *commit_id; GList *ret = NULL; SeafBranch *branch; snprintf (sql, 256, "SELECT name, commit_id FROM branch WHERE repo_id ='%s'", repo_id); pthread_mutex_lock (&mgr->priv->db_lock); if ( !(stmt = sqlite_query_prepare(db, sql)) ) { pthread_mutex_unlock (&mgr->priv->db_lock); return NULL; } while (1) { result = sqlite3_step (stmt); if (result == SQLITE_ROW) { name = (char *)sqlite3_column_text(stmt, 0); commit_id = (char *)sqlite3_column_text(stmt, 1); branch = seaf_branch_new (name, repo_id, commit_id); ret = g_list_prepend (ret, branch); } if (result == SQLITE_DONE) break; if (result == SQLITE_ERROR) { const gchar *str = sqlite3_errmsg (db); seaf_warning ("Couldn't prepare query, error: %d->'%s'\n", result, str ? str : "no error given"); sqlite3_finalize (stmt); seaf_branch_list_free (ret); pthread_mutex_unlock (&mgr->priv->db_lock); return NULL; } } sqlite3_finalize (stmt); pthread_mutex_unlock (&mgr->priv->db_lock); return g_list_reverse(ret); }
static gboolean get_branches (SeafDBRow *row, void *vplist) { GList **plist = vplist; const char *commit_id; const char *name; const char *repo_id; SeafBranch *branch; name = seaf_db_row_get_column_text (row, 0); repo_id = seaf_db_row_get_column_text (row, 1); commit_id = seaf_db_row_get_column_text (row, 2); branch = seaf_branch_new (name, repo_id, commit_id); *plist = g_list_prepend (*plist, branch); return TRUE; }
static SeafBranch * real_get_branch (SeafBranchManager *mgr, const char *repo_id, const char *name) { SeafBranch *branch = NULL; sqlite3_stmt *stmt; sqlite3 *db; char *sql; int result; pthread_mutex_lock (&mgr->priv->db_lock); db = mgr->priv->db; sql = sqlite3_mprintf ("SELECT commit_id FROM Branch " "WHERE name = %Q and repo_id='%s'", name, repo_id); if (!(stmt = sqlite_query_prepare (db, sql))) { seaf_warning ("[Branch mgr] Couldn't prepare query %s\n", sql); sqlite3_free (sql); pthread_mutex_unlock (&mgr->priv->db_lock); return NULL; } sqlite3_free (sql); result = sqlite3_step (stmt); if (result == SQLITE_ROW) { char *commit_id = (char *)sqlite3_column_text (stmt, 0); branch = seaf_branch_new (name, repo_id, commit_id); pthread_mutex_unlock (&mgr->priv->db_lock); sqlite3_finalize (stmt); return branch; } else if (result == SQLITE_ERROR) { const char *str = sqlite3_errmsg (db); seaf_warning ("Couldn't prepare query, error: %d->'%s'\n", result, str ? str : "no error given"); } sqlite3_finalize (stmt); pthread_mutex_unlock (&mgr->priv->db_lock); return NULL; }
static SeafBranch * real_get_branch (SeafBranchManager *mgr, const char *repo_id, const char *name) { char commit_id[41]; char *sql; commit_id[0] = 0; sql = "SELECT commit_id FROM Branch WHERE name=? AND repo_id=?"; if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_branch, commit_id, 2, "string", name, "string", repo_id) < 0) { seaf_warning ("[branch mgr] DB error when get branch %s.\n", name); return NULL; } if (commit_id[0] == 0) return NULL; return seaf_branch_new (name, repo_id, commit_id); }
static SeafBranch * real_get_branch (SeafBranchManager *mgr, const char *repo_id, const char *name) { char commit_id[41]; char sql[256]; commit_id[0] = 0; snprintf (sql, sizeof(sql), "SELECT commit_id FROM Branch WHERE name='%s' AND repo_id='%s'", name, repo_id); if (seaf_db_foreach_selected_row (mgr->seaf->db, sql, get_branch, commit_id) < 0) { g_warning ("[branch mgr] DB error when get branch %s.\n", name); return NULL; } if (commit_id[0] == 0) return NULL; return seaf_branch_new (name, repo_id, commit_id); }
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; }
static int do_create_virtual_repo (SeafRepoManager *mgr, SeafRepo *origin_repo, const char *repo_id, const char *repo_name, const char *repo_desc, const char *root_id, const char *user, const char *passwd, GError **error) { SeafRepo *repo = NULL; SeafCommit *commit = NULL; SeafBranch *master = NULL; int ret = 0; repo = seaf_repo_new (repo_id, repo_name, repo_desc); repo->no_local_history = TRUE; if (passwd != NULL && passwd[0] != '\0') { repo->encrypted = TRUE; repo->enc_version = origin_repo->enc_version; seafile_generate_magic (repo->enc_version, repo_id, passwd, repo->magic); if (repo->enc_version == 2) memcpy (repo->random_key, origin_repo->random_key, 96); } /* Virtual repos share fs and block store with origin repo and * have the same version as the origin. */ repo->version = origin_repo->version; memcpy (repo->store_id, origin_repo->id, 36); commit = seaf_commit_new (NULL, repo->id, root_id, /* root id */ user, /* creator */ EMPTY_SHA1, /* creator id */ repo_desc, /* description */ 0); /* ctime */ seaf_repo_to_commit (repo, commit); if (seaf_commit_manager_add_commit (seaf->commit_mgr, commit) < 0) { seaf_warning ("Failed to add commit.\n"); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Failed to add commit"); ret = -1; goto out; } master = seaf_branch_new ("master", repo->id, commit->commit_id); if (seaf_branch_manager_add_branch (seaf->branch_mgr, master) < 0) { seaf_warning ("Failed to add branch.\n"); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Failed to add branch"); ret = -1; goto out; } if (seaf_repo_set_head (repo, master) < 0) { seaf_warning ("Failed to set repo head.\n"); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Failed to set repo head."); ret = -1; goto out; } if (seaf_repo_manager_add_repo (mgr, repo) < 0) { seaf_warning ("Failed to add repo.\n"); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Failed to add repo."); ret = -1; goto out; } out: if (repo) seaf_repo_unref (repo); if (commit) seaf_commit_unref (commit); if (master) seaf_branch_unref (master); return ret; }
static SeafRepo* get_available_repo (char *repo_id, gboolean repair) { GList *commit_list = NULL; GList *temp_list = NULL; SeafCommit *temp_commit = NULL; SeafBranch *branch = NULL; SeafRepo *repo = NULL; SeafVirtRepo *vinfo = NULL; gboolean io_error; seaf_message ("Scanning available commits...\n"); seaf_obj_store_foreach_obj (seaf->commit_mgr->obj_store, repo_id, 1, fsck_get_repo_commit, &commit_list); if (commit_list == NULL) { seaf_warning ("No available commits for repo %.8s, can't be repaired.\n", repo_id); return NULL; } commit_list = g_list_sort (commit_list, compare_commit_by_ctime); repo = seaf_repo_new (repo_id, NULL, NULL); if (repo == NULL) { seaf_warning ("Out of memory, stop to run fsck for repo %.8s.\n", repo_id); goto out; } 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); seaf_virtual_repo_info_free (vinfo); } else { repo->is_virtual = FALSE; memcpy (repo->store_id, repo->id, 36); } for (temp_list = commit_list; temp_list; temp_list = temp_list->next) { temp_commit = temp_list->data; io_error = FALSE; if (!fsck_verify_seafobj (repo->store_id, 1, temp_commit->root_id, &io_error, VERIFY_DIR, repair)) { if (io_error) { seaf_repo_unref (repo); repo = NULL; goto out; } // fs object of this commit is corrupted, // continue to verify next continue; } branch = seaf_branch_new ("master", repo_id, temp_commit->commit_id); if (branch == NULL) { seaf_warning ("Out of memory, stop to run fsck for repo %.8s.\n", repo_id); seaf_repo_unref (repo); repo = NULL; goto out; } repo->head = branch; seaf_repo_from_commit (repo, temp_commit); char time_buf[64]; strftime (time_buf, 64, "%Y-%m-%d %H:%M:%S", localtime((time_t *)&temp_commit->ctime)); seaf_message ("Find available commit %.8s(created at %s) for repo %.8s.\n", temp_commit->commit_id, time_buf, repo_id); break; } out: 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 repo; }