static inline void transition_sync_state (SyncTask *task, int new_state) { g_assert (new_state >= 0 && new_state < SYNC_STATE_NUM); if (task->state != new_state) { if (!task->quiet && !(task->state == SYNC_STATE_DONE && new_state == SYNC_STATE_INIT) && !(task->state == SYNC_STATE_INIT && new_state == SYNC_STATE_DONE)) { seaf_message ("Repo '%s' sync state transition from '%s' to '%s'.\n", task->repo->name, sync_state_str[task->state], sync_state_str[new_state]); } if ((task->state == SYNC_STATE_MERGE || task->state == SYNC_STATE_UPLOAD) && new_state == SYNC_STATE_DONE && need_notify_sync(task->repo)) { GString *buf = g_string_new (NULL); g_string_append_printf (buf, "%s\t%s", task->repo->name, task->repo->id); seaf_mq_manager_publish_notification (seaf->mq_mgr, "sync.done", buf->str); g_string_free (buf, TRUE); } task->state = new_state; if (new_state == SYNC_STATE_DONE || new_state == SYNC_STATE_CANCELED || new_state == SYNC_STATE_ERROR) { task->info->in_sync = FALSE; --(task->mgr->n_running_tasks); if (new_state == SYNC_STATE_ERROR) task->info->err_cnt++; else task->info->err_cnt = 0; } } }
static int start_ccnet_server () { if (!ctl->config_dir) return -1; seaf_message ("starting ccnet-server ...\n"); char *argv[] = { "ccnet-server", "-c", ctl->config_dir, "-d", "-P", ctl->pidfile[PID_CCNET], NULL}; int pid = spawn_process (argv); if (pid <= 0) { seaf_warning ("Failed to spawn ccnet-server\n"); return -1; } return 0; }
static void calculate_send_object_list_done (void *vdata) { CcnetProcessor *processor = vdata; USE_PRIV; if (!priv->calc_success) { ccnet_processor_send_response (processor, SC_SHUTDOWN, SS_SHUTDOWN, NULL, 0); ccnet_processor_done (processor, FALSE); return; } if (priv->send_obj_list == NULL) { seaf_message ("No fs objects to put. Done.\n"); ccnet_processor_send_response (processor, SC_END, SS_END, NULL, 0); ccnet_processor_done (processor, TRUE); return; } send_object_list_segments (processor); processor->state = SEND_OBJECTS; }
static int start_seaf_monitor () { if (!ctl->config_dir || !ctl->seafile_dir) return -1; seaf_message ("starting seaf-mon ...\n"); char *argv[] = { "seaf-mon", "-c", ctl->config_dir, "-d", ctl->seafile_dir, "-P", ctl->pidfile[PID_MONITOR], NULL}; int pid = spawn_process (argv); if (pid <= 0) { seaf_warning ("Failed to spawn seaf-mon\n"); return -1; } return 0; }
/* * check and recover repo, for curropted file or folder set it empty */ static void check_and_recover_repo (SeafRepo *repo, gboolean reset, gboolean repair) { FsckData fsck_data; SeafCommit *rep_commit; seaf_message ("Checking file system integrity of repo %s(%.8s)...\n", repo->name, repo->id); rep_commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); memset (&fsck_data, 0, sizeof(fsck_data)); fsck_data.repair = repair; fsck_data.repo = repo; fsck_data.existing_blocks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); char *root_id = fsck_check_dir_recursive (rep_commit->root_id, "/", &fsck_data); g_hash_table_destroy (fsck_data.existing_blocks); if (root_id == NULL) return; if (repair) { if (strcmp (root_id, rep_commit->root_id) != 0) { // some fs objects curropted for the head commit, // create new head commit using the new root_id reset_commit_to_repair (repo, rep_commit, root_id); } else if (reset) { // for reset commit but fs objects not curropted, also create a repaired commit reset_commit_to_repair (repo, rep_commit, rep_commit->root_id); } } g_free (root_id); seaf_commit_unref (rep_commit); }
static void repair_repos (GList *repo_id_list, gboolean repair) { GList *ptr; char *repo_id; SeafRepo *repo; gboolean exists; gboolean reset; gboolean io_error; for (ptr = repo_id_list; ptr; ptr = ptr->next) { reset = FALSE; repo_id = ptr->data; seaf_message ("Running fsck for repo %s.\n", repo_id); if (!is_uuid_valid (repo_id)) { seaf_warning ("Invalid repo id %s.\n", repo_id); goto next; } exists = seaf_repo_manager_repo_exists (seaf->repo_mgr, repo_id); if (!exists) { seaf_warning ("Repo %.8s doesn't exist.\n", repo_id); goto next; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { seaf_message ("Repo %.8s HEAD commit is corrupted, " "need to restore to an old version.\n", repo_id); repo = get_available_repo (repo_id, repair); if (!repo) { goto next; } reset = TRUE; } else { SeafCommit *commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!commit) { seaf_warning ("Failed to get head commit %s of repo %s\n", repo->head->commit_id, repo->id); seaf_repo_unref (repo); goto next; } io_error = FALSE; if (!fsck_verify_seafobj (repo->store_id, repo->version, commit->root_id, &io_error, VERIFY_DIR, repair)) { if (io_error) { seaf_commit_unref (commit); seaf_repo_unref (repo); goto next; } else { // root fs object is corrupted, get available commit seaf_message ("Repo %.8s HEAD commit is corrupted, " "need to restore to an old version.\n", repo_id); seaf_commit_unref (commit); seaf_repo_unref (repo); repo = get_available_repo (repo_id, repair); if (!repo) { goto next; } reset = TRUE; } } else { // head commit is available seaf_commit_unref (commit); } } check_and_recover_repo (repo, reset, repair); seaf_repo_unref (repo); next: seaf_message ("Fsck finished for repo %.8s.\n\n", repo_id); } }
int main(int argc, char *argv[]) { evbase_t *evbase = NULL; evhtp_t *htp = NULL; int daemon_mode = 1; int c; char *logfile = NULL; char *ccnet_debug_level_str = "info"; char *http_debug_level_str = "debug"; const char *debug_str = NULL; 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 't': num_threads = atoi(optarg); break; case 'f': daemon_mode = 0; break; case 'l': logfile = g_strdup(optarg); break; case 'g': ccnet_debug_level_str = optarg; break; case 'G': http_debug_level_str = optarg; break; case 'D': debug_str = optarg; break; default: usage(); exit(-1); } } #ifndef WIN32 if (daemon_mode) daemon(1, 0); #endif g_type_init(); ccnet_client = ccnet_client_new(); if ((ccnet_client_load_confdir(ccnet_client, config_dir)) < 0) { g_warning ("Read config dir error\n"); return -1; } if (seafile_dir == NULL) seafile_dir = g_build_filename (config_dir, "seafile-data", NULL); if (logfile == NULL) logfile = g_build_filename (seafile_dir, "http.log", NULL); seaf = seafile_session_new (seafile_dir, ccnet_client); if (!seaf) { g_warning ("Failed to create seafile session.\n"); exit (1); } if (seafile_session_init(seaf) < 0) exit (1); seaf->client_pool = ccnet_client_pool_new (config_dir); if (!debug_str) debug_str = g_getenv("SEAFILE_DEBUG"); seafile_debug_set_flags_string (debug_str); if (seafile_log_init (logfile, ccnet_debug_level_str, http_debug_level_str) < 0) { g_warning ("Failed to init log.\n"); exit (1); } load_httpserver_config (seaf); if (use_https) { seaf_message ("port = %d, https = true, pemfile = %s, privkey = %s\n", bind_port, pemfile, privkey); } else { seaf_message ("port = %d, https = false\n", bind_port); } evbase = event_base_new(); htp = evhtp_new(evbase, NULL); if (pemfile != NULL) { evhtp_ssl_cfg_t scfg; memset (&scfg, 0, sizeof(scfg)); scfg.pemfile = pemfile; scfg.privfile = privkey; scfg.scache_type = evhtp_ssl_scache_type_internal; scfg.scache_timeout = 5000; evhtp_ssl_init (htp, &scfg); } if (access_file_init (htp) < 0) exit (1); if (upload_file_init (htp) < 0) exit (1); evhtp_set_gencb(htp, default_cb, NULL); evhtp_use_threads(htp, NULL, num_threads, NULL); if (evhtp_bind_socket(htp, bind_addr, bind_port, 128) < 0) { g_warning ("Could not bind socket: %s\n", strerror(errno)); exit(-1); } event_base_loop(evbase, 0); return 0; }
int main(int argc, char *argv[]) { int c; gboolean repair = FALSE; gboolean esync = FALSE; char *export_path = NULL; #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 'r': repair = TRUE; break; case 'e': esync = TRUE; break; case 'E': export_path = strdup(optarg); break; case 'c': config_dir = strdup(optarg); break; case 'd': seafile_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, 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); #ifdef __linux__ uid_t current_user, seafile_user; if (!check_user (seafile_dir, ¤t_user, &seafile_user)) { seaf_message ("Current user (%u) is not the user for running " "seafile server (%u). Unable to run fsck.\n", current_user, seafile_user); exit(1); } #endif seaf = seafile_session_new(seafile_dir, ccnet_client); if (!seaf) { seaf_warning ("Failed to create seafile session.\n"); exit (1); } GList *repo_id_list = NULL; int i; for (i = optind; i < argc; i++) repo_id_list = g_list_append (repo_id_list, g_strdup(argv[i])); if (export_path) { export_file (repo_id_list, seafile_dir, export_path); } else { seaf_fsck (repo_id_list, repair, esync); } return 0; }
char * seaf_clone_manager_add_download_task (SeafCloneManager *mgr, const char *repo_id, int repo_version, const char *peer_id, const char *repo_name, const char *token, const char *passwd, const char *magic, int enc_version, const char *random_key, const char *wt_parent, const char *peer_addr, const char *peer_port, const char *email, const char *more_info, GError **error) { SeafRepo *repo; char *wt_tmp, *worktree; char *ret; if (!seaf->started) { seaf_message ("System not started, skip adding clone task.\n"); return NULL; } #ifdef USE_GPL_CRYPTO if (repo_version == 0 || (passwd && enc_version < 2)) { seaf_warning ("Don't support syncing old version libraries.\n"); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Don't support syncing old version libraries"); return NULL; } #endif if (passwd && !check_encryption_args (magic, enc_version, random_key, error)) return NULL; /* After a repo was unsynced, the sync task may still be blocked in the * network, so the repo is not actually deleted yet. * In this case just return an error to the user. */ SyncInfo *sync_info = seaf_sync_manager_get_sync_info (seaf->sync_mgr, repo_id); if (sync_info && sync_info->in_sync) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Repo already exists"); return NULL; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (repo != NULL && repo->head != NULL) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Repo already exists"); return NULL; } if (is_duplicate_task (mgr, repo_id)) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Task is already in progress"); return NULL; } if (passwd && seafile_verify_repo_passwd(repo_id, passwd, magic, enc_version) < 0) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Incorrect password"); return NULL; } IgnoreReason reason; if (should_ignore_on_checkout (repo_name, &reason)) { if (reason == IGNORE_REASON_END_SPACE_PERIOD) g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Library name ends with space or period character"); else g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Library name contains invalid characters such as ':', '*', '|', '?'"); return NULL; } wt_tmp = g_build_filename (wt_parent, repo_name, NULL); worktree = make_worktree_for_download (mgr, wt_tmp, error); if (!worktree) { g_free (wt_tmp); return NULL; } /* If a repo was unsynced and then downloaded again, there may be * a garbage record for this repo. We don't want the downloaded blocks * be removed by GC. */ if (repo_version > 0) seaf_repo_manager_remove_garbage_repo (seaf->repo_mgr, repo_id); /* Delete orphan information in the db in case the repo was corrupt. */ if (!repo) seaf_repo_manager_remove_repo_ondisk (seaf->repo_mgr, repo_id, FALSE); ret = add_task_common (mgr, repo_id, repo_version, peer_id, repo_name, token, passwd, enc_version, random_key, worktree, peer_addr, peer_port, email, more_info, TRUE, error); g_free (worktree); g_free (wt_tmp); return ret; }
char * seaf_clone_manager_add_task (SeafCloneManager *mgr, const char *repo_id, int repo_version, const char *peer_id, const char *repo_name, const char *token, const char *passwd, const char *magic, int enc_version, const char *random_key, const char *worktree_in, const char *peer_addr, const char *peer_port, const char *email, const char *more_info, GError **error) { SeafRepo *repo; char *worktree; char *ret; gboolean sync_wt_name = FALSE; if (!seaf->started) { seaf_message ("System not started, skip adding clone task.\n"); return NULL; } #ifdef USE_GPL_CRYPTO if (repo_version == 0 || (passwd && enc_version < 2)) { seaf_warning ("Don't support syncing old version libraries.\n"); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Don't support syncing old version libraries"); return NULL; } #endif if (passwd && !check_encryption_args (magic, enc_version, random_key, error)) return NULL; /* After a repo was unsynced, the sync task may still be blocked in the * network, so the repo is not actually deleted yet. * In this case just return an error to the user. */ SyncInfo *sync_info = seaf_sync_manager_get_sync_info (seaf->sync_mgr, repo_id); if (sync_info && sync_info->in_sync) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Repo already exists"); return NULL; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (repo != NULL && repo->head != NULL) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Repo already exists"); return NULL; } if (is_duplicate_task (mgr, repo_id)) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Task is already in progress"); return NULL; } if (passwd && seafile_verify_repo_passwd(repo_id, passwd, magic, enc_version) < 0) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Incorrect password"); return NULL; } if (!seaf_clone_manager_check_worktree_path (mgr, worktree_in, error)) return NULL; /* Return error if worktree_in conflicts with another repo or * is not a directory. */ worktree = make_worktree (mgr, worktree_in, FALSE, error); if (!worktree) { return NULL; } /* Don't sync worktree folder name with library name later if they're not the same * at the beginning. */ sync_wt_name = is_wt_repo_name_same (worktree, repo_name); /* If a repo was unsynced and then downloaded again, there may be * a garbage record for this repo. We don't want the downloaded blocks * be removed by GC. */ if (repo_version > 0) seaf_repo_manager_remove_garbage_repo (seaf->repo_mgr, repo_id); /* Delete orphan information in the db in case the repo was corrupt. */ if (!repo) seaf_repo_manager_remove_repo_ondisk (seaf->repo_mgr, repo_id, FALSE); ret = add_task_common (mgr, repo_id, repo_version, peer_id, repo_name, token, passwd, enc_version, random_key, worktree, peer_addr, peer_port, email, more_info, sync_wt_name, error); g_free (worktree); return ret; }
static void handle_response (CcnetProcessor *processor, char *code, char *code_msg, char *content, int clen) { SeafileCheckTxV3Proc *proc = (SeafileCheckTxV3Proc *)processor; TransferTask *task = proc->task; if (strncmp(code, SC_OK, 3) == 0) { if (proc->type == CHECK_TX_TYPE_UPLOAD) handle_upload_ok (processor, task, content, clen); else handle_download_ok (processor, task, content, clen); } else if (strncmp (code, SC_PUT_TOKEN, 3) == 0) { /* In LAN sync, we don't use session token. */ if (clen == 0) { ccnet_processor_done (processor, TRUE); return; } if (content[clen-1] != '\0') { seaf_warning ("Bad response content.\n"); transfer_task_set_error (task, TASK_ERR_UNKNOWN); ccnet_processor_send_update (processor, SC_BAD_ARGS, SS_BAD_ARGS, NULL, 0); ccnet_processor_done (processor, FALSE); return; } task->session_token = g_strdup (content); ccnet_processor_send_update (processor, SC_GET_VERSION, SS_GET_VERSION, NULL, 0); } else if (strncmp (code, SC_VERSION, 3) == 0) { int server_version = atoi(content); /* There is a bug in block transfer in version 4, so it's not supported. */ if (server_version == 4) server_version = 3; task->protocol_version = MIN (server_version, CURRENT_PROTO_VERSION); if (task->protocol_version < 5) { seaf_warning ("Deprecated server protocol version %d.\n", task->protocol_version); transfer_task_set_error (task, TASK_ERR_DEPRECATED_SERVER); ccnet_processor_done (processor, FALSE); return; } if (task->repo_version == 0) task->protocol_version = 5; else if (task->protocol_version == 5) { /* Syncing version 1 reop with 2.x server is not supported. * Actually version 1 repo can only be created by 3.x servers. * If version 1 repos exist on 2.x server, it means a down-grade * operation has been performed, which is not supported. */ seaf_warning ("Syncing version %d repo with protocol version %d " "is not supported.\n", task->repo_version, task->protocol_version); transfer_task_set_error (task, TASK_ERR_DEPRECATED_SERVER); ccnet_processor_done (processor, FALSE); return; } if (task->protocol_version >= 7 && !task->server_side_merge) task->protocol_version = 6; if (task->protocol_version >= 7 && task->type == TASK_TYPE_DOWNLOAD) set_download_head_info (task); seaf_message ("repo version is %d, protocol version is %d.\n", task->repo_version, task->protocol_version); ccnet_processor_done (processor, TRUE); } else { seaf_warning ("[check tx v3] Bad response: %s %s", code, code_msg); if (strncmp(code, SC_ACCESS_DENIED, 3) == 0) transfer_task_set_error (task, TASK_ERR_ACCESS_DENIED); else if (strncmp(code, SC_QUOTA_ERROR, 3) == 0) transfer_task_set_error (task, TASK_ERR_CHECK_QUOTA); else if (strncmp(code, SC_QUOTA_FULL, 3) == 0) transfer_task_set_error (task, TASK_ERR_QUOTA_FULL); else if (strncmp(code, SC_PROTOCOL_MISMATCH, 3) == 0) transfer_task_set_error (task, TASK_ERR_PROTOCOL_VERSION); else if (strncmp(code, SC_BAD_REPO, 3) == 0) transfer_task_set_error (task, TASK_ERR_BAD_REPO_ID); else transfer_task_set_error (task, TASK_ERR_UNKNOWN); ccnet_processor_done (processor, FALSE); } }
static int read_seafdav_config() { int ret = 0; char *seafdav_conf = NULL; GKeyFile *key_file = NULL; GError *error = NULL; seafdav_conf = g_build_filename(ctl->central_config_dir, "seafdav.conf", NULL); if (!g_file_test(seafdav_conf, G_FILE_TEST_EXISTS)) { goto out; } key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, seafdav_conf, G_KEY_FILE_KEEP_COMMENTS, NULL)) { seaf_warning("Failed to load seafdav.conf\n"); ret = -1; goto out; } /* enabled */ ctl->seafdav_config.enabled = g_key_file_get_boolean(key_file, "WEBDAV", "enabled", &error); if (error != NULL) { if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { seaf_message ("Error when reading WEBDAV.enabled, use default value 'false'\n"); } ctl->seafdav_config.enabled = FALSE; g_clear_error (&error); goto out; } if (!ctl->seafdav_config.enabled) { goto out; } /* fastcgi */ ctl->seafdav_config.fastcgi = g_key_file_get_boolean(key_file, "WEBDAV", "fastcgi", &error); if (error != NULL) { if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { seaf_message ("Error when reading WEBDAV.fastcgi, use default value 'false'\n"); } ctl->seafdav_config.fastcgi = FALSE; g_clear_error (&error); } /* host */ char *host = seaf_key_file_get_string (key_file, "WEBDAV", "host", &error); if (error != NULL) { g_clear_error(&error); ctl->seafdav_config.host = g_strdup(ctl->seafdav_config.fastcgi ? "localhost" : "0.0.0.0"); } else { ctl->seafdav_config.host = host; } /* port */ ctl->seafdav_config.port = g_key_file_get_integer(key_file, "WEBDAV", "port", &error); if (error != NULL) { if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { seaf_message ("Error when reading WEBDAV.port, use deafult value 8080\n"); } ctl->seafdav_config.port = 8080; g_clear_error (&error); } if (ctl->seafdav_config.port <= 0 || ctl->seafdav_config.port > 65535) { seaf_warning("Failed to load seafdav config: invalid port %d\n", ctl->seafdav_config.port); ret = -1; goto out; } out: if (key_file) { g_key_file_free (key_file); } g_free (seafdav_conf); return ret; }
int main (int argc, char **argv) { int c; char *config_dir = DEFAULT_CONFIG_DIR; char *seafile_dir = NULL; char *worktree_dir = NULL; char *logfile = NULL; const char *debug_str = NULL; int daemon_mode = 0; CcnetClient *client; char *ccnet_debug_level_str = "info"; char *seafile_debug_level_str = "debug"; #ifdef WIN32 LoadLibraryA ("exchndl.dll"); argv = get_argv_utf8 (&argc); #endif while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != EOF) { switch (c) { case 'h': usage(); exit (1); break; case 'v': exit (1); break; case 'c': config_dir = optarg; break; case 'd': seafile_dir = g_strdup(optarg); break; case 'b': daemon_mode = 1; break; case 'D': debug_str = optarg; break; case 'w': worktree_dir = g_strdup(optarg); break; case 'l': logfile = g_strdup(optarg); break; case 'g': ccnet_debug_level_str = optarg; break; case 'G': seafile_debug_level_str = optarg; break; default: usage (); exit (1); } } argc -= optind; argv += optind; #ifndef WIN32 if (daemon_mode) { #ifndef __APPLE__ daemon (1, 0); #else /* __APPLE */ /* daemon is deprecated under APPLE * use fork() instead * */ switch (fork ()) { case -1: seaf_warning ("Failed to daemonize"); exit (-1); break; case 0: /* all good*/ break; default: /* kill origin process */ exit (0); } #endif /* __APPLE */ } #endif /* !WIN32 */ cdc_init (); #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init(); #endif #if !GLIB_CHECK_VERSION(2, 31, 0) g_thread_init(NULL); #endif if (!debug_str) debug_str = g_getenv("SEAFILE_DEBUG"); seafile_debug_set_flags_string (debug_str); if (logfile == NULL) logfile = g_build_filename (config_dir, "logs", "seafile.log", NULL); if (seafile_log_init (logfile, ccnet_debug_level_str, seafile_debug_level_str) < 0) { seaf_warning ("Failed to init log.\n"); exit (1); } if (!bind_ccnet_service (config_dir)) { seaf_warning ("Failed to bind ccnet service\n"); exit (1); } /* init ccnet */ client = ccnet_init (config_dir); if (!client) exit (1); start_rpc_service (client); create_sync_rpc_clients (config_dir); appletrpc_client = ccnet_create_async_rpc_client (client, NULL, "applet-rpcserver"); /* init seafile */ if (seafile_dir == NULL) seafile_dir = g_build_filename (config_dir, "seafile-data", NULL); if (worktree_dir == NULL) worktree_dir = g_build_filename (g_get_home_dir(), "seafile", NULL); seaf = seafile_session_new (seafile_dir, worktree_dir, client); if (!seaf) { seaf_warning ("Failed to create seafile session.\n"); exit (1); } seaf->ccnetrpc_client = ccnetrpc_client; seaf->appletrpc_client = appletrpc_client; seaf_message ("starting seafile client "SEAFILE_CLIENT_VERSION"\n"); #if defined(SEAFILE_SOURCE_COMMIT_ID) seaf_message ("seafile source code version "SEAFILE_SOURCE_COMMIT_ID"\n"); #endif g_free (seafile_dir); g_free (worktree_dir); g_free (logfile); set_signal_handlers (seaf); seafile_session_prepare (seaf); seafile_session_start (seaf); seafile_session_config_set_string (seaf, "wktree", seaf->worktree_dir); ccnet_main (client); return 0; }
static void enable_sync_repo (const char *repo_id) { SeafRepo *repo = NULL; SeafCommit *parent_commit = NULL; SeafCommit *new_commit = NULL; gboolean exists; if (!is_uuid_valid (repo_id)) { seaf_warning ("Invalid repo id %s.\n", repo_id); return; } exists = seaf_repo_manager_repo_exists (seaf->repo_mgr, repo_id); if (!exists) { seaf_warning ("Repo %.8s doesn't exist.\n", repo_id); return; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) return; if (!repo->repaired) { seaf_repo_unref (repo); return; } seaf_message ("Enabling sync repo %s.\n", repo_id); parent_commit = seaf_commit_manager_get_commit_compatible (seaf->commit_mgr, repo_id, repo->head->commit_id); if (!parent_commit) { seaf_warning ("Commit %s:%s is missing\n", repo_id, repo->head->commit_id); goto out; } new_commit = seaf_commit_new (NULL, repo_id, parent_commit->root_id, parent_commit->creator_name, parent_commit->creator_id, "Enable sync repo", 0); if (!new_commit) { seaf_warning ("Out of memory when create commit.\n"); goto out; } new_commit->parent_id = g_strdup (parent_commit->commit_id); seaf_repo_to_commit (repo, new_commit); new_commit->repaired = FALSE; if (seaf_commit_manager_add_commit (seaf->commit_mgr, new_commit) < 0) { seaf_warning ("Failed to save commit %.8s for repo %.8s.\n", new_commit->commit_id, repo_id); goto out; } seaf_branch_set_commit (repo->head, new_commit->commit_id); if (seaf_branch_manager_update_branch (seaf->branch_mgr, repo->head) < 0) { seaf_warning ("Failed to update head commit %.8s to repo %.8s.\n", new_commit->commit_id, repo_id); } else { seaf_message ("Enable sync repo %.8s success.\n", repo_id); } out: if (parent_commit) seaf_commit_unref (parent_commit); if (new_commit) seaf_commit_unref (new_commit); if (repo) seaf_repo_unref (repo); }
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; }
static char* fsck_check_dir_recursive (const char *id, const char *parent_dir, FsckData *fsck_data) { SeafDir *dir; SeafDir *new_dir; GList *p; SeafDirent *seaf_dent; char *dir_id = NULL; char *path = NULL; gboolean io_error = FALSE; SeafFSManager *mgr = seaf->fs_mgr; char *store_id = fsck_data->repo->store_id; int version = fsck_data->repo->version; gboolean is_corrupted = FALSE; dir = seaf_fs_manager_get_seafdir (mgr, store_id, version, id); for (p = dir->entries; p; p = p->next) { seaf_dent = p->data; io_error = FALSE; if (S_ISREG(seaf_dent->mode)) { path = g_strdup_printf ("%s%s", parent_dir, seaf_dent->name); if (!path) { seaf_warning ("Out of memory, stop to run fsck for repo %.8s.\n", fsck_data->repo->id); goto out; } if (!fsck_verify_seafobj (store_id, version, seaf_dent->id, &io_error, VERIFY_FILE, fsck_data->repair)) { if (io_error) { g_free (path); goto out; } is_corrupted = TRUE; if (fsck_data->repair) { seaf_message ("File %s(%.8s) is corrupted, recreate an empty file.\n", path, seaf_dent->id); } else { seaf_message ("File %s(%.8s) is corrupted.\n", path, seaf_dent->id); } // file corrupted, set it empty memcpy (seaf_dent->id, EMPTY_SHA1, 40); seaf_dent->size = 0; } else { if (check_blocks (seaf_dent->id, fsck_data, &io_error) < 0) { if (io_error) { g_free (path); goto out; } is_corrupted = TRUE; if (fsck_data->repair) { seaf_message ("File %s(%.8s) is corrupted, recreate an empty file.\n", path, seaf_dent->id); } else { seaf_message ("File %s(%.8s) is corrupted.\n", path, seaf_dent->id); } // file corrupted, set it empty memcpy (seaf_dent->id, EMPTY_SHA1, 40); seaf_dent->size = 0; } } g_free (path); } else if (S_ISDIR(seaf_dent->mode)) { path = g_strdup_printf ("%s%s/", parent_dir, seaf_dent->name); if (!path) { seaf_warning ("Out of memory, stop to run fsck for repo %.8s.\n", fsck_data->repo->id); goto out; } if (!fsck_verify_seafobj (store_id, version, seaf_dent->id, &io_error, VERIFY_DIR, fsck_data->repair)) { if (io_error) { g_free (path); goto out; } if (fsck_data->repair) { seaf_message ("Dir %s(%.8s) is corrupted, recreate an empty dir.\n", path, seaf_dent->id); } else { seaf_message ("Dir %s(%.8s) is corrupted.\n", path, seaf_dent->id); } is_corrupted = TRUE; // dir corrupted, set it empty memcpy (seaf_dent->id, EMPTY_SHA1, 40); } else { dir_id = fsck_check_dir_recursive (seaf_dent->id, path, fsck_data); if (dir_id == NULL) { // IO error g_free (path); goto out; } if (strcmp (dir_id, seaf_dent->id) != 0) { is_corrupted = TRUE; // dir corrupted, set it to new dir_id memcpy (seaf_dent->id, dir_id, 41); } g_free (dir_id); } g_free (path); } } if (is_corrupted) { new_dir = seaf_dir_new (NULL, dir->entries, version); if (fsck_data->repair) { if (seaf_dir_save (mgr, store_id, version, new_dir) < 0) { seaf_warning ("Failed to save dir\n"); seaf_dir_free (new_dir); goto out; } } dir_id = g_strdup (new_dir->dir_id); seaf_dir_free (new_dir); dir->entries = NULL; } else { dir_id = g_strdup (dir->dir_id); } out: seaf_dir_free (dir); return dir_id; }
static void print_enc_repo (gpointer key, gpointer value, gpointer user_data) { seaf_message ("%s(%s)\n", (char *)key, (char *)value); }
char * seaf_clone_manager_add_download_task (SeafCloneManager *mgr, const char *repo_id, const char *peer_id, const char *repo_name, const char *token, const char *passwd, const char *magic, const char *wt_parent, const char *peer_addr, const char *peer_port, const char *email, GError **error) { SeafRepo *repo; char *wt_tmp, *worktree; char *ret; if (!seaf->started) { seaf_message ("System not started, skip adding clone task.\n"); return NULL; } g_assert (strlen(repo_id) == 36); repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (repo != NULL && repo->head != NULL) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Repo already exists"); return NULL; } if (is_duplicate_task (mgr, repo_id)) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Task is already in progress"); return NULL; } /* If magic is not given, check password before checkout. */ if (passwd && magic && seaf_repo_verify_passwd(repo_id, passwd, magic) < 0) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Incorrect password"); return NULL; } wt_tmp = g_build_filename (wt_parent, repo_name, NULL); worktree = make_worktree_for_download (mgr, wt_tmp, error); if (!worktree) { g_free (wt_tmp); return NULL; } ret = add_task_common (mgr, repo, repo_id, peer_id, repo_name, token, passwd, worktree, peer_addr, peer_port, email, error); g_free (worktree); g_free (wt_tmp); return ret; }
static int fast_forward_or_merge (const char *repo_id, SeafCommit *base, SeafCommit *new_commit) { #define MAX_RETRY_COUNT 3 SeafRepo *repo = NULL; SeafCommit *current_head = NULL, *merged_commit = NULL; int retry_cnt = 0; int ret = 0; repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { seaf_warning ("Repo %s doesn't exist.\n", repo_id); ret = -1; goto out; } retry: current_head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!current_head) { seaf_warning ("Failed to find head commit of %s.\n", repo_id); ret = -1; goto out; } /* Merge if base and head are not the same. */ if (strcmp (base->commit_id, current_head->commit_id) != 0) { MergeOptions opt; const char *roots[3]; char *desc = NULL; memset (&opt, 0, sizeof(opt)); opt.n_ways = 3; memcpy (opt.remote_repo_id, repo_id, 36); memcpy (opt.remote_head, new_commit->commit_id, 40); opt.do_merge = TRUE; roots[0] = base->root_id; /* base */ roots[1] = current_head->root_id; /* head */ roots[2] = new_commit->root_id; /* remote */ if (seaf_merge_trees (repo->store_id, repo->version, 3, roots, &opt) < 0) { seaf_warning ("Failed to merge.\n"); ret = -1; goto out; } if (!opt.conflict) desc = g_strdup("Auto merge by system"); else { desc = gen_merge_description (repo, opt.merged_tree_root, current_head->root_id, new_commit->root_id); if (!desc) desc = g_strdup("Auto merge by system"); } merged_commit = seaf_commit_new(NULL, repo->id, opt.merged_tree_root, new_commit->creator_name, EMPTY_SHA1, desc, 0); g_free (desc); merged_commit->parent_id = g_strdup (current_head->commit_id); merged_commit->second_parent_id = g_strdup (new_commit->commit_id); merged_commit->new_merge = TRUE; if (opt.conflict) merged_commit->conflict = TRUE; seaf_repo_to_commit (repo, merged_commit); if (seaf_commit_manager_add_commit (seaf->commit_mgr, merged_commit) < 0) { seaf_warning ("Failed to add commit.\n"); ret = -1; goto out; } } else { seaf_commit_ref (new_commit); merged_commit = new_commit; } seaf_branch_set_commit(repo->head, merged_commit->commit_id); if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr, repo->head, current_head->commit_id) < 0) { seaf_repo_unref (repo); repo = NULL; seaf_commit_unref (current_head); current_head = NULL; seaf_commit_unref (merged_commit); merged_commit = NULL; repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { seaf_warning ("Repo %s doesn't exist.\n", repo_id); ret = -1; goto out; } if (++retry_cnt <= MAX_RETRY_COUNT) { seaf_message ("Concurrent branch update, retry.\n"); /* Sleep random time between 100 and 1000 millisecs. */ USLEEP (g_random_int_range(1, 11) * 100 * 1000); goto retry; } else { seaf_warning ("Stop retrying.\n"); ret = -1; goto out; } } out: seaf_commit_unref (current_head); seaf_commit_unref (merged_commit); seaf_repo_unref (repo); return ret; }
static SeafCommit* get_available_commit (const char *repo_id) { GList *commit_list = NULL; GList *temp_list = NULL; GList *next_list = NULL; SeafCommit *temp_commit = NULL; gboolean io_error; seaf_message ("Scanning available commits for repo %s...\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) { seaf_warning ("No available commits for repo %.8s, export failed.\n\n", repo_id); return NULL; } commit_list = g_list_sort (commit_list, compare_commit_by_ctime); temp_list = commit_list; while (temp_list) { next_list = temp_list->next; temp_commit = temp_list->data; io_error = FALSE; if (memcmp (temp_commit->root_id, EMPTY_SHA1, 40) == 0) { seaf_commit_unref (temp_commit); temp_commit = NULL; temp_list = next_list; continue; } else if (!fsck_verify_seafobj (repo_id, 1, temp_commit->root_id, &io_error, VERIFY_DIR, FALSE)) { seaf_commit_unref (temp_commit); temp_commit = NULL; temp_list = next_list; if (io_error) { break; } // fs object of this commit is corrupted, // continue to verify next continue; } 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), will export files from it.\n", temp_commit->commit_id, time_buf); temp_list = next_list; break; } while (temp_list) { seaf_commit_unref (temp_list->data); temp_list = temp_list->next; } g_list_free (commit_list); if (!temp_commit && !io_error) { seaf_warning ("No available commits for repo %.8s, export failed.\n\n", repo_id); } return temp_commit; }
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; }
int main (int argc, char **argv) { int c; char *config_dir = DEFAULT_CONFIG_DIR; char *seafile_dir = NULL; char *worktree_dir = NULL; char *logfile = NULL; const char *debug_str = NULL; int daemon_mode = 0; CcnetClient *client; char *ccnet_debug_level_str = "info"; char *seafile_debug_level_str = "debug"; #ifdef WIN32 argv = get_argv_utf8 (&argc); #endif while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != EOF) { switch (c) { case 'h': exit (1); break; case 'v': exit (1); break; case 'c': config_dir = optarg; break; case 'd': seafile_dir = g_strdup(optarg); break; case 'b': daemon_mode = 1; break; case 'D': debug_str = optarg; break; case 'w': worktree_dir = g_strdup(optarg); break; case 'l': logfile = g_strdup(optarg); break; case 'g': ccnet_debug_level_str = optarg; break; case 'G': seafile_debug_level_str = optarg; break; default: usage (); exit (1); } } argc -= optind; argv += optind; #ifndef WIN32 #ifndef __APPLE__ if (daemon_mode) daemon (1, 0); #endif #endif g_type_init (); #if !GLIB_CHECK_VERSION(2,32,0) g_thread_init (NULL); #endif if (!debug_str) debug_str = g_getenv("SEAFILE_DEBUG"); seafile_debug_set_flags_string (debug_str); /* init ccnet */ client = ccnet_init (config_dir); if (!client) exit (1); register_processors (client); start_rpc_service (client); create_sync_rpc_clients (config_dir); appletrpc_client = ccnet_create_async_rpc_client (client, NULL, "applet-rpcserver"); /* init seafile */ if (seafile_dir == NULL) seafile_dir = g_build_filename (config_dir, "seafile-data", NULL); if (worktree_dir == NULL) worktree_dir = g_build_filename (g_get_home_dir(), "seafile", NULL); if (logfile == NULL) logfile = g_build_filename (config_dir, "logs", "seafile.log", NULL); seaf = seafile_session_new (seafile_dir, worktree_dir, client); if (!seaf) { fprintf (stderr, "Failed to create seafile session.\n"); exit (1); } seaf->ccnetrpc_client = ccnetrpc_client; seaf->appletrpc_client = appletrpc_client; if (seafile_log_init (logfile, ccnet_debug_level_str, seafile_debug_level_str) < 0) { fprintf (stderr, "Failed to init log.\n"); exit (1); } seaf_message ("starting seaf-daemon "PACKAGE_VERSION"\n"); g_free (seafile_dir); g_free (worktree_dir); g_free (logfile); set_signal_handlers (seaf); seafile_session_prepare (seaf); seafile_session_start (seaf); seafile_session_config_set_string (seaf, "wktree", seaf->worktree_dir); ccnet_main (client); return 0; }