1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-06-26 15:11:34 +00:00
seafile-server/server/gc/seaf-migrate.c

306 lines
7.9 KiB
C

#include "common.h"
#define DEBUG_FLAG SEAFILE_DEBUG_OTHER
#include "log.h"
#include <getopt.h>
#include <ccnet.h>
#include "seafile-session.h"
#include "utils.h"
static char *config_dir = NULL;
static char *seafile_dir = NULL;
static char *central_config_dir = NULL;
CcnetClient *ccnet_client;
SeafileSession *seaf;
static const char *short_opts = "hvc:d:VDiF:";
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h', },
{ "version", no_argument, NULL, 'v', },
{ "config-file", required_argument, NULL, 'c', },
{ "central-config-dir", required_argument, NULL, 'F' },
{ "seafdir", required_argument, NULL, 'd', },
{ 0, 0, 0, 0, },
};
static int
migrate_v0_repos_to_v1_layout ();
static void usage ()
{
fprintf (stderr,
"usage: seaf-migrate [-c config_dir] [-d seafile_dir]\n");
}
#ifdef WIN32
/* Get the commandline arguments in unicode, then convert them to utf8 */
static char **
get_argv_utf8 (int *argc)
{
int i = 0;
char **argv = NULL;
const wchar_t *cmdline = NULL;
wchar_t **argv_w = NULL;
cmdline = GetCommandLineW();
argv_w = CommandLineToArgvW (cmdline, argc);
if (!argv_w) {
printf("failed to CommandLineToArgvW(), GLE=%lu\n", GetLastError());
return NULL;
}
argv = (char **)malloc (sizeof(char*) * (*argc));
for (i = 0; i < *argc; i++) {
argv[i] = wchar_to_utf8 (argv_w[i]);
}
return argv;
}
#endif
int
main(int argc, char *argv[])
{
int c;
#ifdef WIN32
argv = get_argv_utf8 (&argc);
#endif
config_dir = DEFAULT_CONFIG_DIR;
while ((c = getopt_long(argc, argv,
short_opts, long_opts, NULL)) != EOF) {
switch (c) {
case 'h':
usage();
exit(0);
case 'v':
exit(-1);
break;
case 'c':
config_dir = strdup(optarg);
break;
case 'd':
seafile_dir = strdup(optarg);
break;
case 'F':
central_config_dir = strdup(optarg);
break;
default:
usage();
exit(-1);
}
}
#if !GLIB_CHECK_VERSION(2, 35, 0)
g_type_init();
#endif
if (seafile_log_init ("-", "info", "debug") < 0) {
seaf_warning ("Failed to init log.\n");
exit (1);
}
ccnet_client = ccnet_client_new();
if ((ccnet_client_load_confdir(ccnet_client, central_config_dir, config_dir)) < 0) {
seaf_warning ("Read config dir error\n");
return -1;
}
if (seafile_dir == NULL)
seafile_dir = g_build_filename (config_dir, "seafile-data", NULL);
seaf = seafile_session_new(central_config_dir, seafile_dir, ccnet_client, TRUE);
if (!seaf) {
seaf_warning ("Failed to create seafile session.\n");
exit (1);
}
migrate_v0_repos_to_v1_layout ();
return 0;
}
typedef struct {
SeafRepo *repo;
GHashTable *visited;
/* > 0: keep a period of history;
* == 0: only keep data in head commit;
* < 0: keep all history data.
*/
gint64 truncate_time;
gboolean traversed_head;
gboolean stop_copy_blocks;
} MigrationData;
static int
migrate_file_blocks (SeafFSManager *mgr, MigrationData *data, const char *file_id)
{
SeafRepo *repo = data->repo;
Seafile *seafile;
int i;
char *block_id;
seafile = seaf_fs_manager_get_seafile (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) {
block_id = seafile->blk_sha1s[i];
if (seaf_block_manager_copy_block (seaf->block_mgr,
repo->store_id, repo->version,
repo->store_id, 1,
block_id) < 0) {
seaf_warning ("Failed to copy block %s.\n", block_id);
seafile_unref (seafile);
return -1;
}
}
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)
{
MigrationData *data = user_data;
SeafRepo *repo = data->repo;
if (data->visited != NULL) {
if (g_hash_table_lookup (data->visited, obj_id) != NULL) {
*stop = TRUE;
return TRUE;
}
char *key = g_strdup(obj_id);
g_hash_table_replace (data->visited, key, key);
}
if (seaf_obj_store_copy_obj (seaf->fs_mgr->obj_store,
repo->store_id, repo->version,
repo->store_id, 1,
obj_id) < 0) {
seaf_warning ("Failed to copy fs object %s.\n", obj_id);
return FALSE;
}
if (data->stop_copy_blocks)
return TRUE;
if (type == SEAF_METADATA_TYPE_FILE &&
migrate_file_blocks (mgr, data, obj_id) < 0)
return FALSE;
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 int
migrate_repo (SeafRepo *repo)
{
MigrationData *data;
int ret = 0;
seaf_message ("Migrating data for repo %.8s.\n", repo->id);
data = g_new0(MigrationData, 1);
data->repo = repo;
data->visited = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
gint64 truncate_time = seaf_repo_manager_get_repo_truncate_time (repo->manager,
repo->id);
data->truncate_time = truncate_time;
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
repo->id,
repo->version,
repo->head->commit_id,
traverse_commit,
data,
FALSE);
if (!res) {
seaf_warning ("Migration of repo %s is not completed.\n", repo->id);
ret = -1;
}
g_hash_table_destroy (data->visited);
g_free (data);
return ret;
}
static int
migrate_v0_repos_to_v1_layout ()
{
GList *repos = NULL, *ptr;
SeafRepo *repo;
gboolean error = FALSE;
repos = seaf_repo_manager_get_repo_list (seaf->repo_mgr, -1, -1, &error);
for (ptr = repos; ptr; ptr = ptr->next) {
repo = ptr->data;
if (!repo->is_corrupted && repo->version == 0)
migrate_repo (repo);
seaf_repo_unref (repo);
}
g_list_free (repos);
return 0;
}