mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-08-12 20:17:05 +00:00
* Add online gc core * Add check gc when update repo * Go add check gc when update repo * Add gc unit test * Support set pwd_hash for gc and fsck * Optimize to check if block exists --------- Co-authored-by: 杨赫然 <heran.yang@seafile.com>
245 lines
7.1 KiB
C
245 lines
7.1 KiB
C
#include "seafile-session.h"
|
|
#include "utils.h"
|
|
#include "log.h"
|
|
|
|
typedef struct VerifyData {
|
|
SeafRepo *repo;
|
|
gint64 truncate_time;
|
|
gboolean traversed_head;
|
|
GHashTable *exist_blocks;
|
|
} VerifyData;
|
|
|
|
static int
|
|
check_blocks (VerifyData *data, const char *file_id)
|
|
{
|
|
SeafRepo *repo = data->repo;
|
|
Seafile *seafile;
|
|
int i;
|
|
|
|
seafile = seaf_fs_manager_get_seafile (seaf->fs_mgr,
|
|
repo->store_id,
|
|
repo->version,
|
|
file_id);
|
|
if (!seafile) {
|
|
seaf_warning ("Failed to find file %s.\n", file_id);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < seafile->n_blocks; ++i) {
|
|
if (!g_hash_table_lookup(data->exist_blocks, seafile->blk_sha1s[i])) {
|
|
seaf_message ("Block %s is missing.\n", seafile->blk_sha1s[i]);
|
|
}
|
|
}
|
|
|
|
seafile_unref (seafile);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
fs_callback (SeafFSManager *mgr,
|
|
const char *store_id,
|
|
int version,
|
|
const char *obj_id,
|
|
int type,
|
|
void *user_data,
|
|
gboolean *stop)
|
|
{
|
|
VerifyData *data = user_data;
|
|
|
|
if (type == SEAF_METADATA_TYPE_FILE && check_blocks (data, obj_id) < 0)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* Still traverse the first commit older than truncate_time.
|
|
* If a file in the child commit of this commit is deleted,
|
|
* we need to access this commit in order to restore it
|
|
* from trash.
|
|
*/
|
|
*stop = 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 int
|
|
verify_virtual_repos (VerifyData *data)
|
|
{
|
|
SeafRepo *repo = data->repo;
|
|
if (repo->is_virtual) {
|
|
return 0;
|
|
}
|
|
|
|
GList *vrepo_ids = NULL, *ptr;
|
|
char *repo_id;
|
|
SeafVirtRepo *vinfo;
|
|
int ret = 0;
|
|
|
|
vrepo_ids = seaf_repo_manager_get_virtual_repo_ids_by_origin (seaf->repo_mgr,
|
|
repo->id);
|
|
|
|
for (ptr = vrepo_ids; ptr; ptr = ptr->next) {
|
|
repo_id = ptr->data;
|
|
vinfo = seaf_repo_manager_get_virtual_repo_info (seaf->repo_mgr, repo_id);
|
|
if (!vinfo) {
|
|
continue;
|
|
}
|
|
|
|
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
|
|
repo->store_id, repo->version,
|
|
vinfo->base_commit,
|
|
traverse_commit,
|
|
data,
|
|
FALSE);
|
|
seaf_virtual_repo_info_free (vinfo);
|
|
if (!res) {
|
|
seaf_warning ("Failed to traverse base commit %s for virtual repo %s.\n", vinfo->base_commit, repo_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
string_list_free (vrepo_ids);
|
|
return ret;
|
|
|
|
}
|
|
|
|
static gboolean
|
|
collect_exist_blocks (const char *store_id, int version,
|
|
const char *block_id, void *vdata)
|
|
{
|
|
GHashTable *exist_blocks = vdata;
|
|
char *copy = g_strdup (block_id);
|
|
|
|
g_hash_table_replace (exist_blocks, copy, copy);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
verify_repo (SeafRepo *repo)
|
|
{
|
|
GList *branches, *ptr;
|
|
SeafBranch *branch;
|
|
int ret = 0;
|
|
VerifyData data = {0};
|
|
|
|
data.repo = repo;
|
|
data.truncate_time = seaf_repo_manager_get_repo_truncate_time (repo->manager,
|
|
repo->id);
|
|
data.exist_blocks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
ret = seaf_block_manager_foreach_block (seaf->block_mgr,
|
|
repo->store_id, repo->version,
|
|
collect_exist_blocks,
|
|
data.exist_blocks);
|
|
if (ret < 0) {
|
|
seaf_warning ("Failed to collect existing blocks for repo %.8s, stop GC.\n\n",
|
|
repo->id);
|
|
g_hash_table_destroy (data.exist_blocks);
|
|
return ret;
|
|
}
|
|
|
|
branches = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo->id);
|
|
if (branches == NULL) {
|
|
seaf_warning ("[GC] Failed to get branch list of repo %s.\n", repo->id);
|
|
g_hash_table_destroy (data.exist_blocks);
|
|
return -1;
|
|
}
|
|
|
|
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
|
|
branch = ptr->data;
|
|
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
|
|
repo->id,
|
|
repo->version,
|
|
branch->commit_id,
|
|
traverse_commit,
|
|
&data, FALSE);
|
|
seaf_branch_unref (branch);
|
|
if (!res) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_list_free (branches);
|
|
|
|
if (ret < 0) {
|
|
g_hash_table_destroy (data.exist_blocks);
|
|
return ret;
|
|
}
|
|
|
|
ret = verify_virtual_repos (&data);
|
|
|
|
g_hash_table_destroy (data.exist_blocks);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
verify_repos (GList *repo_id_list)
|
|
{
|
|
if (repo_id_list == NULL)
|
|
repo_id_list = seaf_repo_manager_get_repo_id_list (seaf->repo_mgr);
|
|
|
|
GList *ptr;
|
|
SeafRepo *repo;
|
|
int ret = 0;
|
|
|
|
for (ptr = repo_id_list; ptr != NULL; ptr = ptr->next) {
|
|
repo = seaf_repo_manager_get_repo_ex (seaf->repo_mgr, (const gchar *)ptr->data);
|
|
|
|
g_free (ptr->data);
|
|
|
|
if (!repo)
|
|
continue;
|
|
|
|
seaf_message ("Start to verify repo %s\n", repo->id);
|
|
if (repo->is_corrupted) {
|
|
seaf_warning ("Repo %s is corrupted.\n", repo->id);
|
|
} else {
|
|
ret = verify_repo (repo);
|
|
if (ret < 0) {
|
|
seaf_warning ("Failed to verify repo %s\n", repo->id);
|
|
seaf_repo_unref (repo);
|
|
continue;
|
|
}
|
|
seaf_message ("Verify repo %s success\n", repo->id);
|
|
seaf_repo_unref (repo);
|
|
}
|
|
}
|
|
|
|
g_list_free (repo_id_list);
|
|
|
|
return ret;
|
|
}
|