File Manager / src / model /
fs-operation.c
- /*
- * Copyright 2014 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- #include "model/fs-operation.h"
- #include "utils/common-utils.h"
- #include "utils/model-utils.h"
- #include "utils/logger.h"
- #include <sys/types.h>
- #include <dirent.h>
- #include <Ecore.h>
- #define FM_MAX_INT_DIGITS_COUNT 10
- #define FM_COPY_BUF_SIZE 16384
- struct _fs_operation {
- Eina_List *source_list;
- char *dst_path;
- operation_type oper_type;
- fs_operation_cb_func cb_func;
- fs_operation_cb_data *cb_data;
- Ecore_Thread *exec_thread;
- Eina_Bool is_canceled;
- };
- typedef struct _fs_operation_copy_info {
- char *source_path;
- char *destination_path;
- file_type type;
- } fs_operation_copy_info;
- static int _fs_operation_copy(fs_operation *operation);
- static int _fs_operation_move(fs_operation *operation);
- static int _fs_operation_delete(fs_operation *operation);
- static int _fs_operation_rename(fs_operation *operation, const char *source_path, const char *dest_path);
- static void _fs_operation_clear_data(fs_operation *operation);
- static void _fs_operation_clear_copy_list(Eina_List **copy_list);
- static int _fs_operation_recursive_fill_copy_list(fs_operation *operation, Eina_List *src_list, Eina_List **copy_list,
- const char *destination_path);
- static int _fs_operation_proceed_copy(Eina_List *copy_list);
- static bool _fs_operation_file_copy(const char *src, const char *dst);
- static bool _fs_operation_file_recursive_rm(const char *dir);
- static int _fs_operation_get_items_list(Eina_List **list_to_fill, const char *source_path);
- static char *_fs_operation_remove_file_extention(const char *path);
- static char *_fs_operation_append_filename_if_duplicate(const char *path_to_check, const char *original_name);
- static void _fs_operation_delete_exec_thread(fs_operation *operation);
- static void _fs_operation_run(void *data, Ecore_Thread *thread);
- static void _fs_operation_end(void *data, Ecore_Thread *thread);
- fs_operation *fs_operation_create()
- {
- fs_operation *operation = calloc(1, sizeof(fs_operation));
- if (operation) {
- operation->oper_type = OPERATION_TYPE_NONE;
- operation->is_canceled = EINA_FALSE;
- }
- return operation;
- }
- void fs_operation_destroy(fs_operation *operation)
- {
- if (operation) {
- if (operation->exec_thread && !operation->is_canceled) {
- operation->is_canceled = EINA_TRUE;
- return;
- }
- _fs_operation_delete_exec_thread(operation);
- _fs_operation_clear_data(operation);
- free(operation);
- }
- }
- int fs_operation_set_data(fs_operation *operation,
- Eina_List *file_list,
- const char *dst_path,
- operation_type type)
- {
- RETVM_IF(!operation, RESULT_TYPE_INVALID_ARG, "Operation object is NULL");
- RETVM_IF(!file_list, RESULT_TYPE_INVALID_ARG, "File list is NULL");
- RETVM_IF(!dst_path && (type != OPERATION_TYPE_DELETE), RESULT_TYPE_INVALID_ARG, "Destination path is NULL");
- RETVM_IF(type == OPERATION_TYPE_NONE, RESULT_TYPE_INVALID_ARG, "No appropriate operation type");
- int res = common_util_copy_selected_file_list(file_list, &operation->source_list);
- if (res != RESULT_TYPE_OK) {
- _fs_operation_clear_data(operation);
- ERR("Failed to copy source list");
- return res;
- }
- if (dst_path) {
- operation->dst_path = strdup(dst_path);
- if (!operation->dst_path) {
- _fs_operation_clear_data(operation);
- ERR("Failed to allocate memory for destination path");
- return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
- }
- }
- operation->oper_type = type;
- return RESULT_TYPE_OK;
- }
- void _fs_operation_delete_exec_thread(fs_operation *operation)
- {
- if (operation->exec_thread) {
- ecore_thread_cancel(operation->exec_thread);
- operation->exec_thread = NULL;
- }
- }
- void _fs_operation_run(void *data, Ecore_Thread *thread)
- {
- fs_operation *operation = data;
- int res = RESULT_TYPE_FAIL;
- switch (operation->oper_type) {
- case OPERATION_TYPE_COPY:
- res = _fs_operation_copy(operation);
- break;
- case OPERATION_TYPE_MOVE:
- res = _fs_operation_move(operation);
- break;
- case OPERATION_TYPE_DELETE:
- res = _fs_operation_delete(operation);
- break;
- default:
- ERR("Operation type not set");
- return;
- break;
- }
- if (operation->is_canceled) {
- return;
- }
- if (operation->cb_data) {
- operation->cb_data->result = res;
- }
- }
- void _fs_operation_end(void *data, Ecore_Thread *thread)
- {
- fs_operation *operation = data;
- _fs_operation_delete_exec_thread(operation);
- if (operation->is_canceled) {
- fs_operation_destroy(operation);
- return;
- }
- if (operation->cb_func) {
- operation->cb_func(operation->cb_data);
- }
- }
- int fs_operation_execute(fs_operation *operation,
- fs_operation_cb_func cb_func,
- fs_operation_cb_data *cb_data)
- {
- RETVM_IF(!operation, RESULT_TYPE_INVALID_ARG, "Operation object is NULL");
- RETVM_IF(!operation->source_list, RESULT_TYPE_FAIL, "File list not set");
- RETVM_IF(operation->oper_type == OPERATION_TYPE_NONE, RESULT_TYPE_FAIL, "Type of operation not set");
- RETVM_IF(!operation->dst_path && (operation->oper_type != OPERATION_TYPE_DELETE),
- RESULT_TYPE_FAIL, "Destination path not set");
- operation->cb_func = cb_func;
- operation->cb_data = cb_data;
- operation->exec_thread = ecore_thread_feedback_run(_fs_operation_run,
- NULL,
- _fs_operation_end,
- NULL,
- (void *)operation,
- EINA_TRUE);
- RETVM_IF(!operation->exec_thread, RESULT_TYPE_FAIL, "Failed to create thread");
- return RESULT_TYPE_OK;
- }
- static void _fs_operation_clear_data(fs_operation *operation)
- {
- common_util_clear_file_list(&operation->source_list);
- operation->oper_type = OPERATION_TYPE_NONE;
- operation->cb_func = NULL;
- operation->cb_data = NULL;
- free(operation->dst_path);
- operation->dst_path = NULL;
- }
- static bool _fs_operation_file_copy(const char *src, const char *dst)
- {
- FILE *source_file = NULL;
- FILE *destintation_file = NULL;
- char buf[FM_COPY_BUF_SIZE] = {0};
- char src_realpath[PATH_MAX] = {'\0'};
- char dst_realpath[PATH_MAX] = {'\0'};
- size_t num = 0;
- if (!realpath(src, src_realpath)) {
- return false;
- }
- if (realpath(dst, dst_realpath) && !strcmp(src_realpath, dst_realpath)) {
- return false;
- }
- source_file = fopen(src, "rb");
- if (!source_file) {
- return false;
- }
- destintation_file = fopen(dst, "wb");
- if (!destintation_file) {
- fclose(source_file);
- return false;
- }
- while ((num = fread(buf, 1, sizeof(buf), source_file)) > 0) {
- if (fwrite(buf, 1, num, destintation_file) != num) {
- fclose(source_file);
- fclose(destintation_file);
- return false;
- }
- }
- fclose(source_file);
- fclose(destintation_file);
- return true;
- }
- static bool _fs_operation_file_recursive_rm(const char *dir)
- {
- DIR *dir_ptr = NULL;
- struct dirent *dp = NULL;
- char path[PATH_MAX] = {'\0'};
- struct stat st;
- if (stat(dir, &st) == -1) {
- return false;
- }
- if (S_ISDIR(st.st_mode)) {
- dir_ptr = opendir(dir);
- if (dir_ptr) {
- while ((dp = readdir(dir_ptr))) {
- if ((strcmp(dp->d_name, ".")) && (strcmp(dp->d_name, ".."))) {
- snprintf(path, PATH_MAX, "%s/%s", dir, dp->d_name);
- if (!_fs_operation_file_recursive_rm(path)) {
- closedir(dir_ptr);
- return false;
- }
- }
- }
- closedir(dir_ptr);
- }
- }
- if (remove(dir) < 0) {
- return false;
- }
- return true;
- }
- static void _fs_operation_clear_copy_list(Eina_List **copy_list)
- {
- RETM_IF(!copy_list, "Copy list pointer is NULL");
- if (*copy_list) {
- fs_operation_copy_info *pNode = NULL;
- EINA_LIST_FREE(*copy_list, pNode)
- {
- free(pNode->source_path);
- free(pNode->destination_path);
- free(pNode);
- }
- *copy_list = NULL;
- }
- }
- static char *_fs_operation_remove_file_extention(const char *path)
- {
- int len = strlen(path);
- char *striped_path = calloc(1, (len + 1));
- char *extention = strrchr(path, '.');
- if (!extention) {
- strncpy(striped_path, path, (len + 1));
- return striped_path;
- }
- strncpy(striped_path, path, (extention - path));
- striped_path[extention - path] = '\0';
- return striped_path;
- }
- static char *_fs_operation_append_filename_if_duplicate(const char *path_to_check, const char *striped_path)
- {
- if (model_utils_is_file_exists(path_to_check)) {
- int copy_index = 1;
- char *new_path = NULL;
- char index_buffer[FM_MAX_INT_DIGITS_COUNT] = { 0 };
- char *extention = NULL;
- Eina_Bool is_dir = model_utils_file_is_dir(path_to_check);
- if (!is_dir) {
- extention = strrchr(path_to_check, '.');
- }
- while (true) {
- sprintf(index_buffer, "%d", copy_index);
- new_path = common_util_strconcat(striped_path, "(", index_buffer, ")", extention, NULL);
- if (model_utils_is_file_exists(new_path)) {
- free(new_path);
- copy_index++;
- } else {
- return new_path;
- }
- }
- }
- return strdup(path_to_check);
- }
- static int _fs_operation_get_items_list(Eina_List **list_to_fill, const char *source_path)
- {
- Eina_List *dir_list = NULL;
- Eina_List *file_list = NULL;
- int res = model_utils_read_dir(source_path, &dir_list, &file_list);
- RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to get items list");
- *list_to_fill = eina_list_merge(dir_list, file_list);
- return RESULT_TYPE_OK;
- }
- static int _fs_operation_recursive_fill_copy_list(fs_operation *operation, Eina_List *src_list,
- Eina_List **copy_list,
- const char *destination_path)
- {
- Eina_List *list = NULL;
- node_info *data = NULL;
- EINA_LIST_FOREACH(src_list, list, data) {
- if (operation->is_canceled) {
- return RESULT_TYPE_OPERATION_INTERUPTED;
- }
- char *striped_path = NULL;
- char *source_path = common_util_strconcat(data->parent_path, "/", data->name, NULL);
- char *dest_path = common_util_strconcat(destination_path, "/", data->name, NULL);
- if (strcmp(source_path, destination_path) == 0) {
- free(source_path);
- free(dest_path);
- return RESULT_TYPE_OPERATION_INVALID_DEST;
- }
- if (model_utils_file_is_dir(dest_path)) {
- striped_path = strdup(dest_path);
- } else {
- striped_path = _fs_operation_remove_file_extention(dest_path);
- }
- char *checked_path = _fs_operation_append_filename_if_duplicate(dest_path, striped_path);
- int res = RESULT_TYPE_OK;
- fs_operation_copy_info *copy_item = calloc(1, sizeof(fs_operation_copy_info));
- copy_item->source_path = source_path;
- copy_item->destination_path = checked_path;
- copy_item->type = data->type;
- if (data->type == FILE_TYPE_DIR) {
- Eina_List *new_src_list = NULL;
- res = _fs_operation_get_items_list(&new_src_list, source_path);
- if (res == RESULT_TYPE_OK) {
- res = _fs_operation_recursive_fill_copy_list(operation, new_src_list, copy_list, checked_path);
- common_util_clear_file_list(&new_src_list);
- }
- }
- *copy_list = eina_list_append(*copy_list, copy_item);
- free(dest_path);
- free(striped_path);
- RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to copy items");
- }
- return RESULT_TYPE_OK;
- }
- static int _fs_operation_proceed_copy(Eina_List *copy_list)
- {
- Eina_List *list = NULL;
- fs_operation_copy_info *data = NULL;
- int res = RESULT_TYPE_OK;
- EINA_LIST_REVERSE_FOREACH(copy_list, list, data) {
- if (data->type == FILE_TYPE_DIR) {
- (void)mkdir(data->destination_path, DIR_MODE);
- } else if (!_fs_operation_file_copy(data->source_path, data->destination_path)) {
- res = RESULT_TYPE_FAIL;
- }
- }
- RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to proceed copy list");
- return RESULT_TYPE_OK;
- }
- static int _fs_operation_copy(fs_operation *operation)
- {
- Eina_List *copy_list = NULL;
- int res = _fs_operation_recursive_fill_copy_list(operation, operation->source_list, ©_list, operation->dst_path);
- if (res == RESULT_TYPE_OK) {
- res = _fs_operation_proceed_copy(copy_list);
- }
- _fs_operation_clear_copy_list(©_list);
- RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to copy source");
- return RESULT_TYPE_OK;
- }
- static int _fs_operation_rename(fs_operation *operation, const char *source_path, const char *dest_path)
- {
- int res = RESULT_TYPE_FAIL;
- struct stat st;
- if (stat(source_path, &st) != 0) {
- ERR("Failed to get stat for folder %s. ERRNO = %d", source_path, errno);
- return res;
- }
- if (S_ISREG(st.st_mode)) {
- if (!_fs_operation_file_copy(source_path, dest_path)) {
- ERR("Failed to copy file %s", source_path);
- return res;
- }
- chmod(dest_path, st.st_mode);
- int res_remove = remove(source_path);
- RETVM_IF(res_remove < 0, RESULT_TYPE_FAIL, "Failed to remove file %s", source_path);
- res = RESULT_TYPE_OK;
- } else {
- Eina_List *copy_list = NULL;
- Eina_List *src_list = NULL;
- res = _fs_operation_get_items_list(&src_list, source_path);
- RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to get item list for dis %s", source_path);
- res = _fs_operation_recursive_fill_copy_list(operation, src_list, ©_list, dest_path);
- common_util_clear_file_list(&src_list);
- RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to fill copy list recursivly");
- fs_operation_copy_info *copy_item = calloc(1, sizeof(fs_operation_copy_info));
- copy_item->source_path = strdup(source_path);
- copy_item->destination_path = strdup(dest_path);
- copy_item->type = FILE_TYPE_DIR;
- copy_list = eina_list_append(copy_list, copy_item);
- res = _fs_operation_proceed_copy(copy_list);
- _fs_operation_clear_copy_list(©_list);
- RETVM_IF(res != RESULT_TYPE_OK, res, "Failed perform operation copy");
- if (!_fs_operation_file_recursive_rm(source_path)) {
- ERR("Failed to delete dir %s recursively", source_path);
- return RESULT_TYPE_FAIL;
- }
- }
- return res;
- }
- static int _fs_operation_move(fs_operation *operation)
- {
- Eina_List *list = NULL;
- node_info *data = NULL;
- int result = RESULT_TYPE_OK;
- EINA_LIST_FOREACH(operation->source_list, list, data) {
- if (operation->is_canceled) {
- return RESULT_TYPE_OPERATION_INTERUPTED;
- }
- char *striped_path = NULL;
- char *source_path = common_util_strconcat(data->parent_path, "/", data->name, NULL);
- char *dest_path = common_util_strconcat(operation->dst_path, "/", data->name, NULL);
- if (strcmp(source_path, dest_path) == 0) {
- free(source_path);
- free(dest_path);
- return RESULT_TYPE_OPERATION_INVALID_DEST;
- }
- if (model_utils_file_is_dir(dest_path)) {
- striped_path = strdup(dest_path);
- } else {
- striped_path = _fs_operation_remove_file_extention(dest_path);
- }
- char *checked_path = _fs_operation_append_filename_if_duplicate(dest_path, striped_path);
- if (rename(source_path, checked_path)) {
- if (errno == EINVAL) {
- result = RESULT_TYPE_OPERATION_INVALID_DEST;
- } else if (errno == EXDEV) {
- result = _fs_operation_rename(operation, source_path, checked_path);
- }
- }
- free(dest_path);
- free(striped_path);
- free(source_path);
- free(checked_path);
- RETVM_IF(result != RESULT_TYPE_OK, result, "Failed to move source ");
- }
- return RESULT_TYPE_OK;
- }
- static int _fs_operation_delete(fs_operation *operation)
- {
- Eina_List *list = NULL;
- node_info *data = NULL;
- int result = RESULT_TYPE_OK;
- EINA_LIST_FOREACH(operation->source_list, list, data) {
- if (operation->is_canceled) {
- result = RESULT_TYPE_OPERATION_INTERUPTED;
- break;
- }
- char *temp_name = common_util_strconcat(data->parent_path, "/", data->name, NULL);
- if (data->type == FILE_TYPE_DIR && model_utils_file_is_dir(temp_name)) {
- if (!_fs_operation_file_recursive_rm(temp_name)) {
- ERR("Failed to delete dir %s recursively", temp_name);
- }
- } else if (model_utils_is_file_exists(temp_name)) {
- if (remove(temp_name) < 0) {
- ERR("Failed to delete file %s", temp_name);
- }
- } else {
- ERR("%s %s doesn't exist", (data->type == FILE_TYPE_DIR) ? "Dir" : "File", temp_name);
- }
- free(temp_name);
- }
- return result;
- }