int diff_merge (SeafCommit *merge, GList **results) { SeafCommit *parent1, *parent2; struct tree_desc t[3]; struct unpack_trees_options opts; struct index_state istate; g_assert (*results == NULL); g_assert (merge->parent_id != NULL && merge->second_parent_id != NULL); parent1 = seaf_commit_manager_get_commit (seaf->commit_mgr, merge->parent_id); if (!parent1) { seaf_warning ("failed to find commit %s.\n", merge->parent_id); return -1; } parent2 = seaf_commit_manager_get_commit (seaf->commit_mgr, merge->second_parent_id); if (!parent2) { seaf_warning ("failed to find commit %s.\n", merge->second_parent_id); seaf_commit_unref (parent1); return -1; } fill_tree_descriptor(&t[0], merge->root_id); fill_tree_descriptor(&t[1], parent1->root_id); fill_tree_descriptor(&t[2], parent2->root_id); seaf_commit_unref (parent1); seaf_commit_unref (parent2); /* Empty index */ memset(&istate, 0, sizeof(istate)); memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; opts.index_only = 1; opts.merge = 1; opts.fn = threeway_diff; opts.unpack_data = results; opts.src_index = &istate; opts.dst_index = NULL; if (unpack_trees(3, t, &opts) < 0) { seaf_warning ("failed to unpack trees.\n"); return -1; } if (*results != NULL) diff_resolve_renames (results); tree_desc_free (&t[0]); tree_desc_free (&t[1]); tree_desc_free (&t[2]); return 0; }
static void fill_in_repo_info (GList *shared_repos) { SeafileSharedRepo *srepo; GList *ptr; SeafRepo *repo = NULL; SeafCommit *commit = NULL; for (ptr = shared_repos; ptr; ptr = ptr->next) { srepo = ptr->data; repo = seaf_repo_manager_get_repo (seaf->repo_mgr, seafile_shared_repo_get_repo_id(srepo)); if (!repo) continue; commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->head->commit_id); if (!commit) { seaf_repo_unref (repo); continue; } g_object_set (srepo, "repo_name", repo->name, "repo_desc", repo->desc, "encrypted", repo->encrypted, "last_modified", commit->ctime, NULL); seaf_repo_unref (repo); seaf_commit_unref (commit); } }
static void * merge_job (void *data) { MergeAux *aux = data; CloneTask *task = aux->task; SeafRepo *repo = aux->repo; SeafBranch *local = NULL; SeafCommit *head = NULL; /* If we haven't indexed files in the worktree, index them now. */ if (task->root_id[0] == 0) { if (seaf_repo_index_worktree_files (task->repo_id, task->worktree, task->passwd, task->root_id) < 0) return aux; } local = seaf_branch_manager_get_branch (seaf->branch_mgr, repo->id, "local"); if (!local) { aux->success = FALSE; goto out; } head = seaf_commit_manager_get_commit (seaf->commit_mgr, local->commit_id); if (!head) { aux->success = FALSE; goto out; } if (check_fast_forward (head, task->root_id)) { seaf_debug ("[clone mgr] Fast forward.\n"); if (fast_forward_checkout (repo, head, task) < 0) goto out; } else { if (real_merge (repo, head, task) < 0) goto out; /* Commit the result of merge. */ GError *error = NULL; /* XXX: the commit code assumes repo->head is set. */ repo->head = local; seaf_repo_index_commit (repo, "", &error); if (error) { seaf_warning ("Failed to commit after merge.\n"); goto out; } repo->head = NULL; } /* Set repo head to mark checkout done. */ seaf_repo_set_head (repo, local); aux->success = TRUE; out: seaf_branch_unref (local); seaf_commit_unref (head); return aux; }
static int test_get_fs_size() { gint64 size; const char *commit_id; SeafCommit *commit; SeafRepo *repo; printf ("\n=== test get fs size\n"); repo = create_repo ("get_fs_size"); commit_id = first_commit (repo); commit = seaf_commit_manager_get_commit (seaf->commit_mgr, commit_id); if (!commit) { fprintf (stderr, "Failed to get commit\n"); return -1; } size = seaf_fs_manager_get_fs_size (seaf->fs_mgr, commit->root_id); printf ("size is %"G_GINT64_FORMAT"\n", size); seaf_commit_unref (commit); printf ("\n=== get fs size succeeded.\n"); return 0; }
static int get_file_modifier_mtime_v1 (const char *repo_id, const char *store_id, int version, const char *head, const char *path, char **modifier, gint64 *mtime) { SeafCommit *commit = NULL; SeafDir *dir = NULL; SeafDirent *dent = NULL; int ret = 0; commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id, version, head); if (!commit) { seaf_warning ("Failed to get commit %s.\n", head); return -1; } char *parent = g_path_get_dirname (path); if (strcmp(parent, ".") == 0) { g_free (parent); parent = g_strdup(""); } char *filename = g_path_get_basename (path); dir = seaf_fs_manager_get_seafdir_by_path (seaf->fs_mgr, store_id, version, commit->root_id, parent, NULL); if (!dir) { seaf_warning ("dir %s doesn't exist in repo %s.\n", parent, repo_id); ret = -1; goto out; } GList *p; for (p = dir->entries; p; p = p->next) { SeafDirent *d = p->data; if (strcmp (d->name, filename) == 0) { dent = d; break; } } if (!dent) { goto out; } *modifier = g_strdup(dent->modifier); *mtime = dent->mtime; out: g_free (parent); g_free (filename); seaf_commit_unref (commit); seaf_dir_free (dir); return ret; }
/** * Get the user who last changed a file. * @head: head commit to start the search. * @path: path of the file. */ char * get_last_changer_of_file (const char *head, const char *path) { char commit_id[41]; SeafCommit *commit = NULL; char *file_id = NULL; int changed; char *ret = NULL; GError *error = NULL; memcpy (commit_id, head, 41); while (1) { commit = seaf_commit_manager_get_commit (seaf->commit_mgr, commit_id); if (!commit) break; /* We hit the initial commit. */ if (!commit->parent_id) break; file_id = seaf_fs_manager_path_to_obj_id (seaf->fs_mgr, commit->root_id, path, NULL, &error); if (error) { g_clear_error (&error); break; } /* We expect commit to have this file. */ if (!file_id) break; changed = diff_parents_with_path (commit, path, file_id, commit_id, &error); if (error) { g_clear_error (&error); break; } if (changed) { ret = g_strdup (commit->creator_name); break; } else { /* If this commit doesn't change the file, commit_id will be set * to the parent commit to traverse. */ g_free (file_id); seaf_commit_unref (commit); } } g_free (file_id); if (commit) seaf_commit_unref (commit); return ret; }
/* * Check whether the current head of @repo is consistent (including fs and block), * if not, find and reset its head to the last consistent commit. * Note that this procedure will not work with a corrupted commit object. */ static void check_and_reset_consistent_state (SeafRepo *repo) { FsckRes res; SeafCommit *rep_commit; SeafCommit *new_commit; seaf_message ("Checking file system integrity of repo %s(%.8s)...\n", repo->name, repo->id); memset (&res, 0, sizeof(res)); res.repo = repo; res.existing_blocks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id, check_fs_integrity, &res, TRUE); g_hash_table_destroy (res.existing_blocks); if (!res.consistent_head) { recover_corrupted_repo_head (repo->id); return; } /* If the current head is not consistent, reset it. */ if (strcmp (res.consistent_head, repo->head->commit_id) != 0) { rep_commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, res.consistent_head); if (rep_commit) { new_commit = cre_commit_from_parent (repo->id, rep_commit); if (new_commit == NULL) { seaf_warning ("Failed to update branch head.\n"); } else { seaf_message ("Resetting head of repo %.8s to commit %.8s.\n", repo->id, new_commit->commit_id); 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 branch head.\n"); } else { seaf_commit_manager_add_commit (seaf->commit_mgr, new_commit); } seaf_commit_unref (new_commit); } seaf_commit_unref (rep_commit); } else { seaf_warning ("Failed to update branch head.\n"); } } g_free (res.consistent_head); }
void wt_status_collect_changes_index (struct index_state *index, GList **results, SeafRepo *repo) { SeafFSManager *fs_mgr; SeafCommit *head; int pos = 0; DiffEntry *de; fs_mgr = repo->manager->seaf->fs_mgr; head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!head) { seaf_warning ("Failed to get commit %s.\n", repo->head->commit_id); return; } mark_all_ce_unused (index); /* if repo is initial, we don't need to check index changes */ if (strncmp(EMPTY_SHA1, head->root_id, 40) != 0) { SeafDir *root; /* call diff_index to get status */ root = seaf_fs_manager_get_seafdir (fs_mgr, repo->id, repo->version, head->root_id); if (!root) { seaf_warning ("Failed to get root %s.\n", head->root_id); seaf_commit_unref (head); return; } if (diff_index(repo->id, repo->version, index, root, results) < 0) g_warning("diff index failed\n"); seaf_dir_free (root); seaf_commit_unref (head); return; } seaf_commit_unref (head); while (1) { struct cache_entry *ce = next_cache_entry(index, &pos); if (!ce || ce_stage(ce)) break; ce->ce_flags |= CE_UNPACKED; de = diff_entry_new (DIFF_TYPE_INDEX, DIFF_STATUS_ADDED, ce->sha1, ce->name); *results = g_list_prepend (*results, de); } }
void seaf_repo_manager_cleanup_virtual_repos (SeafRepoManager *mgr, const char *origin_repo_id) { SeafRepo *repo = NULL; SeafCommit *head = NULL; GList *vinfo_list = NULL, *ptr; SeafVirtRepo *vinfo; SeafDir *dir; GError *error = NULL; repo = seaf_repo_manager_get_repo (mgr, origin_repo_id); if (!repo) { seaf_warning ("Failed to get repo %.10s.\n", origin_repo_id); goto out; } head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!head) { seaf_warning ("Failed to get commit %s:%.8s.\n", repo->id, repo->head->commit_id); goto out; } vinfo_list = seaf_repo_manager_get_virtual_info_by_origin (mgr, origin_repo_id); for (ptr = vinfo_list; ptr; ptr = ptr->next) { vinfo = ptr->data; dir = seaf_fs_manager_get_seafdir_by_path (seaf->fs_mgr, repo->store_id, repo->version, head->root_id, vinfo->path, &error); if (error) { if (error->code == SEAF_ERR_PATH_NO_EXIST) { handle_missing_virtual_repo (mgr, repo, head, vinfo); } g_clear_error (&error); } else seaf_dir_free (dir); seaf_virtual_repo_info_free (vinfo); } out: seaf_repo_unref (repo); seaf_commit_unref (head); g_list_free (vinfo_list); }
static int readdir_repo(SeafileSession *seaf, const char *user, const char *repo_id, const char *repo_path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *info) { SeafRepo *repo = NULL; SeafBranch *branch; SeafCommit *commit = NULL; SeafDir *dir = NULL; GList *l; int ret = 0; repo = seaf_repo_manager_get_repo(seaf->repo_mgr, repo_id); if (!repo) { seaf_warning ("Failed to get repo %s.\n", repo_id); ret = -ENOENT; goto out; } branch = repo->head; commit = seaf_commit_manager_get_commit(seaf->commit_mgr, repo->id, repo->version, branch->commit_id); if (!commit) { seaf_warning ("Failed to get commit %.8s.\n", branch->commit_id); ret = -ENOENT; goto out; } dir = seaf_fs_manager_get_seafdir_by_path(seaf->fs_mgr, repo->store_id, repo->version, commit->root_id, repo_path, NULL); if (!dir) { seaf_warning ("Path %s doesn't exist in repo %s.\n", repo_path, repo_id); ret = -ENOENT; goto out; } for (l = dir->entries; l; l = l->next) { SeafDirent *seaf_dent = (SeafDirent *) l->data; /* FIXME: maybe we need to return stbuf */ filler(buf, seaf_dent->name, NULL, 0); } out: seaf_repo_unref (repo); seaf_commit_unref (commit); seaf_dir_free (dir); return ret; }
VCCompareResult vc_compare_commits (const char *repo_id, int version, const char *c1, const char *c2) { SeafCommit *commit1, *commit2, *ca; VCCompareResult ret; /* Treat the same as up-to-date. */ if (strcmp (c1, c2) == 0) return VC_UP_TO_DATE; commit1 = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id, version, c1); if (!commit1) return VC_INDEPENDENT; commit2 = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id, version, c2); if (!commit2) { seaf_commit_unref (commit1); return VC_INDEPENDENT; } ca = get_merge_base (commit1, commit2); if (!ca) ret = VC_INDEPENDENT; else if (strcmp(ca->commit_id, commit1->commit_id) == 0) ret = VC_UP_TO_DATE; else if (strcmp(ca->commit_id, commit2->commit_id) == 0) ret = VC_FAST_FORWARD; else ret = VC_INDEPENDENT; if (ca) seaf_commit_unref (ca); seaf_commit_unref (commit1); seaf_commit_unref (commit2); return ret; }
/* * check and recover repo, for corrupted 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); if (!rep_commit) { seaf_warning ("Failed to load commit %s of repo %s\n", repo->head->commit_id, repo->id); return; } 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) { seaf_commit_unref (rep_commit); return; } if (repair) { if (strcmp (root_id, rep_commit->root_id) != 0) { // some fs objects corrupted 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 corrupted, 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 gboolean collect_repos (SeafDBRow *row, void *data) { GList **p_repos = data; const char *repo_id; const char *email; const char *permission; SeafRepo *repo = NULL; SeafCommit *commit = NULL; SeafileSharedRepo *srepo; repo_id = seaf_db_row_get_column_text (row, 0); repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) goto out; email = seaf_db_row_get_column_text (row, 1); permission = seaf_db_row_get_column_text (row, 2); commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->head->commit_id); if (!commit) goto out; srepo = g_object_new (SEAFILE_TYPE_SHARED_REPO, "share_type", "personal", "repo_id", repo_id, "repo_name", repo->name, "repo_desc", repo->desc, "encrypted", repo->encrypted, "user", email, "permission", permission, "last_modified", commit->ctime, NULL); *p_repos = g_list_prepend (*p_repos, srepo); out: seaf_repo_unref (repo); seaf_commit_unref (commit); return TRUE; }
static gboolean collect_repos (SeafDBRow *row, void *data) { GList **p_repos = data; const char *repo_id; const char *vrepo_id; const char *email; const char *permission; const char *commit_id; SeafileSharedRepo *srepo; SeafCommit *commit; repo_id = seaf_db_row_get_column_text (row, 0); vrepo_id = seaf_db_row_get_column_text (row, 1); email = seaf_db_row_get_column_text (row, 2); permission = seaf_db_row_get_column_text (row, 3); commit_id = seaf_db_row_get_column_text (row, 4); commit = seaf_commit_manager_get_commit (seaf->commit_mgr, commit_id); if (!commit) return TRUE; char *email_l = g_ascii_strdown (email, -1); srepo = g_object_new (SEAFILE_TYPE_SHARED_REPO, "share_type", "personal", "repo_id", repo_id, "user", email_l, "permission", permission, "repo_name", commit->repo_name, "repo_desc", commit->repo_desc, "encrypted", commit->encrypted, "last_modified", commit->ctime, "is_virtual", (vrepo_id != NULL), NULL); g_free (email_l); seaf_commit_unref (commit); *p_repos = g_list_prepend (*p_repos, srepo); return TRUE; }
static void load_repo_commit (SeafRepoManager *manager, SeafRepo *repo, SeafBranch *branch) { SeafCommit *commit; commit = seaf_commit_manager_get_commit (manager->seaf->commit_mgr, branch->commit_id); if (!commit) { g_warning ("Commit %s is missing\n", branch->commit_id); repo->is_corrupted = TRUE; return; } set_head_common (repo, branch); seaf_repo_from_commit (repo, commit); seaf_commit_unref (commit); }
/* * Get the list of new blocks that would be checked out after * we merge with a branch headed by @remote. * * This function should be called before downloading any block * if the repo is set to not preserving history. In this case, * we don't want to download any block that will not be checked * out to the worktree (i.e. data from any historical commits). * * Return 0 if successfully calculate the block list, -1 otherwise. * If there is no new block to download, *@bl will be set to NULL; * otherwise it's set to the block list. */ int merge_get_new_block_list (SeafRepo *repo, SeafCommit *remote, BlockList **bl) { SeafCommit *common = NULL; SeafCommit *head; int ret = 0; head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->head->commit_id); if (!head) { g_warning ("current branch corrupted.\n"); return -1; } common = get_merge_base (head, remote); if (!common) { g_warning ("Cannot find common ancestor\n"); ret = -1; goto free_head; } if (strcmp(common->commit_id, remote->commit_id) == 0) { /* We are already up to date. No new block. */ *bl = NULL; } else if (strcmp(common->commit_id, head->commit_id) == 0) { /* Fast forward. */ ret = get_new_blocks_ff (repo, head, remote, bl); } else { /* Not up-to-date and ff, we need a real merge. */ ret = get_new_blocks_merge (repo, head, remote, common, bl); } seaf_commit_unref (common); free_head: seaf_commit_unref (head); return ret; }
static int test_commit() { SeafRepo *repo; SeafCommit *commit; const char *commit_id; printf ("\n=== test commit\n"); repo = create_repo ("test-commit"); commit_id = first_commit (repo); printf ("*** print index\n"); if (print_index (repo) < 0) return -1; commit = seaf_commit_manager_get_commit (seaf->commit_mgr, commit_id); if (!commit) { fprintf (stderr, "Failed to get commit\n"); return -1; } if (traverse_dir (commit->root_id) < 0) return -1; /* status */ char *status = seaf_repo_status (repo); printf ("Status\n%s", status); g_assert (strcmp(status, "") == 0); g_free (status); seaf_commit_unref (commit); printf ("\n=== test commit succeeded.\n"); return 0; }
static void receive_commit (CcnetProcessor *processor, char *content, int clen) { ObjectPack *pack = (ObjectPack *)content; TransferTask *task = ((SeafileGetcommitV2Proc *)processor)->tx_task; SeafCommit *commit; if (clen < sizeof(ObjectPack)) { g_warning ("[getcommit] invalid object id.\n"); goto bad; } seaf_debug ("[getcommit] recv commit object %.8s\n", pack->id); if (save_commit (pack, clen) < 0) { goto bad; } commit = seaf_commit_manager_get_commit (seaf->commit_mgr, pack->id); if (!commit) goto bad; if (strcmp (commit->root_id, EMPTY_SHA1) != 0) object_list_insert (task->fs_roots, commit->root_id); seaf_commit_unref (commit); return; bad: g_warning ("[getcommit] Bad commit object received.\n"); transfer_task_set_error (((SeafileGetcommitV2Proc *)processor)->tx_task, TASK_ERR_DOWNLOAD_COMMIT); ccnet_processor_send_update (processor, SC_BAD_OBJECT, SS_BAD_OBJECT, NULL, 0); ccnet_processor_done (processor, FALSE); }
static void* compute_repo_size (void *vjob) { RepoSizeJob *job = vjob; SizeScheduler *sched = job->sched; SeafRepo *repo = NULL; SeafCommit *head = NULL; char *cached_head_id = NULL; gint64 size = 0; retry: repo = seaf_repo_manager_get_repo (sched->seaf->repo_mgr, job->repo_id); if (!repo) { seaf_warning ("[scheduler] failed to get repo %s.\n", job->repo_id); return vjob; } cached_head_id = get_cached_head_id (sched->seaf->db, job->repo_id); if (g_strcmp0 (cached_head_id, repo->head->commit_id) == 0) goto out; head = seaf_commit_manager_get_commit (sched->seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!head) { seaf_warning ("[scheduler] failed to get head commit %s.\n", repo->head->commit_id); goto out; } size = seaf_fs_manager_get_fs_size (sched->seaf->fs_mgr, repo->store_id, repo->version, head->root_id); if (size < 0) { seaf_warning ("[scheduler] Failed to compute size of repo %.8s.\n", repo->id); goto out; } int ret = set_repo_size (sched->seaf->db, job->repo_id, cached_head_id, repo->head->commit_id, size); if (ret == SET_SIZE_ERROR) seaf_warning ("[scheduler] failed to store repo size %s.\n", job->repo_id); else if (ret == SET_SIZE_CONFLICT) { size = 0; seaf_repo_unref (repo); seaf_commit_unref (head); g_free (cached_head_id); repo = NULL; head = NULL; cached_head_id = NULL; goto retry; } out: seaf_repo_unref (repo); seaf_commit_unref (head); g_free (cached_head_id); return vjob; }
static void *merge_virtual_repo (void *vtask) { MergeTask *task = vtask; SeafRepoManager *mgr = seaf->repo_mgr; char *repo_id = task->repo_id; SeafVirtRepo *vinfo; SeafRepo *repo = NULL, *orig_repo = NULL; SeafCommit *head = NULL, *orig_head = NULL, *base = NULL; char *root = NULL, *orig_root = NULL, *base_root = NULL; char new_base_commit[41] = {0}; int ret = 0; /* repos */ repo = seaf_repo_manager_get_repo (mgr, repo_id); if (!repo) { seaf_warning ("Failed to get virt repo %.10s.\n", repo_id); ret = -1; goto out; } vinfo = repo->virtual_info; orig_repo = seaf_repo_manager_get_repo (mgr, vinfo->origin_repo_id); if (!orig_repo) { seaf_warning ("Failed to get orig repo %.10s.\n", vinfo->origin_repo_id); ret = -1; goto out; } /* commits */ head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!head) { seaf_warning ("Failed to get commit %s:%.8s.\n", repo->id, repo->head->commit_id); ret = -1; goto out; } orig_head = seaf_commit_manager_get_commit (seaf->commit_mgr, orig_repo->id, orig_repo->version, orig_repo->head->commit_id); if (!orig_head) { seaf_warning ("Failed to get commit %s:%.8s.\n", orig_repo->id, orig_repo->head->commit_id); ret = -1; goto out; } base = seaf_commit_manager_get_commit (seaf->commit_mgr, orig_repo->id, orig_repo->version, vinfo->base_commit); if (!base) { seaf_warning ("Failed to get commit %s:%.8s.\n", orig_repo->id, vinfo->base_commit); ret = -1; goto out; } /* fs roots */ root = head->root_id; base_root = seaf_fs_manager_get_seafdir_id_by_path (seaf->fs_mgr, orig_repo->store_id, orig_repo->version, base->root_id, vinfo->path, NULL); if (!base_root) { seaf_warning ("Cannot find seafdir for repo %.10s path %s.\n", vinfo->origin_repo_id, vinfo->path); ret = -1; goto out; } orig_root = seaf_fs_manager_get_seafdir_id_by_path (seaf->fs_mgr, orig_repo->store_id, orig_repo->version, orig_head->root_id, vinfo->path, NULL); if (!orig_root) { seaf_warning ("Cannot find seafdir for repo %.10s path %s.\n", vinfo->origin_repo_id, vinfo->path); ret = -1; goto out; } if (strcmp (root, orig_root) == 0) { /* Nothing to merge. */ seaf_debug ("Nothing to merge.\n"); } else if (strcmp (base_root, root) == 0) { /* Origin changed, virtual repo not changed. */ seaf_debug ("Origin changed, virtual repo not changed.\n"); ret = seaf_repo_manager_update_dir (mgr, repo_id, "/", orig_root, orig_head->creator_name, head->commit_id, NULL, NULL); if (ret < 0) { seaf_warning ("Failed to update root of virtual repo %.10s.\n", repo_id); goto out; } set_virtual_repo_base_commit_path (repo->id, orig_repo->head->commit_id, vinfo->path); } else if (strcmp (base_root, orig_root) == 0) { /* Origin not changed, virutal repo changed. */ seaf_debug ("Origin not changed, virutal repo changed.\n"); ret = seaf_repo_manager_update_dir (mgr, vinfo->origin_repo_id, vinfo->path, root, head->creator_name, orig_head->commit_id, new_base_commit, NULL); if (ret < 0) { seaf_warning ("Failed to update origin repo %.10s path %s.\n", vinfo->origin_repo_id, vinfo->path); goto out; } set_virtual_repo_base_commit_path (repo->id, new_base_commit, vinfo->path); /* Since origin repo is updated, we have to merge it with other * virtual repos if necessary. But we don't need to merge with * the current virtual repo again. */ seaf_repo_manager_cleanup_virtual_repos (mgr, vinfo->origin_repo_id); seaf_repo_manager_merge_virtual_repo (mgr, vinfo->origin_repo_id, repo_id); } else { /* Both origin and virtual repo are changed. */ seaf_debug ("Both origin and virtual repo are changed.\n"); MergeOptions opt; const char *roots[3]; memset (&opt, 0, sizeof(opt)); opt.n_ways = 3; memcpy (opt.remote_repo_id, repo_id, 36); memcpy (opt.remote_head, head->commit_id, 40); opt.do_merge = TRUE; roots[0] = base_root; /* base */ roots[1] = orig_root; /* head */ roots[2] = root; /* remote */ /* Merge virtual into origin */ if (seaf_merge_trees (orig_repo->store_id, orig_repo->version, 3, roots, &opt) < 0) { seaf_warning ("Failed to merge virtual repo %.10s.\n", repo_id); ret = -1; goto out; } seaf_debug ("Number of dirs visted in merge: %d.\n", opt.visit_dirs); /* Update virtual repo root. */ ret = seaf_repo_manager_update_dir (mgr, repo_id, "/", opt.merged_tree_root, orig_head->creator_name, head->commit_id, NULL, NULL); if (ret < 0) { seaf_warning ("Failed to update root of virtual repo %.10s.\n", repo_id); goto out; } /* Update origin repo path. */ ret = seaf_repo_manager_update_dir (mgr, vinfo->origin_repo_id, vinfo->path, opt.merged_tree_root, head->creator_name, orig_head->commit_id, new_base_commit, NULL); if (ret < 0) { seaf_warning ("Failed to update origin repo %.10s path %s.\n", vinfo->origin_repo_id, vinfo->path); goto out; } set_virtual_repo_base_commit_path (repo->id, new_base_commit, vinfo->path); seaf_repo_manager_cleanup_virtual_repos (mgr, vinfo->origin_repo_id); seaf_repo_manager_merge_virtual_repo (mgr, vinfo->origin_repo_id, repo_id); } out: seaf_repo_unref (repo); seaf_repo_unref (orig_repo); seaf_commit_unref (head); seaf_commit_unref (orig_head); seaf_commit_unref (base); g_free (base_root); g_free (orig_root); return vtask; }
/* * If the missing virtual repo is renamed, update database entry; * otherwise delete the virtual repo. */ static void handle_missing_virtual_repo (SeafRepoManager *mgr, SeafRepo *repo, SeafCommit *head, SeafVirtRepo *vinfo) { SeafCommit *parent = NULL; char *old_dir_id = NULL; GList *diff_res = NULL, *ptr; DiffEntry *de; parent = seaf_commit_manager_get_commit (seaf->commit_mgr, head->repo_id, head->version, head->parent_id); if (!parent) { seaf_warning ("Failed to find commit %s:%s.\n", head->repo_id, head->parent_id); return; } int rc = diff_commits (parent, head, &diff_res, TRUE); if (rc < 0) { seaf_warning ("Failed to diff commit %s to %s.\n", parent->commit_id, head->commit_id); seaf_commit_unref (parent); return; } char *path = vinfo->path, *sub_path, *p, *par_path; gboolean is_renamed = FALSE; p = &path[strlen(path)]; par_path = g_strdup(path); sub_path = NULL; while (1) { GError *error = NULL; old_dir_id = seaf_fs_manager_get_seafdir_id_by_path (seaf->fs_mgr, repo->store_id, repo->version, parent->root_id, par_path, &error); if (!old_dir_id) { if (error && error->code == SEAF_ERR_PATH_NO_EXIST) { seaf_warning ("Failed to find %s under commit %s in repo %s.\n", par_path, parent->commit_id, repo->store_id); seaf_debug ("Delete virtual repo %.10s.\n", vinfo->repo_id); seaf_repo_manager_del_virtual_repo (mgr, vinfo->repo_id); g_clear_error (&error); } goto out; } char de_id[41]; char *new_path; for (ptr = diff_res; ptr; ptr = ptr->next) { de = ptr->data; if (de->status == DIFF_STATUS_DIR_RENAMED) { rawdata_to_hex (de->sha1, de_id, 20); if (strcmp (de_id, old_dir_id) == 0) { if (sub_path != NULL) new_path = g_strconcat ("/", de->new_name, "/", sub_path, NULL); else new_path = g_strconcat ("/", de->new_name, NULL); seaf_debug ("Updating path of virtual repo %s to %s.\n", vinfo->repo_id, new_path); set_virtual_repo_base_commit_path (vinfo->repo_id, head->commit_id, new_path); is_renamed = TRUE; break; } } } g_free (old_dir_id); if (is_renamed) break; while (--p != path && *p != '/'); if (p == path) break; g_free (par_path); g_free (sub_path); par_path = g_strndup (path, p - path); sub_path = g_strdup (p + 1); } if (!is_renamed) { seaf_debug ("Delete virtual repo %.10s.\n", vinfo->repo_id); seaf_repo_manager_del_virtual_repo (mgr, vinfo->repo_id); } out: g_free (par_path); g_free (sub_path); for (ptr = diff_res; ptr; ptr = ptr->next) diff_entry_free ((DiffEntry *)ptr->data); g_list_free (diff_res); seaf_commit_unref (parent); }
char * seaf_repo_manager_create_virtual_repo (SeafRepoManager *mgr, const char *origin_repo_id, const char *path, const char *repo_name, const char *repo_desc, const char *owner, const char *passwd, GError **error) { SeafRepo *origin_repo = NULL; SeafCommit *origin_head = NULL; char *repo_id = NULL; char *dir_id = NULL; char *orig_owner = NULL; if (seaf_repo_manager_is_virtual_repo (mgr, origin_repo_id)) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Cannot create sub-library from a sub-library"); return NULL; } repo_id = get_existing_virtual_repo (mgr, origin_repo_id, path); if (repo_id) { return repo_id; } origin_repo = seaf_repo_manager_get_repo (mgr, origin_repo_id); if (!origin_repo) { seaf_warning ("Failed to get origin repo %.10s\n", origin_repo_id); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Origin library not exists"); return NULL; } if (origin_repo->encrypted) { if (origin_repo->enc_version < 2) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Library encryption version must be higher than 2"); seaf_repo_unref (origin_repo); return NULL; } if (!passwd || passwd[0] == 0) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Password is not set"); seaf_repo_unref (origin_repo); return NULL; } if (seafile_verify_repo_passwd (origin_repo_id, passwd, origin_repo->magic, origin_repo->enc_version) < 0) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Incorrect password"); seaf_repo_unref (origin_repo); return NULL; } } origin_head = seaf_commit_manager_get_commit (seaf->commit_mgr, origin_repo->id, origin_repo->version, origin_repo->head->commit_id); if (!origin_head) { seaf_warning ("Failed to get head commit %.8s of repo %s.\n", origin_repo->head->commit_id, origin_repo->id); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Bad origin repo head"); goto error; } dir_id = seaf_fs_manager_get_seafdir_id_by_path (seaf->fs_mgr, origin_repo->store_id, origin_repo->version, origin_head->root_id, path, NULL); if (!dir_id) { seaf_warning ("Path %s doesn't exist or is not a dir in repo %.10s.\n", path, origin_repo_id); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Bad path"); goto error; } repo_id = gen_uuid(); /* Save virtual repo info before actually create the repo. */ if (save_virtual_repo_info (mgr, repo_id, origin_repo_id, path, origin_head->commit_id) < 0) { seaf_warning ("Failed to save virtual repo info for %.10s:%s", origin_repo_id, path); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Internal error"); goto error; } orig_owner = seaf_repo_manager_get_repo_owner (mgr, origin_repo_id); if (do_create_virtual_repo (mgr, origin_repo, repo_id, repo_name, repo_desc, dir_id, orig_owner, passwd, error) < 0) goto error; if (seaf_repo_manager_set_repo_owner (mgr, repo_id, orig_owner) < 0) { seaf_warning ("Failed to set repo owner for %.10s.\n", repo_id); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Failed to set repo owner."); goto error; } /* The size of virtual repo is non-zero at the beginning. */ update_repo_size (repo_id); seaf_repo_unref (origin_repo); seaf_commit_unref (origin_head); g_free (dir_id); g_free (orig_owner); return repo_id; error: seaf_repo_unref (origin_repo); seaf_commit_unref (origin_head); g_free (repo_id); g_free (dir_id); g_free (orig_owner); return NULL; }
static void * update_repo (void *vprocessor) { CcnetProcessor *processor = vprocessor; USE_PRIV; char *repo_id, *branch_name, *new_head; SeafRepo *repo = NULL; SeafBranch *branch = NULL; SeafCommit *commit = NULL; char old_commit_id[41]; repo_id = priv->repo_id; branch_name = priv->branch_name; new_head = priv->new_head; /* Since this is the last step of upload procedure, commit should exist. */ commit = seaf_commit_manager_get_commit (seaf->commit_mgr, new_head); if (!commit) { priv->rsp_code = g_strdup (SC_BAD_COMMIT); priv->rsp_msg = g_strdup (SS_BAD_COMMIT); goto out; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { /* repo is deleted on server */ priv->rsp_code = g_strdup (SC_BAD_REPO); priv->rsp_msg = g_strdup (SC_BAD_REPO); goto out; } if (seaf_quota_manager_check_quota (seaf->quota_mgr, repo_id) < 0) { priv->rsp_code = g_strdup(SC_QUOTA_FULL); priv->rsp_msg = g_strdup(SS_QUOTA_FULL); goto out; } branch = seaf_branch_manager_get_branch (seaf->branch_mgr, repo_id, branch_name); if (!branch) { priv->rsp_code = g_strdup (SC_BAD_BRANCH); priv->rsp_msg = g_strdup (SS_BAD_BRANCH); goto out; } /* If branch exists, check fast forward. */ if (strcmp (new_head, branch->commit_id) != 0 && !is_fast_forward (new_head, branch->commit_id)) { g_warning ("Upload is not fast forward. Refusing.\n"); seaf_repo_unref (repo); seaf_commit_unref (commit); seaf_branch_unref (branch); priv->rsp_code = g_strdup (SC_NOT_FF); priv->rsp_msg = g_strdup (SS_NOT_FF); return vprocessor; } /* Update branch. In case of concurrent update, we must ensure atomicity. */ memcpy (old_commit_id, branch->commit_id, 41); seaf_branch_set_commit (branch, commit->commit_id); if (seaf_branch_manager_test_and_update_branch (seaf->branch_mgr, branch, old_commit_id) < 0) { g_warning ("Upload is not fast forward, concurrent update.\n"); priv->rsp_code = g_strdup (SC_NOT_FF); priv->rsp_msg = g_strdup (SS_NOT_FF); goto out; } out: if (repo) seaf_repo_unref (repo); if (commit) seaf_commit_unref (commit); if (branch) seaf_branch_unref (branch); if (!priv->rsp_code) { priv->rsp_code = g_strdup (SC_OK); priv->rsp_msg = g_strdup (SS_OK); } return vprocessor; }
static int get_file_modifier_mtime_v0 (const char *repo_id, const char *store_id, int version, const char *head, const char *path, char **modifier, gint64 *mtime) { char commit_id[41]; SeafCommit *commit = NULL; char *file_id = NULL; int changed; int ret = 0; GError *error = NULL; *modifier = NULL; *mtime = 0; memcpy (commit_id, head, 41); while (1) { commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id, version, commit_id); if (!commit) { ret = -1; break; } /* We hit the initial commit. */ if (!commit->parent_id) break; file_id = seaf_fs_manager_path_to_obj_id (seaf->fs_mgr, store_id, version, commit->root_id, path, NULL, &error); if (error) { g_clear_error (&error); ret = -1; break; } /* We expect commit to have this file. */ if (!file_id) { ret = -1; break; } changed = diff_parents_with_path (commit, repo_id, store_id, version, path, file_id, commit_id, &error); if (error) { g_clear_error (&error); ret = -1; break; } if (changed) { *modifier = g_strdup (commit->creator_name); *mtime = commit->ctime; break; } else { /* If this commit doesn't change the file, commit_id will be set * to the parent commit to traverse. */ g_free (file_id); seaf_commit_unref (commit); } } g_free (file_id); if (commit) seaf_commit_unref (commit); return ret; }
/** * Diff a specific file with parent(s). * If @commit is a merge, both parents will be compared. * @commit must have this file and it's id is given in @file_id. * * Returns 0 if there is no difference; 1 otherwise. * If returns 0, @parent will point to the next commit to traverse. * If I/O error occurs, @error will be set. */ static int diff_parents_with_path (SeafCommit *commit, const char *repo_id, const char *store_id, int version, const char *path, const char *file_id, char *parent, GError **error) { SeafCommit *p1 = NULL, *p2 = NULL; char *file_id_p1 = NULL, *file_id_p2 = NULL; int ret = 0; p1 = seaf_commit_manager_get_commit (seaf->commit_mgr, commit->repo_id, commit->version, commit->parent_id); if (!p1) { g_warning ("Failed to find commit %s.\n", commit->parent_id); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, " "); return 0; } if (strcmp (p1->root_id, EMPTY_SHA1) == 0) { seaf_commit_unref (p1); return 1; } if (commit->second_parent_id) { p2 = seaf_commit_manager_get_commit (seaf->commit_mgr, commit->repo_id, commit->version, commit->second_parent_id); if (!p2) { g_warning ("Failed to find commit %s.\n", commit->second_parent_id); seaf_commit_unref (p1); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, " "); return 0; } } if (!p2) { file_id_p1 = seaf_fs_manager_path_to_obj_id (seaf->fs_mgr, store_id, version, p1->root_id, path, NULL, error); if (*error) goto out; if (!file_id_p1 || strcmp (file_id, file_id_p1) != 0) ret = 1; else memcpy (parent, p1->commit_id, 41); } else { file_id_p1 = seaf_fs_manager_path_to_obj_id (seaf->fs_mgr, store_id, version, p1->root_id, path, NULL, error); if (*error) goto out; file_id_p2 = seaf_fs_manager_path_to_obj_id (seaf->fs_mgr, store_id, version, p2->root_id, path, NULL, error); if (*error) goto out; if (file_id_p1 && file_id_p2) { if (strcmp(file_id, file_id_p1) != 0 && strcmp(file_id, file_id_p2) != 0) ret = 1; else if (strcmp(file_id, file_id_p1) == 0) memcpy (parent, p1->commit_id, 41); else memcpy (parent, p2->commit_id, 41); } else if (file_id_p1 && !file_id_p2) { if (strcmp(file_id, file_id_p1) != 0) ret = 1; else memcpy (parent, p1->commit_id, 41); } else if (!file_id_p1 && file_id_p2) { if (strcmp(file_id, file_id_p2) != 0) ret = 1; else memcpy (parent, p2->commit_id, 41); } else { ret = 1; } } out: g_free (file_id_p1); g_free (file_id_p2); if (p1) seaf_commit_unref (p1); if (p2) seaf_commit_unref (p2); return ret; }
static void* compute_repo_size (void *vjob) { RepoSizeJob *job = vjob; Scheduler *sched = job->sched; SeafRepo *repo = NULL; SeafCommit *head = NULL; char *cached_head_id = NULL; BlockList *bl; char *block_id; BlockMetadata *bmd; guint64 size = 0; repo = seaf_repo_manager_get_repo (sched->seaf->repo_mgr, job->repo_id); if (!repo) { g_warning ("[scheduler] failed to get repo %s.\n", job->repo_id); return vjob; } cached_head_id = get_cached_head_id (sched->seaf->db, job->repo_id); if (g_strcmp0 (cached_head_id, repo->head->commit_id) == 0) goto out; head = seaf_commit_manager_get_commit (sched->seaf->commit_mgr, repo->head->commit_id); if (!head) { g_warning ("[scheduler] failed to get head commit %s.\n", repo->head->commit_id); goto out; } /* Load block list first so that we don't need to count duplicate blocks. * We only calculate the size of the head commit. */ bl = block_list_new (); if (seaf_fs_manager_populate_blocklist (seaf->fs_mgr, head->root_id, bl) < 0) { block_list_free (bl); goto out; } int i; for (i = 0; i < bl->n_blocks; ++i) { block_id = g_ptr_array_index (bl->block_ids, i); bmd = seaf_block_manager_stat_block (sched->seaf->block_mgr, block_id); if (bmd) { size += bmd->size; g_free (bmd); } } block_list_free (bl); if (set_repo_size (sched->seaf->db, job->repo_id, repo->head->commit_id, size) < 0) g_warning ("[scheduler] failed to store repo size %s.\n", job->repo_id); out: seaf_repo_unref (repo); seaf_commit_unref (head); g_free (cached_head_id); return vjob; }
static int seaf_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *info) { int n_parts; char *user, *repo_id, *repo_path; SeafRepo *repo = NULL; SeafBranch *branch = NULL; SeafCommit *commit = NULL; Seafile *file = NULL; char *file_id = NULL; int ret = 0; /* Now we only support read-only mode */ if ((info->flags & 3) != O_RDONLY) return -EACCES; if (parse_fuse_path (path, &n_parts, &user, &repo_id, &repo_path) < 0) { seaf_warning ("Invalid input path %s.\n", path); return -ENOENT; } if (n_parts != 2 && n_parts != 3) { seaf_warning ("Invalid input path for open: %s.\n", path); ret = -EACCES; goto out; } repo = seaf_repo_manager_get_repo(seaf->repo_mgr, repo_id); if (!repo) { seaf_warning ("Failed to get repo %s.\n", repo_id); ret = -ENOENT; goto out; } branch = repo->head; commit = seaf_commit_manager_get_commit(seaf->commit_mgr, repo->id, repo->version, branch->commit_id); if (!commit) { seaf_warning ("Failed to get commit %s:%.8s.\n", repo->id, branch->commit_id); ret = -ENOENT; goto out; } file_id = seaf_fs_manager_get_seafile_id_by_path(seaf->fs_mgr, repo->store_id, repo->version, commit->root_id, repo_path, NULL); if (!file_id) { seaf_warning ("Path %s doesn't exist in repo %s.\n", repo_path, repo_id); ret = -ENOENT; goto out; } file = seaf_fs_manager_get_seafile(seaf->fs_mgr, repo->store_id, repo->version, file_id); if (!file) { ret = -ENOENT; goto out; } ret = read_file(seaf, repo->store_id, repo->version, file, buf, size, offset, info); seafile_unref (file); out: g_free (user); g_free (repo_id); g_free (repo_path); g_free (file_id); seaf_repo_unref (repo); seaf_commit_unref (commit); 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 void * update_repo (void *vprocessor) { CcnetProcessor *processor = vprocessor; USE_PRIV; char *repo_id, *new_head; SeafRepo *repo = NULL; SeafCommit *new_commit = NULL, *base = NULL; repo_id = priv->repo_id; new_head = priv->new_head; repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { /* repo is deleted on server */ priv->rsp_code = g_strdup (SC_BAD_REPO); priv->rsp_msg = g_strdup (SC_BAD_REPO); goto out; } /* Since this is the last step of upload procedure, commit should exist. */ new_commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, new_head); if (!new_commit) { seaf_warning ("Failed to get commit %s for repo %s.\n", new_head, repo->id); priv->rsp_code = g_strdup (SC_BAD_COMMIT); priv->rsp_msg = g_strdup (SS_BAD_COMMIT); goto out; } base = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, new_commit->parent_id); if (!base) { seaf_warning ("Failed to get commit %s for repo %s.\n", new_commit->parent_id, repo->id); priv->rsp_code = g_strdup (SC_BAD_COMMIT); priv->rsp_msg = g_strdup (SS_BAD_COMMIT); goto out; } if (seaf_quota_manager_check_quota (seaf->quota_mgr, repo_id) < 0) { priv->rsp_code = g_strdup(SC_QUOTA_FULL); priv->rsp_msg = g_strdup(SS_QUOTA_FULL); goto out; } if (fast_forward_or_merge (repo_id, base, new_commit) < 0) { priv->rsp_code = g_strdup(SC_SERVER_ERROR); priv->rsp_msg = g_strdup(SS_SERVER_ERROR); goto out; } seaf_repo_manager_cleanup_virtual_repos (seaf->repo_mgr, repo_id); seaf_repo_manager_merge_virtual_repo (seaf->repo_mgr, repo_id, NULL); out: seaf_repo_unref (repo); seaf_commit_unref (new_commit); seaf_commit_unref (base); if (!priv->rsp_code) { priv->rsp_code = g_strdup (SC_OK); priv->rsp_msg = g_strdup (SS_OK); } return vprocessor; }
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); } }