static int traverse_dir (const char *id) { SeafDir *dir; GList *p; SeafDirent *seaf_dent; dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr, id); if (!dir) { fprintf (stderr, "Failed to read dir %s\n", id); return -1; } for (p = dir->entries; p; p = p->next) { seaf_dent = (SeafDirent *)p->data; if (S_ISREG(seaf_dent->mode)) { printf ("check file %s\n", seaf_dent->name); return check_seafile (seaf_dent->id); } else if (S_ISDIR(seaf_dent->mode)) { printf ("check directory %s\n", seaf_dent->name); return traverse_dir (seaf_dent->id); } } seaf_dir_free (dir); return 0; }
static int delete_dir_recursive (const char *repo_id, int repo_version, const char *dir_path, SeafDir *dir, const char *worktree, struct index_state *istate) { GList *ptr; SeafDirent *dent; char *sub_path; SeafDir *sub_dir; for (ptr = dir->entries; ptr; ptr = ptr->next) { dent = ptr->data; sub_path = g_strconcat (dir_path, "/", dent->name, NULL); if (S_ISDIR(dent->mode)) { if (strcmp(dent->id, EMPTY_SHA1) == 0) { delete_path (worktree, sub_path, dent->mode, 0); } else { sub_dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr, repo_id, repo_version, dent->id); if (!sub_dir) { seaf_warning ("Failed to find dir %s in repo %.8s.\n", sub_path, repo_id); g_free (sub_path); return -1; } if (delete_dir_recursive (repo_id, repo_version, sub_path, sub_dir, worktree, istate) < 0) { g_free (sub_path); return -1; } seaf_dir_free (sub_dir); } } else if (S_ISREG(dent->mode)) { struct cache_entry *ce; ce = index_name_exists (istate, sub_path, strlen(sub_path), 0); if (ce) { delete_path (worktree, sub_path, dent->mode, ce->ce_mtime.sec); } } g_free (sub_path); } char *full_path = g_build_filename (worktree, dir_path, NULL); if (seaf_remove_empty_dir (full_path) < 0) { seaf_warning ("Failed to remove dir %s: %s.\n", full_path, strerror(errno)); return -1; } return 0; }
/* * Recursively check fs tree rooted at @dir_id. This function returns when * all non-existent or invalid objects have been put into data->fetch_objs. */ static void check_seafdir (CcnetProcessor *processor, const char *dir_id) { SeafileGetfsProc *proc = (SeafileGetfsProc *)processor; USE_PRIV; SeafDir *dir = NULL; GList *ptr; SeafDirent *dent; if (!seaf_fs_manager_object_exists(seaf->fs_mgr, dir_id)) { priv->fetch_objs = g_list_prepend (priv->fetch_objs, g_strdup(dir_id)); return; } dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr, dir_id); if (!dir) { /* corrupt dir object */ priv->fetch_objs = g_list_prepend (priv->fetch_objs, g_strdup(dir_id)); return; } for (ptr = dir->entries; ptr; ptr = ptr->next) { dent = ptr->data; /* Don't check objects that have been checked before. */ if (g_hash_table_lookup (priv->fs_objects, dent->id)) continue; g_hash_table_insert (priv->fs_objects, g_strdup(dent->id), (gpointer)1); if (!seaf_fs_manager_object_exists(seaf->fs_mgr, dent->id)) { priv->fetch_objs = g_list_prepend (priv->fetch_objs, g_strdup(dent->id)); continue; } if (S_ISDIR(dent->mode)) { check_seafdir (processor, dent->id); } else if (S_ISREG (dent->mode) && proc->tx_task->is_clone) { /* Only check seafile object integrity when clone. * This is for the purpose of recovery. * In ordinary sync, checking every file object's integrity would * take too much CPU time. */ gboolean ok; gboolean err = FALSE; ok = seaf_fs_manager_verify_seafile (seaf->fs_mgr, dent->id, TRUE, &err); if (!ok && !err) { seaf_warning ("File object %.8s is corrupt, recover from server.\n", dent->id); priv->fetch_objs = g_list_prepend (priv->fetch_objs, g_strdup(dent->id)); } } } seaf_dir_free (dir); }
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); } }
static int get_files_dirs_recursive(struct merge_options *o, SeafDir *tree, char *base, int baselen) { GList *p; char *path; int ret = 0; for (p = tree->entries; p; p = p->next) { SeafDirent *dent = (SeafDirent *)p->data; SeafDir *subdir; int new_baselen; int pathlen; switch (S_IFMT & dent->mode) { case S_IFREG: pathlen = baselen + dent->name_len + 1; path = malloc(pathlen); snprintf(path, pathlen, "%s%s", base, dent->name); g_hash_table_replace(o->current_file_set, path, path); break; case S_IFDIR: /* Ignore empty dirs. */ if (memcmp (dent->id, EMPTY_SHA1, 40) == 0) break; pathlen = baselen + dent->name_len + 1; path = malloc(pathlen); snprintf(path, pathlen, "%s%s", base, dent->name); g_hash_table_replace(o->current_directory_set, path, path); snprintf(base + baselen, SEAF_PATH_MAX, "%s/", dent->name); new_baselen = baselen + dent->name_len + 1; subdir = seaf_fs_manager_get_seafdir(seaf->fs_mgr, dent->id); if (!subdir) { g_warning("Failed to get dir %s\n", dent->id); return -1; } ret = get_files_dirs_recursive(o, subdir, base, new_baselen); base[baselen] = 0; break; case S_IFLNK: break; default: break; } } return ret; }
static int check_object (CcnetProcessor *processor) { USE_PRIV; char *obj_id; SeafDir *dir; static int i = 0; request_object_batch_begin(priv); /* process inspect queue */ /* Note: All files in a directory must be checked in an iteration, * so we may send out more items than REQUEST_THRESHOLD */ while (g_hash_table_size (priv->fs_objects) < MAX_NUM_UNREVD) { obj_id = (char *) g_queue_pop_head (priv->inspect_queue); if (obj_id == NULL) break; if (!seaf_fs_manager_object_exists(seaf->fs_mgr, obj_id)) { request_object_batch (processor, priv, obj_id); } else { dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr, obj_id); if (!dir) { /* corrupt dir object */ request_object_batch (processor, priv, obj_id); } else { check_seafdir(processor, dir); seaf_dir_free (dir); } } g_free (obj_id); /* free the memory */ } request_object_batch_flush (processor, priv); /* check end condition */ if (i%10 == 0) seaf_debug ("[getfs] pending objects num: %d\n", priv->pending_objects); ++i; if (priv->pending_objects == 0 && g_queue_is_empty(priv->inspect_queue)) { ccnet_processor_send_update (processor, SC_END, SS_END, NULL, 0); ccnet_processor_done (processor, TRUE); return FALSE; } else return TRUE; }
static void export_repo_files_recursive (const char *repo_id, const char *id, const char *parent_dir) { SeafDir *dir; GList *p; SeafDirent *seaf_dent; char *path; SeafFSManager *mgr = seaf->fs_mgr; int version = 1; dir = seaf_fs_manager_get_seafdir (mgr, repo_id, version, id); if (!dir) { return; } for (p = dir->entries; p; p = p->next) { seaf_dent = p->data; path = g_build_filename (parent_dir, seaf_dent->name, NULL); if (S_ISREG(seaf_dent->mode)) { // create file create_file (repo_id, seaf_dent->id, path); } else if (S_ISDIR(seaf_dent->mode)) { if (g_mkdir (path, 0777) < 0) { seaf_warning ("Failed to mkdir %s: %s.\n", path, strerror (errno)); g_free (path); continue; } else { seaf_message ("Export dir %s.\n", path); } export_repo_files_recursive (repo_id, seaf_dent->id, path); } g_free (path); } seaf_dir_free (dir); }
GList * seaf_repo_manager_list_dir_with_perm (SeafRepoManager *mgr, const char *repo_id, const char *dir_path, const char *dir_id, const char *user, int offset, int limit, GError **error) { SeafRepo *repo; char *perm = NULL; SeafDir *dir; SeafDirent *dent; SeafileDirent *d; GList *res = NULL; GList *p; if (!repo_id || !is_uuid_valid(repo_id) || dir_id == NULL || !user) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_DIR_ID, "Bad dir id"); return NULL; } perm = seaf_repo_manager_check_permission (mgr, repo_id, user, error); if (!perm) { if (*error == NULL) g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Access denied"); return NULL; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Bad repo id"); g_free (perm); return NULL; } dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr, repo->store_id, repo->version, dir_id); if (!dir) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_DIR_ID, "Bad dir id"); seaf_repo_unref (repo); g_free (perm); return NULL; } dir->entries = g_list_sort (dir->entries, comp_dirent_func); if (offset < 0) { offset = 0; } int index = 0; for (p = dir->entries; p != NULL; p = p->next, index++) { if (index < offset) { continue; } if (limit > 0) { if (index >= offset + limit) break; } dent = p->data; d = g_object_new (SEAFILE_TYPE_DIRENT, "obj_id", dent->id, "obj_name", dent->name, "mode", dent->mode, "version", dent->version, "mtime", dent->mtime, "size", dent->size, "permission", perm, NULL); res = g_list_prepend (res, d); } seaf_dir_free (dir); seaf_repo_unref (repo); g_free (perm); res = g_list_reverse (res); return res; }
/* * Merge the commits h1 and h2, return merged tree root id * and a flag indicating the cleanness of the merge. * Return 0 if merge is done (no matter clean or not); -1 otherwise. */ int merge_recursive(struct merge_options *o, const char *h1_root, const char *h2_root, const char *ca_root, int *clean, char **root_id) { SeafDir *head, *remote, *common; int code, ret = 0; struct unpack_trees_options opts; char *error = NULL; *clean = 1; head = seaf_fs_manager_get_seafdir (seaf->fs_mgr, h1_root); remote = seaf_fs_manager_get_seafdir (seaf->fs_mgr, h2_root); common = seaf_fs_manager_get_seafdir (seaf->fs_mgr, ca_root); if (!head || !remote || !common) { g_warning ("Invalid commits!\n"); return -1; } /* Get merged index. */ code = seafile_merge_trees(o, &opts, common, head, remote, &error); if (code != 0) { ret = -1; goto out; } /* If only collect blocks, return success. */ if (o->collect_blocks_only) { process_unmerged_entries (o, head, remote); goto out; } /* Update worktree. */ /* On windows, we have to Check if any files need to be updated * are locked by other program (e.g. Office). If no file is locked, * update worktree; otherwise just quit. * * Note that if we're recovering merge on startup, we need to update * worktree no matter files are locked or not, since we cannot retry * this operation. This will produce more confusing results, but * it doesn't hurt data integrity. */ #ifdef WIN32 if (o->recover_merge || o->force_merge || !files_locked_on_windows (o->index, o->worktree)) { update_worktree (&opts, o->recover_merge, o->remote_head, o->branch2, NULL); *clean = process_unmerged_entries (o, head, remote); } else { /* Don't update anything. */ g_debug ("[merge] files are locked, quit merge now.\n"); ret = -1; goto out; } #else update_worktree (&opts, o->recover_merge, o->remote_head, o->branch2, NULL); *clean = process_unmerged_entries (o, head, remote); #endif if (*clean) *root_id = write_tree_from_memory(o); out: seaf_dir_free (head); seaf_dir_free (remote); seaf_dir_free (common); return ret; }
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 int getattr_repo(SeafileSession *seaf, const char *user, const char *repo_id, const char *repo_path, struct stat *stbuf) { SeafRepo *repo = NULL; SeafBranch *branch; SeafCommit *commit = NULL; guint32 mode = 0; char *id = NULL; 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, branch->commit_id); if (!commit) { seaf_warning ("Failed to get commit %.8s.\n", branch->commit_id); ret = -ENOENT; goto out; } id = seaf_fs_manager_path_to_obj_id(seaf->fs_mgr, commit->root_id, repo_path, &mode, NULL); if (!id) { seaf_warning ("Path %s doesn't exist in repo %s.\n", repo_path, repo_id); ret = -ENOENT; goto out; } if (S_ISDIR(mode)) { SeafDir *dir; GList *l; int cnt = 2; /* '.' and '..' */ dir = seaf_fs_manager_get_seafdir(seaf->fs_mgr, id); if (dir) { for (l = dir->entries; l; l = l->next) cnt++; } stbuf->st_size += cnt * sizeof(SeafDirent); stbuf->st_mode = mode | 0755; stbuf->st_nlink = 2; seaf_dir_free (dir); } else if (S_ISREG(mode)) { Seafile *file; file = seaf_fs_manager_get_seafile(seaf->fs_mgr, id); if (file) stbuf->st_size = file->file_size; stbuf->st_mode = mode | 0644; stbuf->st_nlink = 1; seafile_unref (file); } else { return -ENOENT; } out: g_free (id); seaf_repo_unref (repo); seaf_commit_unref (commit); return ret; }
GList * seaf_repo_manager_list_dir_with_perm (SeafRepoManager *mgr, const char *repo_id, const char *dir_path, const char *dir_id, const char *user, int offset, int limit, GError **error) { SeafRepo *repo; char *perm = NULL; SeafDir *dir; SeafDirent *dent; SeafileDirent *d; GList *res = NULL; GList *p; if (!repo_id || !is_uuid_valid(repo_id) || dir_id == NULL || !user) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_DIR_ID, "Bad dir id"); return NULL; } perm = seaf_repo_manager_check_permission (mgr, repo_id, user, error); if (!perm) { if (*error == NULL) g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Access denied"); return NULL; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Bad repo id"); g_free (perm); return NULL; } dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr, repo->store_id, repo->version, dir_id); if (!dir) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_DIR_ID, "Bad dir id"); seaf_repo_unref (repo); g_free (perm); return NULL; } dir->entries = g_list_sort (dir->entries, comp_dirent_func); if (offset < 0) { offset = 0; } int index = 0; gboolean is_shared; char *cur_path; GHashTable *shared_sub_dirs = NULL; if (!repo->virtual_info) { char *repo_owner = seaf_repo_manager_get_repo_owner (seaf->repo_mgr, repo_id); if (repo_owner && strcmp (user, repo_owner) == 0) { shared_sub_dirs = seaf_share_manager_get_shared_sub_dirs (seaf->share_mgr, repo->store_id, dir_path); } g_free (repo_owner); } for (p = dir->entries; p != NULL; p = p->next, index++) { if (index < offset) { continue; } if (limit > 0) { if (index >= offset + limit) break; } dent = p->data; d = g_object_new (SEAFILE_TYPE_DIRENT, "obj_id", dent->id, "obj_name", dent->name, "mode", dent->mode, "version", dent->version, "mtime", dent->mtime, "size", dent->size, "permission", perm, NULL); if (shared_sub_dirs && S_ISDIR(dent->mode)) { if (strcmp (dir_path, "/") == 0) { cur_path = g_strconcat (dir_path, dent->name, NULL); } else { cur_path = g_strconcat (dir_path, "/", dent->name, NULL); } is_shared = g_hash_table_lookup (shared_sub_dirs, cur_path) ? TRUE : FALSE; g_free (cur_path); g_object_set (d, "is_shared", is_shared, NULL); } res = g_list_prepend (res, d); } if (shared_sub_dirs) g_hash_table_destroy (shared_sub_dirs); seaf_dir_free (dir); seaf_repo_unref (repo); g_free (perm); if (res) res = g_list_reverse (res); return res; }