mirror of
https://github.com/haiwen/seafile.git
synced 2025-01-07 03:17:13 +08:00
[client] Fix case rename bug and remove ignored hidden files when delete empty dir.
This commit is contained in:
parent
e79c7f092a
commit
2c4a44c67d
@ -684,7 +684,8 @@ diff_resolve_renames (GList **diff_entries)
|
||||
/* Collect all "deleted" entries. */
|
||||
for (p = *diff_entries; p != NULL; p = p->next) {
|
||||
de = p->data;
|
||||
if (de->status == DIFF_STATUS_DELETED &&
|
||||
if ((de->status == DIFF_STATUS_DELETED ||
|
||||
de->status == DIFF_STATUS_DIR_DELETED) &&
|
||||
memcmp (de->sha1, empty_sha1, 20) != 0)
|
||||
g_hash_table_insert (deleted, de->sha1, p);
|
||||
}
|
||||
@ -692,7 +693,8 @@ diff_resolve_renames (GList **diff_entries)
|
||||
/* Collect all "added" entries into a separate list. */
|
||||
for (p = *diff_entries; p != NULL; p = p->next) {
|
||||
de = p->data;
|
||||
if (de->status == DIFF_STATUS_ADDED &&
|
||||
if ((de->status == DIFF_STATUS_ADDED ||
|
||||
de->status == DIFF_STATUS_DIR_ADDED) &&
|
||||
memcmp (de->sha1, empty_sha1, 20) != 0)
|
||||
added = g_list_prepend (added, p);
|
||||
}
|
||||
@ -704,6 +706,7 @@ diff_resolve_renames (GList **diff_entries)
|
||||
while (p != NULL) {
|
||||
GList *p_add, *p_del;
|
||||
DiffEntry *de_add, *de_del, *de_rename;
|
||||
int rename_status;
|
||||
|
||||
p_add = p->data;
|
||||
de_add = p_add->data;
|
||||
@ -711,7 +714,13 @@ diff_resolve_renames (GList **diff_entries)
|
||||
p_del = g_hash_table_lookup (deleted, de_add->sha1);
|
||||
if (p_del) {
|
||||
de_del = p_del->data;
|
||||
de_rename = diff_entry_new (de_del->type, DIFF_STATUS_RENAMED,
|
||||
|
||||
if (de_add->status == DIFF_STATUS_DIR_ADDED)
|
||||
rename_status = DIFF_STATUS_DIR_RENAMED;
|
||||
else
|
||||
rename_status = DIFF_STATUS_RENAMED;
|
||||
|
||||
de_rename = diff_entry_new (de_del->type, rename_status,
|
||||
de_del->sha1, de_del->name);
|
||||
de_rename->new_name = g_strdup(de_add->name);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#define DIFF_STATUS_UNMERGED 'U'
|
||||
#define DIFF_STATUS_DIR_ADDED 'B'
|
||||
#define DIFF_STATUS_DIR_DELETED 'C'
|
||||
#define DIFF_STATUS_DIR_RENAMED 'E'
|
||||
|
||||
enum {
|
||||
STATUS_UNMERGED_NONE,
|
||||
|
121
common/fs-mgr.c
121
common/fs-mgr.c
@ -1890,6 +1890,85 @@ seaf_fs_manager_traverse_tree (SeafFSManager *mgr,
|
||||
return traverse_dir (mgr, repo_id, version, root_id, callback, user_data, skip_errors);
|
||||
}
|
||||
|
||||
static int
|
||||
traverse_dir_path (SeafFSManager *mgr,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
const char *dir_path,
|
||||
SeafDirent *dent,
|
||||
TraverseFSPathCallback callback,
|
||||
void *user_data)
|
||||
{
|
||||
SeafDir *dir;
|
||||
GList *p;
|
||||
SeafDirent *seaf_dent;
|
||||
gboolean stop = FALSE;
|
||||
char *sub_path;
|
||||
int ret = 0;
|
||||
|
||||
if (!callback (mgr, dir_path, dent, user_data, &stop))
|
||||
return -1;
|
||||
|
||||
if (stop)
|
||||
return 0;
|
||||
|
||||
dir = seaf_fs_manager_get_seafdir (mgr, repo_id, version, dent->id);
|
||||
if (!dir) {
|
||||
seaf_warning ("get seafdir %s failed\n", dent->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (p = dir->entries; p; p = p->next) {
|
||||
seaf_dent = (SeafDirent *)p->data;
|
||||
sub_path = g_strconcat (dir_path, "/", seaf_dent->name, NULL);
|
||||
|
||||
if (S_ISREG(seaf_dent->mode)) {
|
||||
if (!callback (mgr, sub_path, seaf_dent, user_data, &stop)) {
|
||||
g_free (sub_path);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
} else if (S_ISDIR(seaf_dent->mode)) {
|
||||
if (traverse_dir_path (mgr, repo_id, version, sub_path, seaf_dent,
|
||||
callback, user_data) < 0) {
|
||||
g_free (sub_path);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_free (sub_path);
|
||||
}
|
||||
|
||||
seaf_dir_free (dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
seaf_fs_manager_traverse_path (SeafFSManager *mgr,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
const char *root_id,
|
||||
const char *dir_path,
|
||||
TraverseFSPathCallback callback,
|
||||
void *user_data)
|
||||
{
|
||||
SeafDirent *dent;
|
||||
int ret = 0;
|
||||
|
||||
dent = seaf_fs_manager_get_dirent_by_path (mgr, repo_id, version,
|
||||
root_id, dir_path);
|
||||
if (!dent) {
|
||||
seaf_warning ("Failed to get dirent for %.8s:%s.\n", repo_id, dir_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = traverse_dir_path (mgr, repo_id, version, dir_path, dent,
|
||||
callback, user_data);
|
||||
|
||||
seaf_dirent_free (dent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fill_blocklist (SeafFSManager *mgr,
|
||||
const char *repo_id, int version,
|
||||
@ -2247,6 +2326,48 @@ seaf_fs_manager_get_seafdir_id_by_path (SeafFSManager *mgr,
|
||||
return dir_id;
|
||||
}
|
||||
|
||||
SeafDirent *
|
||||
seaf_fs_manager_get_dirent_by_path (SeafFSManager *mgr,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
const char *root_id,
|
||||
const char *path)
|
||||
{
|
||||
SeafDirent *dent = NULL;
|
||||
SeafDir *dir = NULL;
|
||||
char *parent_dir = NULL;
|
||||
char *file_name = NULL;
|
||||
|
||||
parent_dir = g_path_get_dirname(path);
|
||||
file_name = g_path_get_basename(path);
|
||||
|
||||
if (strcmp (parent_dir, ".") == 0)
|
||||
dir = seaf_fs_manager_get_seafdir (mgr, repo_id, version, root_id);
|
||||
else
|
||||
dir = seaf_fs_manager_get_seafdir_by_path (mgr, repo_id, version,
|
||||
root_id, parent_dir, NULL);
|
||||
|
||||
if (!dir) {
|
||||
seaf_warning ("dir %s doesn't exist in repo %.8s.\n", parent_dir, repo_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
GList *p;
|
||||
for (p = dir->entries; p; p = p->next) {
|
||||
SeafDirent *d = p->data;
|
||||
if (strcmp (d->name, file_name) == 0) {
|
||||
dent = seaf_dirent_dup(d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (dir)
|
||||
seaf_dir_free (dir);
|
||||
|
||||
return dent;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
verify_seafdir_v0 (const char *dir_id, const uint8_t *data, int len,
|
||||
gboolean verify_id)
|
||||
|
@ -253,6 +253,21 @@ seaf_fs_manager_traverse_tree (SeafFSManager *mgr,
|
||||
void *user_data,
|
||||
gboolean skip_errors);
|
||||
|
||||
typedef gboolean (*TraverseFSPathCallback) (SeafFSManager *mgr,
|
||||
const char *path,
|
||||
SeafDirent *dent,
|
||||
void *user_data,
|
||||
gboolean *stop);
|
||||
|
||||
int
|
||||
seaf_fs_manager_traverse_path (SeafFSManager *mgr,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
const char *root_id,
|
||||
const char *dir_path,
|
||||
TraverseFSPathCallback callback,
|
||||
void *user_data);
|
||||
|
||||
gboolean
|
||||
seaf_fs_manager_object_exists (SeafFSManager *mgr,
|
||||
const char *repo_id,
|
||||
@ -326,6 +341,13 @@ seaf_fs_manager_get_seafdir_id_by_path (SeafFSManager *mgr,
|
||||
const char *path,
|
||||
GError **error);
|
||||
|
||||
SeafDirent *
|
||||
seaf_fs_manager_get_dirent_by_path (SeafFSManager *mgr,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
const char *root_id,
|
||||
const char *path);
|
||||
|
||||
/* Check object integrity. */
|
||||
|
||||
gboolean
|
||||
|
@ -314,6 +314,19 @@ seaf_repo_unset_readonly (SeafRepo *repo)
|
||||
save_repo_property (repo->manager, repo->id, REPO_PROP_IS_READONLY, "false");
|
||||
}
|
||||
|
||||
gboolean
|
||||
seaf_repo_manager_is_ignored_hidden_file (const char *filename)
|
||||
{
|
||||
GPatternSpec **spec = ignore_patterns;
|
||||
|
||||
while (*spec) {
|
||||
if (g_pattern_match_string(*spec, filename))
|
||||
return TRUE;
|
||||
spec++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
has_trailing_space_or_period (const char *path)
|
||||
@ -1758,7 +1771,8 @@ checkout_file (const char *repo_id,
|
||||
#ifndef __linux__
|
||||
path = build_case_conflict_free_path (worktree, name,
|
||||
conflict_hash, no_conflict_hash,
|
||||
&case_conflict);
|
||||
&case_conflict,
|
||||
FALSE);
|
||||
#else
|
||||
path = build_checkout_path (worktree, name, strlen(name));
|
||||
#endif
|
||||
@ -1900,7 +1914,8 @@ checkout_empty_dir (const char *worktree,
|
||||
#ifndef __linux__
|
||||
path = build_case_conflict_free_path (worktree, name,
|
||||
conflict_hash, no_conflict_hash,
|
||||
&case_conflict);
|
||||
&case_conflict,
|
||||
FALSE);
|
||||
#else
|
||||
path = build_checkout_path (worktree, name, strlen(name));
|
||||
#endif
|
||||
@ -1974,6 +1989,114 @@ cleanup_file_blocks (const char *repo_id, int version, const char *file_id)
|
||||
seafile_unref (file);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
expand_dir_added_cb (SeafFSManager *mgr,
|
||||
const char *path,
|
||||
SeafDirent *dent,
|
||||
void *user_data,
|
||||
gboolean *stop)
|
||||
{
|
||||
GList **expanded = user_data;
|
||||
DiffEntry *de = NULL;
|
||||
unsigned char sha1[20];
|
||||
|
||||
hex_to_rawdata (dent->id, sha1, 20);
|
||||
|
||||
if (S_ISDIR(dent->mode) && strcmp(dent->id, EMPTY_SHA1) == 0)
|
||||
de = diff_entry_new (DIFF_TYPE_COMMITS, DIFF_STATUS_DIR_ADDED, sha1, path);
|
||||
else if (S_ISREG(dent->mode))
|
||||
de = diff_entry_new (DIFF_TYPE_COMMITS, DIFF_STATUS_ADDED, sha1, path);
|
||||
|
||||
if (de) {
|
||||
de->mtime = dent->mtime;
|
||||
de->mode = dent->mode;
|
||||
de->modifier = g_strdup(dent->modifier);
|
||||
de->size = dent->size;
|
||||
*expanded = g_list_prepend (*expanded, de);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand DIR_ADDED results into multiple ADDED results.
|
||||
*/
|
||||
static int
|
||||
expand_diff_results (const char *repo_id, int version,
|
||||
const char *remote_root, const char *local_root,
|
||||
GList **results)
|
||||
{
|
||||
GList *ptr, *next;
|
||||
DiffEntry *de;
|
||||
char obj_id[41];
|
||||
GList *expanded = NULL;
|
||||
|
||||
ptr = *results;
|
||||
while (ptr) {
|
||||
de = ptr->data;
|
||||
|
||||
next = ptr->next;
|
||||
|
||||
if (de->status == DIFF_STATUS_DIR_ADDED) {
|
||||
*results = g_list_delete_link (*results, ptr);
|
||||
|
||||
rawdata_to_hex (de->sha1, obj_id, 20);
|
||||
if (seaf_fs_manager_traverse_path (seaf->fs_mgr,
|
||||
repo_id, version,
|
||||
remote_root,
|
||||
de->name,
|
||||
expand_dir_added_cb,
|
||||
&expanded) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ptr = next;
|
||||
}
|
||||
|
||||
expanded = g_list_reverse (expanded);
|
||||
*results = g_list_concat (*results, expanded);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
for (ptr = expanded; ptr; ptr = ptr->next)
|
||||
diff_entry_free ((DiffEntry *)(ptr->data));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
do_rename_in_worktree (DiffEntry *de, const char *worktree,
|
||||
GHashTable *conflict_hash, GHashTable *no_conflict_hash)
|
||||
{
|
||||
char *old_path, *new_path;
|
||||
gboolean case_conflict;
|
||||
int ret = 0;
|
||||
|
||||
old_path = g_build_filename (worktree, de->name, NULL);
|
||||
|
||||
if (g_file_test (old_path, G_FILE_TEST_EXISTS)) {
|
||||
#ifndef __linux__
|
||||
new_path = build_case_conflict_free_path (worktree, de->new_name,
|
||||
conflict_hash, no_conflict_hash,
|
||||
&case_conflict,
|
||||
TRUE);
|
||||
#else
|
||||
new_path = build_checkout_path (worktree, de->new_name, strlen(de->new_name));
|
||||
#endif
|
||||
|
||||
if (g_rename (old_path, new_path) < 0) {
|
||||
seaf_warning ("Failed to rename %s to %s: %s.\n",
|
||||
old_path, new_path, strerror(errno));
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
g_free (new_path);
|
||||
}
|
||||
|
||||
g_free (old_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define UPDATE_CACHE_SIZE_LIMIT 100 * (1 << 20) /* 100MB */
|
||||
|
||||
int
|
||||
@ -2047,7 +2170,7 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
|
||||
if (diff_commit_roots (task->repo_id, task->repo_version,
|
||||
master_head ? master_head->root_id : EMPTY_SHA1,
|
||||
remote_head->root_id,
|
||||
&results, FALSE) < 0) {
|
||||
&results, TRUE) < 0) {
|
||||
seaf_warning ("Failed to diff for repo %.8s.\n", task->repo_id);
|
||||
ret = FETCH_CHECKOUT_FAILED;
|
||||
goto out;
|
||||
@ -2056,10 +2179,26 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
|
||||
GList *ptr;
|
||||
DiffEntry *de;
|
||||
|
||||
/* Expand DIR_ADDED diff entries. */
|
||||
if (expand_diff_results (task->repo_id, task->repo_version,
|
||||
remote_head->root_id,
|
||||
master_head ? master_head->root_id : EMPTY_SHA1,
|
||||
&results) < 0) {
|
||||
ret = FETCH_CHECKOUT_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
for (ptr = results; ptr; ptr = ptr->next) {
|
||||
de = ptr->data;
|
||||
if (do_check_file_locked (de->name, worktree)) {
|
||||
if (de->status == DIFF_STATUS_DIR_RENAMED) {
|
||||
if (do_check_dir_locked (de->name, worktree)) {
|
||||
seaf_message ("File(s) in dir %s are locked by other program, "
|
||||
"skip checkout.\n", de->name);
|
||||
ret = FETCH_CHECKOUT_FAILED;
|
||||
goto out;
|
||||
}
|
||||
} else if (do_check_file_locked (de->name, worktree)) {
|
||||
seaf_message ("File %s is locked by other program, skip checkout.\n",
|
||||
de->name);
|
||||
ret = FETCH_CHECKOUT_FAILED;
|
||||
@ -2099,13 +2238,10 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
|
||||
++(task->n_to_download);
|
||||
}
|
||||
|
||||
/* Delete/rename files before deleting dirs,
|
||||
* because we can't delete non-empty dirs.
|
||||
*/
|
||||
for (ptr = results; ptr; ptr = ptr->next) {
|
||||
de = ptr->data;
|
||||
if (de->status == DIFF_STATUS_DELETED) {
|
||||
seaf_debug ("Delete %s.\n", de->name);
|
||||
seaf_debug ("Delete file %s.\n", de->name);
|
||||
|
||||
ce = index_name_exists (&istate, de->name, strlen(de->name), 0);
|
||||
if (!ce)
|
||||
@ -2115,33 +2251,28 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
|
||||
|
||||
remove_from_index_with_prefix (&istate, de->name);
|
||||
try_add_empty_parent_dir_entry (worktree, &istate, ignore_list, de->name);
|
||||
} else if (de->status == DIFF_STATUS_DIR_DELETED) {
|
||||
seaf_debug ("Delete dir %s.\n", de->name);
|
||||
|
||||
/* Nothing to delete. */
|
||||
if (!master_head || strcmp(master_head->root_id, EMPTY_SHA1) == 0)
|
||||
continue;
|
||||
|
||||
delete_dir_with_check (task->repo_id, task->repo_version,
|
||||
master_head->root_id, de->name,
|
||||
worktree, &istate);
|
||||
|
||||
try_add_empty_parent_dir_entry (worktree, &istate, ignore_list, de->name);
|
||||
}
|
||||
}
|
||||
|
||||
for (ptr = results; ptr; ptr = ptr->next) {
|
||||
de = ptr->data;
|
||||
if (de->status == DIFF_STATUS_RENAMED) {
|
||||
if (de->status == DIFF_STATUS_RENAMED ||
|
||||
de->status == DIFF_STATUS_DIR_RENAMED) {
|
||||
seaf_debug ("Rename %s to %s.\n", de->name, de->new_name);
|
||||
|
||||
char *old_path = g_build_filename (worktree, de->name, NULL);
|
||||
|
||||
char *new_path;
|
||||
gboolean case_conflict;
|
||||
#ifndef __linux__
|
||||
new_path = build_case_conflict_free_path (worktree, de->new_name,
|
||||
conflict_hash, no_conflict_hash,
|
||||
&case_conflict);
|
||||
#else
|
||||
new_path = build_checkout_path (worktree, de->new_name, strlen(de->new_name));
|
||||
#endif
|
||||
|
||||
if (g_file_test (old_path, G_FILE_TEST_EXISTS) &&
|
||||
g_rename (old_path, new_path) < 0)
|
||||
seaf_warning ("Failed to rename %s to %s: %s.\n",
|
||||
old_path, new_path, strerror(errno));
|
||||
|
||||
g_free (old_path);
|
||||
g_free (new_path);
|
||||
do_rename_in_worktree (de, worktree, conflict_hash, no_conflict_hash);
|
||||
|
||||
rename_index_entries (&istate, de->name, de->new_name);
|
||||
|
||||
@ -2150,22 +2281,6 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
|
||||
}
|
||||
}
|
||||
|
||||
for (ptr = results; ptr; ptr = ptr->next) {
|
||||
de = ptr->data;
|
||||
if (de->status == DIFF_STATUS_DIR_DELETED) {
|
||||
seaf_debug ("Delete %s.\n", de->name);
|
||||
|
||||
ce = index_name_exists (&istate, de->name, strlen(de->name), 0);
|
||||
if (!ce)
|
||||
continue;
|
||||
|
||||
delete_path (worktree, de->name, de->mode, ce->ce_mtime.sec);
|
||||
|
||||
remove_from_index_with_prefix (&istate, de->name);
|
||||
try_add_empty_parent_dir_entry (worktree, &istate, ignore_list, de->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (istate.cache_changed)
|
||||
update_index (&istate, index_path);
|
||||
|
||||
|
@ -415,4 +415,7 @@ int
|
||||
seaf_repo_fetch_and_checkout (struct _TransferTask *task,
|
||||
const char *remote_head_id);
|
||||
|
||||
gboolean
|
||||
seaf_repo_manager_is_ignored_hidden_file (const char *filename);
|
||||
|
||||
#endif
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "merge.h"
|
||||
#include "vc-utils.h"
|
||||
#include "vc-common.h"
|
||||
#include "index/index.h"
|
||||
|
||||
static gint
|
||||
compare_dirents (gconstpointer a, gconstpointer b)
|
||||
@ -170,21 +171,36 @@ int
|
||||
seaf_remove_empty_dir (const char *path)
|
||||
{
|
||||
SeafStat st;
|
||||
char *ds_store, *thumbs_db;
|
||||
GDir *dir;
|
||||
const char *dname;
|
||||
char *full_path;
|
||||
GError *error = NULL;
|
||||
|
||||
if (seaf_stat (path, &st) < 0 || !S_ISDIR(st.st_mode))
|
||||
return 0;
|
||||
|
||||
if (g_rmdir (path) < 0) {
|
||||
ds_store = g_build_filename (path, ".DS_Store", NULL);
|
||||
g_unlink (ds_store);
|
||||
g_free (ds_store);
|
||||
dir = g_dir_open (path, 0, &error);
|
||||
if (!dir) {
|
||||
seaf_warning ("Failed to open dir %s: %s.\n", path, error->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
thumbs_db = g_build_filename (path, "Thumbs.db", NULL);
|
||||
g_unlink (thumbs_db);
|
||||
g_free (thumbs_db);
|
||||
/* Remove all ignored hidden files. */
|
||||
while ((dname = g_dir_read_name (dir)) != NULL) {
|
||||
if (seaf_repo_manager_is_ignored_hidden_file(dname)) {
|
||||
full_path = g_build_path ("/", path, dname, NULL);
|
||||
if (g_unlink (full_path) < 0)
|
||||
seaf_warning ("Failed to remove file %s: %s.\n",
|
||||
full_path, strerror(errno));
|
||||
g_free (full_path);
|
||||
}
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
|
||||
if (g_rmdir (path) < 0) {
|
||||
seaf_warning ("Failed to remove dir %s: %s.\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -314,7 +330,8 @@ case_conflict_utf8 (const char *name1, const char *name2)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
case_conflict_exists (const char *dir_path, const char *new_dname)
|
||||
case_conflict_exists (const char *dir_path, const char *new_dname,
|
||||
char **conflict_dname)
|
||||
{
|
||||
GDir *dir;
|
||||
const char *dname;
|
||||
@ -337,13 +354,14 @@ case_conflict_exists (const char *dir_path, const char *new_dname)
|
||||
char *norm_dname = g_utf8_normalize (dname, -1, G_NORMALIZE_NFC);
|
||||
if (case_conflict_utf8 (norm_dname, new_dname)) {
|
||||
is_case_conflict = TRUE;
|
||||
g_free (norm_dname);
|
||||
*conflict_dname = norm_dname;
|
||||
break;
|
||||
}
|
||||
g_free (norm_dname);
|
||||
#else
|
||||
if (case_conflict_utf8 (dname, new_dname)) {
|
||||
is_case_conflict = TRUE;
|
||||
*conflict_dname = g_strdup(dname);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@ -406,17 +424,19 @@ build_case_conflict_free_path (const char *worktree,
|
||||
const char *ce_name,
|
||||
GHashTable *conflict_hash,
|
||||
GHashTable *no_conflict_hash,
|
||||
gboolean *is_case_conflict)
|
||||
gboolean *is_case_conflict,
|
||||
gboolean is_rename)
|
||||
{
|
||||
GString *buf = g_string_new (worktree);
|
||||
char **components, *ptr;
|
||||
guint i, n_comps;
|
||||
static int dummy;
|
||||
char *conflict_dname = NULL;
|
||||
|
||||
components = g_strsplit (ce_name, "/", -1);
|
||||
n_comps = g_strv_length (components);
|
||||
for (i = 0; i < n_comps; ++i) {
|
||||
char *path = NULL, *dname = NULL, *case_conflict_free_path = NULL;
|
||||
char *path = NULL, *dname = NULL;
|
||||
SeafStat st;
|
||||
|
||||
ptr = components[i];
|
||||
@ -453,7 +473,7 @@ build_case_conflict_free_path (const char *worktree,
|
||||
}
|
||||
|
||||
/* No luck in the hash tables, we have to run case conflict detection. */
|
||||
if (!case_conflict_exists (buf->str, ptr)) {
|
||||
if (!case_conflict_exists (buf->str, ptr, &conflict_dname)) {
|
||||
/* No case conflict. */
|
||||
if (i != n_comps - 1)
|
||||
g_hash_table_insert (no_conflict_hash,
|
||||
@ -470,26 +490,50 @@ build_case_conflict_free_path (const char *worktree,
|
||||
* remember it in the hash table.
|
||||
*/
|
||||
|
||||
dname = gen_case_conflict_free_dname (buf->str, ptr);
|
||||
if (!is_rename) {
|
||||
dname = gen_case_conflict_free_dname (buf->str, ptr);
|
||||
|
||||
case_conflict_free_path = g_build_path ("/", buf->str, dname, NULL);
|
||||
if (i != n_comps - 1) {
|
||||
if (g_mkdir (case_conflict_free_path, 0777) < 0) {
|
||||
seaf_warning ("Failed to create dir %s.\n", case_conflict_free_path);
|
||||
char *case_conflict_free_path = g_build_path ("/", buf->str, dname, NULL);
|
||||
if (i != n_comps - 1) {
|
||||
if (g_mkdir (case_conflict_free_path, 0777) < 0) {
|
||||
seaf_warning ("Failed to create dir %s.\n",
|
||||
case_conflict_free_path);
|
||||
g_free (path);
|
||||
g_free (dname);
|
||||
g_free (case_conflict_free_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_hash_table_insert (conflict_hash, g_strdup(path), g_strdup(dname));
|
||||
}
|
||||
|
||||
g_string_append_printf (buf, "/%s", dname);
|
||||
|
||||
g_free (dname);
|
||||
g_free (case_conflict_free_path);
|
||||
} else {
|
||||
char *src_path = g_build_path ("/", buf->str, conflict_dname, NULL);
|
||||
|
||||
if (i != (n_comps - 1) && g_rename (src_path, path) < 0) {
|
||||
seaf_warning ("Failed to rename %s to %s: %s.\n",
|
||||
src_path, path, strerror(errno));
|
||||
g_free (path);
|
||||
g_free (dname);
|
||||
g_free (case_conflict_free_path);
|
||||
g_free (src_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_hash_table_insert (conflict_hash, g_strdup(path), g_strdup(dname));
|
||||
/* Since the exsiting dir in the worktree has been renamed,
|
||||
* there is no more case conflict.
|
||||
*/
|
||||
g_hash_table_insert (no_conflict_hash, g_strdup(path), &dummy);
|
||||
|
||||
g_string_append_printf (buf, "/%s", ptr);
|
||||
|
||||
g_free (src_path);
|
||||
}
|
||||
|
||||
g_string_append_printf (buf, "/%s", dname);
|
||||
|
||||
g_free (conflict_dname);
|
||||
g_free (path);
|
||||
g_free (dname);
|
||||
g_free (case_conflict_free_path);
|
||||
}
|
||||
|
||||
g_strfreev (components);
|
||||
@ -565,7 +609,8 @@ checkout_entry (struct cache_entry *ce,
|
||||
#ifndef __linux__
|
||||
path = build_case_conflict_free_path (o->base, ce->name,
|
||||
conflict_hash, no_conflict_hash,
|
||||
&case_conflict);
|
||||
&case_conflict,
|
||||
FALSE);
|
||||
#else
|
||||
path = build_checkout_path (o->base, ce->name, ce_namelen(ce));
|
||||
#endif
|
||||
@ -763,18 +808,106 @@ delete_path (const char *worktree, const char *name,
|
||||
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) {
|
||||
g_warning ("Failed to remove dir %s: %s.\n", full_path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
delete_dir_with_check (const char *repo_id,
|
||||
int repo_version,
|
||||
const char *root_id,
|
||||
const char *dir_path,
|
||||
const char *worktree,
|
||||
struct index_state *istate)
|
||||
{
|
||||
SeafDir *dir;
|
||||
int ret;
|
||||
|
||||
dir = seaf_fs_manager_get_seafdir_by_path (seaf->fs_mgr,
|
||||
repo_id, repo_version,
|
||||
root_id, dir_path, NULL);
|
||||
if (!dir) {
|
||||
seaf_warning ("Failed to find dir %s in repo %.8s.\n", dir_path, repo_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = delete_dir_recursive (repo_id, repo_version, dir_path,
|
||||
dir, worktree, istate);
|
||||
|
||||
seaf_dir_free (dir);
|
||||
|
||||
/* Remove all index entries under this directory */
|
||||
remove_from_index_with_prefix (istate, dir_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
gboolean
|
||||
do_check_file_locked (const char *path, const char *worktree)
|
||||
static gboolean
|
||||
check_file_locked (const char *path)
|
||||
{
|
||||
char *real_path;
|
||||
HANDLE handle;
|
||||
wchar_t *path_w;
|
||||
|
||||
real_path = g_build_path(PATH_SEPERATOR, worktree, path, NULL);
|
||||
path_w = wchar_from_utf8 (real_path);
|
||||
g_free (real_path);
|
||||
path_w = wchar_from_utf8 (path);
|
||||
|
||||
handle = CreateFileW (path_w,
|
||||
GENERIC_WRITE,
|
||||
@ -793,6 +926,73 @@ do_check_file_locked (const char *path, const char *worktree)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
do_check_file_locked (const char *path, const char *worktree)
|
||||
{
|
||||
char *real_path;
|
||||
gboolean ret;
|
||||
real_path = g_build_path(PATH_SEPERATOR, worktree, path, NULL);
|
||||
ret = check_file_locked (real_path);
|
||||
g_free (real_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_dir_locked_recursive (const char *path)
|
||||
{
|
||||
GDir *dir;
|
||||
const char *dname;
|
||||
char *sub_path;
|
||||
SeafStat st;
|
||||
GError *error = NULL;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
dir = g_dir_open (path, 0, &error);
|
||||
if (!dir) {
|
||||
seaf_warning ("Failed to open dir %s: %s.\n", path, error->message);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
while ((dname = g_dir_read_name (dir)) != NULL) {
|
||||
sub_path = g_build_path (PATH_SEPERATOR, path, dname, NULL);
|
||||
|
||||
if (seaf_stat (sub_path, &st) < 0) {
|
||||
seaf_warning ("Failed to stat %s: %s.\n", sub_path, strerror(errno));
|
||||
g_free (sub_path);
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (check_dir_locked_recursive (sub_path)) {
|
||||
g_free (sub_path);
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
} else if (S_ISREG(st.st_mode)) {
|
||||
if (check_file_locked (sub_path)) {
|
||||
g_free (sub_path);
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_free (sub_path);
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
do_check_dir_locked (const char *path, const char *worktree)
|
||||
{
|
||||
char *real_path = g_build_path (PATH_SEPERATOR, worktree, path, NULL);
|
||||
gboolean ret = check_dir_locked_recursive (real_path);
|
||||
g_free (real_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
files_locked_on_windows (struct index_state *index, const char *worktree)
|
||||
{
|
||||
|
@ -43,7 +43,8 @@ build_case_conflict_free_path (const char *worktree,
|
||||
const char *ce_name,
|
||||
GHashTable *conflict_hash,
|
||||
GHashTable *no_conflict_hash,
|
||||
gboolean *is_case_conflict);
|
||||
gboolean *is_case_conflict,
|
||||
gboolean is_rename);
|
||||
|
||||
char *
|
||||
build_checkout_path (const char *worktree, const char *ce_name, int len);
|
||||
@ -52,9 +53,22 @@ int
|
||||
delete_path (const char *worktree, const char *name,
|
||||
unsigned int mode, gint64 old_mtime);
|
||||
|
||||
struct index_state;
|
||||
|
||||
int
|
||||
delete_dir_with_check (const char *repo_id,
|
||||
int repo_version,
|
||||
const char *root_id,
|
||||
const char *dir_path,
|
||||
const char *worktree,
|
||||
struct index_state *istate);
|
||||
|
||||
gboolean
|
||||
do_check_file_locked (const char *path, const char *worktree);
|
||||
|
||||
gboolean
|
||||
do_check_dir_locked (const char *path, const char *worktree);
|
||||
|
||||
gboolean
|
||||
files_locked_on_windows (struct index_state *index, const char *worktree);
|
||||
|
||||
|
@ -112,11 +112,11 @@ static int getattr_repo(SeafileSession *seaf,
|
||||
|
||||
if (strcmp (repo_path, "/") != 0) {
|
||||
// get dirent of the dir
|
||||
SeafDirent *dirent = fuse_get_dirent_by_path (seaf->fs_mgr,
|
||||
repo->store_id,
|
||||
repo->version,
|
||||
commit->root_id,
|
||||
repo_path);
|
||||
SeafDirent *dirent = seaf_fs_manager_get_dirent_by_path (seaf->fs_mgr,
|
||||
repo->store_id,
|
||||
repo->version,
|
||||
commit->root_id,
|
||||
repo_path);
|
||||
if (dirent && repo->version != 0)
|
||||
stbuf->st_mtime = dirent->mtime;
|
||||
|
||||
@ -136,11 +136,11 @@ static int getattr_repo(SeafileSession *seaf,
|
||||
if (file)
|
||||
stbuf->st_size = file->file_size;
|
||||
|
||||
SeafDirent *dirent = fuse_get_dirent_by_path (seaf->fs_mgr,
|
||||
repo->store_id,
|
||||
repo->version,
|
||||
commit->root_id,
|
||||
repo_path);
|
||||
SeafDirent *dirent = seaf_fs_manager_get_dirent_by_path (seaf->fs_mgr,
|
||||
repo->store_id,
|
||||
repo->version,
|
||||
commit->root_id,
|
||||
repo_path);
|
||||
if (dirent && repo->version != 0)
|
||||
stbuf->st_mtime = dirent->mtime;
|
||||
|
||||
|
@ -83,48 +83,6 @@ int parse_fuse_path (const char *path,
|
||||
return ret;
|
||||
}
|
||||
|
||||
SeafDirent *
|
||||
fuse_get_dirent_by_path (SeafFSManager *mgr,
|
||||
const char *repo_id,
|
||||
int version,
|
||||
const char *root_id,
|
||||
const char *path)
|
||||
{
|
||||
SeafDirent *dent = NULL;
|
||||
SeafDir *dir = NULL;
|
||||
char *parent_dir = NULL;
|
||||
char *file_name = NULL;
|
||||
|
||||
parent_dir = g_path_get_dirname(path);
|
||||
file_name = g_path_get_basename(path);
|
||||
|
||||
if (strcmp (parent_dir, ".") == 0)
|
||||
dir = seaf_fs_manager_get_seafdir (mgr, repo_id, version, root_id);
|
||||
else
|
||||
dir = seaf_fs_manager_get_seafdir_by_path (mgr, repo_id, version,
|
||||
root_id, parent_dir, NULL);
|
||||
|
||||
if (!dir) {
|
||||
seaf_warning ("dir %s doesn't exist in repo %.8s.\n", parent_dir, repo_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
GList *p;
|
||||
for (p = dir->entries; p; p = p->next) {
|
||||
SeafDirent *d = p->data;
|
||||
if (strcmp (d->name, file_name) == 0) {
|
||||
dent = seaf_dirent_dup(d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (dir)
|
||||
seaf_dir_free (dir);
|
||||
|
||||
return dent;
|
||||
}
|
||||
|
||||
static int seaf_fuse_getattr(const char *path, struct stat *stbuf)
|
||||
{
|
||||
memset(stbuf, 0, sizeof(struct stat));
|
||||
|
Loading…
Reference in New Issue
Block a user