Skip to content

Commit

Permalink
Add repair-repo to fix corrupt fs (#657)
Browse files Browse the repository at this point in the history
Co-authored-by: 杨赫然 <[email protected]>
  • Loading branch information
feiniks and 杨赫然 authored Apr 29, 2024
1 parent 9f5fcdf commit b9e98c2
Show file tree
Hide file tree
Showing 7 changed files with 421 additions and 19 deletions.
2 changes: 2 additions & 0 deletions common/branch-mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ notify_repo_update (const char *repo_id, const char *commit_id)
static void
on_branch_updated (SeafBranchManager *mgr, SeafBranch *branch)
{
if (seaf->is_repair)
return;
seaf_repo_manager_update_repo_info (seaf->repo_mgr, branch->repo_id, branch->commit_id);

notify_repo_update(branch->repo_id, branch->commit_id);
Expand Down
153 changes: 135 additions & 18 deletions common/merge-new.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,31 +81,79 @@ merge_conflict_dirname (const char *store_id, int version,
return conflict_name;
}

static int
merge_entries (const char *store_id, int version,
int n, SeafDirent *dents[],
const char *basedir,
GList **dents_out,
MergeOptions *opt)
int twoway_merge(const char *store_id, int version, const char *basedir,
SeafDirent *dents[], GList **dents_out, struct MergeOptions *opt)
{
SeafDirent *files[3];
SeafDirent *files[2];
int i;
int n = opt->n_ways;

memset (files, 0, sizeof(files[0])*n);
for (i = 0; i < n; ++i) {
if (dents[i] && S_ISREG(dents[i]->mode))
files[i] = dents[i];
}

/* If we're running 2-way merge, or the caller requires not to
* actually merge contents, just call the callback function.
*/
if (n == 2 || !opt->do_merge)
return opt->callback (basedir, files, opt);
SeafDirent *head, *remote;
char *conflict_name;

/* Otherwise, we're doing a real 3-way merge of the trees.
* It means merge files and handle any conflicts.
*/
head = files[0];
remote = files[1];

if (head && remote) {
if (strcmp (head->id, remote->id) == 0) {
// file match
seaf_debug ("%s%s: files match\n", basedir, head->name);
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
} else {
// file content conflict
seaf_debug ("%s%s: files conflict\n", basedir, head->name);
conflict_name = merge_conflict_filename(store_id, version,
opt,
basedir,
head->name);
if (!conflict_name)
return -1;

g_free (remote->name);
remote->name = conflict_name;
remote->name_len = strlen (remote->name);

*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));

opt->conflict = TRUE;
}
} else if (!head && remote) {
// file not in head, but in remote
seaf_debug ("%s%s: added in remote\n", basedir, remote->name);
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
} else if (head && !remote) {
// file in head, but not in remote
seaf_debug ("%s%s: added in head\n", basedir, head->name);
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
}

return 0;
}

static int
threeway_merge (const char *store_id, int version,
SeafDirent *dents[],
const char *basedir,
GList **dents_out,
MergeOptions *opt)
{
SeafDirent *files[3];
int i;
gint64 curr_time;
int n = opt->n_ways;

memset (files, 0, sizeof(files[0])*n);
for (i = 0; i < n; ++i) {
if (dents[i] && S_ISREG(dents[i]->mode))
files[i] = dents[i];
}

SeafDirent *base, *head, *remote;
char *conflict_name;
Expand Down Expand Up @@ -323,6 +371,25 @@ merge_entries (const char *store_id, int version,
return 0;
}

static int
merge_entries (const char *store_id, int version,
int n, SeafDirent *dents[],
const char *basedir,
GList **dents_out,
MergeOptions *opt)
{
/* If we're running 2-way merge, it means merge files base on head and remote.
*/
if (n == 2)
return twoway_merge (store_id, version, basedir, dents, dents_out, opt);

/* Otherwise, we're doing a real 3-way merge of the trees.
* It means merge files and handle any conflicts.
*/

return threeway_merge (store_id, version, dents, basedir, dents_out, opt);
}

static int
merge_directories (const char *store_id, int version,
int n, SeafDirent *dents[],
Expand All @@ -345,7 +412,7 @@ merge_directories (const char *store_id, int version,

seaf_debug ("dir_mask = %d\n", dir_mask);

if (n == 3 && opt->do_merge) {
if (n == 3) {
switch (dir_mask) {
case 0:
g_return_val_if_reached (-1);
Expand Down Expand Up @@ -407,6 +474,33 @@ merge_directories (const char *store_id, int version,
default:
g_return_val_if_reached (-1);
}
} else if (n == 2) {
switch (dir_mask) {
case 0:
g_return_val_if_reached (-1);
case 1:
/*head is dir, remote is not dir*/
seaf_debug ("%s%s: only head is dir\n", basedir, dents[0]->name);
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[0]));
return 0;
case 2:
/*head is not dir, remote is dir*/
seaf_debug ("%s%s: only remote is dir\n", basedir, dents[1]->name);
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
return 0;
case 3:
if (strcmp (dents[0]->id, dents[1]->id) == 0) {
seaf_debug ("%s%s: dir is the same in head and remote\n",
basedir, dents[0]->name);
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
return 0;
}
seaf_debug ("%s%s: dir is changed in head and remote, merge recursively\n",
basedir, dents[0]->name);
break;
default:
g_return_val_if_reached (-1);
}
}

memset (sub_dirs, 0, sizeof(sub_dirs[0])*n);
Expand All @@ -433,7 +527,7 @@ merge_directories (const char *store_id, int version,

g_free (new_basedir);

if (n == 3 && opt->do_merge) {
if (n == 3) {
if (dir_mask == 3 || dir_mask == 6 || dir_mask == 7) {
merged_dent = seaf_dirent_dup (dents[1]);
memcpy (merged_dent->id, opt->merged_tree_root, 40);
Expand All @@ -443,6 +537,12 @@ merge_directories (const char *store_id, int version,
memcpy (merged_dent->id, opt->merged_tree_root, 40);
*dents_out = g_list_prepend (*dents_out, merged_dent);
}
} else if (n == 2) {
if (dir_mask == 3) {
merged_dent = seaf_dirent_dup (dents[1]);
memcpy (merged_dent->id, opt->merged_tree_root, 40);
*dents_out = g_list_prepend (*dents_out, merged_dent);
}
}

free_sub_dirs:
Expand Down Expand Up @@ -539,7 +639,7 @@ merge_trees_recursive (const char *store_id, int version,
}
}

if (n == 3 && opt->do_merge) {
if (n == 3) {
merged_dents = g_list_sort (merged_dents, compare_dirents);
merged_tree = seaf_dir_new (NULL, merged_dents,
dir_version_from_repo_version(version));
Expand All @@ -556,6 +656,23 @@ merge_trees_recursive (const char *store_id, int version,
seaf_warning ("Failed to save merged tree %s:%s.\n", store_id, basedir);
}
}
} else if (n == 2) {
merged_dents = g_list_sort (merged_dents, compare_dirents);
merged_tree = seaf_dir_new (NULL, merged_dents,
dir_version_from_repo_version(version));

memcpy (opt->merged_tree_root, merged_tree->dir_id, 40);

if ((trees[0] && strcmp (trees[0]->dir_id, merged_tree->dir_id) == 0) ||
(trees[1] && strcmp (trees[1]->dir_id, merged_tree->dir_id) == 0)) {
seaf_dir_free (merged_tree);
} else {
ret = seaf_dir_save (seaf->fs_mgr, store_id, version, merged_tree);
seaf_dir_free (merged_tree);
if (ret < 0) {
seaf_warning ("Failed to save merged tree %s:%s.\n", store_id, basedir);
}
}
}

return ret;
Expand Down
3 changes: 3 additions & 0 deletions server/repo-mgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -923,4 +923,7 @@ seaf_repo_manager_set_repo_status(SeafRepoManager *mgr,
int
seaf_repo_manager_get_repo_status(SeafRepoManager *mgr,
const char *repo_id);

int
seaf_repo_manager_repair_virtual_repo (char *repo_id);
#endif
17 changes: 16 additions & 1 deletion server/seaf-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ SeafileSession *seaf;

char *pidfile = NULL;

static const char *short_options = "hvc:d:l:fP:D:F:p:t";
static const char *short_options = "hvc:d:l:fP:D:F:p:tr:";
static struct option long_options[] = {
{ "help", no_argument, NULL, 'h', },
{ "version", no_argument, NULL, 'v', },
Expand All @@ -45,6 +45,7 @@ static struct option long_options[] = {
{ "pidfile", required_argument, NULL, 'P' },
{ "rpc-pipe-path", required_argument, NULL, 'p' },
{ "test-config", no_argument, NULL, 't' },
{ "repair-repo", required_argument, NULL, 'r' },
{ NULL, 0, NULL, 0, },
};

Expand Down Expand Up @@ -1211,6 +1212,7 @@ main (int argc, char **argv)
const char *debug_str = NULL;
int daemon_mode = 1;
gboolean test_config = FALSE;
char *repo_id = NULL;

#ifdef WIN32
argv = get_argv_utf8 (&argc);
Expand Down Expand Up @@ -1253,6 +1255,9 @@ main (int argc, char **argv)
case 't':
test_config = TRUE;
break;
case 'r':
repo_id = g_strdup (optarg);
break;
default:
usage ();
exit (1);
Expand Down Expand Up @@ -1315,6 +1320,16 @@ main (int argc, char **argv)

event_init ();

if (repo_id) {
seaf = seafile_repair_session_new (central_config_dir, seafile_dir, ccnet_dir);
if (!seaf) {
seaf_warning ("Failed to create repair seafile session.\n");
exit (1);
}
seaf_repo_manager_repair_virtual_repo (repo_id);
exit (0);
}

seaf = seafile_session_new (central_config_dir, seafile_dir, ccnet_dir);
if (!seaf) {
seaf_warning ("Failed to create seafile session.\n");
Expand Down
Loading

0 comments on commit b9e98c2

Please sign in to comment.