seafile/daemon/sync-status-tree.c
Xiangyue Cai eed7fa2250
Reduce explorer refresh when copy large folders into library (#2432)
* refresh explorer only for top dir

* fixed refresh problem

* no need to change ignore logic

* only refresh top dir when syncing

* refresh explorer when syncing
2021-03-22 12:07:16 +08:00

289 lines
6.7 KiB
C

#include "common.h"
#include "seafile-session.h"
#include "sync-status-tree.h"
#include "log.h"
struct _SyncStatusDir {
GHashTable *dirents; /* name -> dirent. */
};
typedef struct _SyncStatusDir SyncStatusDir;
struct _SyncStatusDirent {
char *name;
int mode;
/* Only used for directories. */
SyncStatusDir *subdir;
};
typedef struct _SyncStatusDirent SyncStatusDirent;
struct SyncStatusTree {
SyncStatusDir *root;
char *worktree;
};
typedef struct SyncStatusTree SyncStatusTree;
static void
sync_status_dirent_free (SyncStatusDirent *dirent);
static SyncStatusDir *
sync_status_dir_new ()
{
SyncStatusDir *dir = g_new0 (SyncStatusDir, 1);
dir->dirents = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
(GDestroyNotify)sync_status_dirent_free);
return dir;
}
static void
sync_status_dir_free (SyncStatusDir *dir)
{
if (!dir)
return;
g_hash_table_destroy (dir->dirents);
g_free (dir);
}
static SyncStatusDirent *
sync_status_dirent_new (const char *name, int mode)
{
SyncStatusDirent *dirent = g_new0(SyncStatusDirent, 1);
dirent->name = g_strdup(name);
dirent->mode = mode;
if (S_ISDIR(mode))
dirent->subdir = sync_status_dir_new ();
return dirent;
}
static void
sync_status_dirent_free (SyncStatusDirent *dirent)
{
if (!dirent)
return;
g_free (dirent->name);
sync_status_dir_free (dirent->subdir);
g_free (dirent);
}
SyncStatusTree *
sync_status_tree_new (const char *worktree)
{
SyncStatusTree *tree = g_new0(SyncStatusTree, 1);
tree->root = sync_status_dir_new ();
tree->worktree = g_strdup(worktree);
return tree;
}
#if 0
#ifdef WIN32
static void
refresh_recursive (const char *basedir, SyncStatusDir *dir)
{
GHashTableIter iter;
gpointer key, value;
char *dname, *path;
SyncStatusDirent *dirent;
g_hash_table_iter_init (&iter, dir->dirents);
while (g_hash_table_iter_next (&iter, &key, &value)) {
dname = key;
dirent = value;
path = g_strconcat(basedir, "/", dname, NULL);
seaf_sync_manager_add_refresh_path (seaf->sync_mgr, path);
if (S_ISDIR(dirent->mode))
refresh_recursive (path, dirent->subdir);
g_free (path);
}
}
#endif
#endif /* 0 */
void
sync_status_tree_free (struct SyncStatusTree *tree)
{
if (!tree)
return;
#ifdef WIN32
/* refresh_recursive (tree->worktree, tree->root); */
#endif
/* Free the tree recursively. */
sync_status_dir_free (tree->root);
g_free (tree->worktree);
g_free (tree);
}
void
sync_status_tree_add (SyncStatusTree *tree,
const char *path,
int mode,
gboolean refresh)
{
char **dnames = NULL;
guint n, i;
char *dname;
SyncStatusDir *dir = tree->root;
SyncStatusDirent *dirent;
GString *buf;
dnames = g_strsplit (path, "/", 0);
if (!dnames)
return;
n = g_strv_length (dnames);
buf = g_string_new ("");
g_string_append (buf, tree->worktree);
for (i = 0; i < n; i++) {
dname = dnames[i];
dirent = g_hash_table_lookup (dir->dirents, dname);
g_string_append (buf, "/");
g_string_append (buf, dname);
if (dirent) {
if (S_ISDIR(dirent->mode)) {
if (i == (n-1)) {
goto out;
} else {
dir = dirent->subdir;
}
} else {
goto out;
}
} else {
if (i == (n-1)) {
dirent = sync_status_dirent_new (dname, mode);
g_hash_table_insert (dir->dirents, g_strdup(dname), dirent);
} else {
dirent = sync_status_dirent_new (dname, S_IFDIR);
g_hash_table_insert (dir->dirents, g_strdup(dname), dirent);
dir = dirent->subdir;
}
#ifdef WIN32
if (refresh)
seaf_sync_manager_add_refresh_path (seaf->sync_mgr, buf->str);
#endif
}
}
out:
g_string_free (buf, TRUE);
g_strfreev (dnames);
}
inline static gboolean
is_empty_dir (SyncStatusDirent *dirent)
{
return (g_hash_table_size(dirent->subdir->dirents) == 0);
}
static void
remove_item (SyncStatusDir *dir, const char *dname, const char *fullpath)
{
g_hash_table_remove (dir->dirents, dname);
}
static void
delete_recursive (SyncStatusDir *dir, char **dnames, guint n, guint i,
const char *base)
{
char *dname;
SyncStatusDirent *dirent;
char *fullpath = NULL;
dname = dnames[i];
fullpath = g_strconcat (base, "/", dname, NULL);
dirent = g_hash_table_lookup (dir->dirents, dname);
if (dirent) {
if (S_ISDIR(dirent->mode)) {
if (i == (n-1)) {
if (is_empty_dir(dirent))
remove_item (dir, dname, fullpath);
} else {
delete_recursive (dirent->subdir, dnames, n, ++i, fullpath);
/* If this dir becomes empty after deleting the entry below,
* remove the dir itself too.
*/
if (is_empty_dir(dirent))
remove_item (dir, dname, fullpath);
}
} else if (i == (n-1)) {
remove_item (dir, dname, fullpath);
}
}
g_free (fullpath);
}
void
sync_status_tree_del (SyncStatusTree *tree,
const char *path)
{
char **dnames = NULL;
guint n;
SyncStatusDir *dir = tree->root;
dnames = g_strsplit (path, "/", 0);
if (!dnames)
return;
n = g_strv_length (dnames);
delete_recursive (dir, dnames, n, 0, tree->worktree);
g_strfreev (dnames);
}
int
sync_status_tree_exists (SyncStatusTree *tree,
const char *path)
{
char **dnames = NULL;
guint n, i;
char *dname;
SyncStatusDir *dir = tree->root;
SyncStatusDirent *dirent;
int ret = 0;
dnames = g_strsplit (path, "/", 0);
if (!dnames)
return ret;
n = g_strv_length (dnames);
for (i = 0; i < n; i++) {
dname = dnames[i];
dirent = g_hash_table_lookup (dir->dirents, dname);
if (dirent) {
if (S_ISDIR(dirent->mode)) {
if (i == (n-1)) {
ret = 1;
goto out;
} else {
dir = dirent->subdir;
}
} else {
if (i == (n-1)) {
ret = 1;
goto out;
} else {
goto out;
}
}
} else {
goto out;
}
}
out:
g_strfreev (dnames);
return ret;
}