File Manager / src / model /
fs-manager.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-manager.h"
#include "model/fs-operation.h"
#include "utils/config.h"
#include "utils/model-utils.h"
#include "utils/logger.h"
#include <storage.h>
#include <stdbool.h>
enum {
FM_CHECK_ARG_VALID = 0x0001,
FM_CHECK_EXIST = 0x0002,
FM_CHECK_PATH_VALID = 0x0004,
FM_CHECK_PARENT_DIR_EXIST = 0x0008,
FM_CHECK_DUPLICATED = 0x0010
};
#define FM_COMP_RES_D1_LESS_D2 -1
#define FM_COMP_RES_D1_GREATER_D2 1
#define FM_COMP_RES_D1_EQUAL_D2 0
struct _fs_manager {
Eina_Bool is_locked;
fs_operation *operation;
fs_manager_complete_cb_func user_cb_func;
void *user_cb_data;
};
static void _fs_manager_clear_data(fs_manager *manager);
static Eina_Bool _fs_manager_check_path_valid(const char *path, int *ret);
static Eina_Bool _fs_manager_check_parent_exists(const char *path, int *ret);
static int _fs_manager_check_error(const char *fullpath, int check_option);
static int _fs_manager_sort_by_name_cb(const void *d1, const void *d2);
static int _fs_manager_is_mmc_supported(bool *supported);
static int _fs_manager_generate_operation(fs_manager *manager,
Eina_List *source_list,
const char *dest_path,
operation_type oper_type,
fs_manager_complete_cb_func func,
void *data);
static void _on_operation_completed(void *data);
fs_manager *fs_manager_create()
{
fs_manager *manager = calloc(1, sizeof(fs_manager));
if (manager) {
manager->is_locked = EINA_FALSE;
}
return manager;
}
void fs_manager_destroy(fs_manager *manager)
{
if (manager) {
_fs_manager_clear_data(manager);
free(manager);
}
}
int fs_manager_get_storage_list(fs_manager *manager, Eina_List **storage_list)
{
RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
char *buf = model_utils_get_phone_path();
if (!buf) {
dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] buf == NULL", __FILE__, __LINE__);
return RESULT_TYPE_INVALID_PATH;
}
if (manager->is_locked) {
ERR("File manager is busy");
free(buf);
return RESULT_TYPE_BUSY;
}
bool is_supported = false;
int res = _fs_manager_is_mmc_supported(&is_supported);
if (res != RESULT_TYPE_OK) {
free(buf);
return res;
}
if (is_supported) {
storage_info *const pNode_internal = calloc(1, sizeof(node_info));
pNode_internal->root_name = strdup(FM_MEMORY_LABEL);
pNode_internal->root_path = strdup(FM_MEMORY_FOLDER);
pNode_internal->type = STORAGE_TYPE_MMC;
*storage_list = eina_list_append(*storage_list, pNode_internal);
}
storage_info *const pNode_device = calloc(1, sizeof(node_info));
pNode_device->root_name = strdup(FM_PHONE_LABEL);
pNode_device->root_path = strdup(buf);
pNode_device->type = STORAGE_TYPE_PHONE;
*storage_list = eina_list_append(*storage_list, pNode_device);
*storage_list = eina_list_sort(*storage_list, eina_list_count(*storage_list), _fs_manager_sort_by_name_cb);
free(buf);
return RESULT_TYPE_OK;
}
int fs_manager_get_file_list(fs_manager *manager, const char *dir_path, Eina_List **file_list)
{
RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
RETVM_IF(!dir_path, RESULT_TYPE_INVALID_ARG, "Path is NULL");
RETVM_IF(!file_list, RESULT_TYPE_INVALID_ARG, "File list is NULL");
if (manager->is_locked) {
ERR("File manager is busy");
return RESULT_TYPE_BUSY;
}
int option = FM_CHECK_EXIST;
int ret = _fs_manager_check_error(dir_path, option);
if (ret != RESULT_TYPE_OK) {
return ret;
}
Eina_List *dirs = NULL;
Eina_List *files = NULL;
ret = model_utils_read_dir(dir_path, &dirs, &files);
if (ret != RESULT_TYPE_OK) {
ERR("Failed to read dir '%s'", dir_path);
return ret;
}
dirs = eina_list_sort(dirs, eina_list_count(dirs), _fs_manager_sort_by_name_cb);
files = eina_list_sort(files, eina_list_count(files), _fs_manager_sort_by_name_cb);
*file_list = eina_list_merge(dirs, files);
return RESULT_TYPE_OK;
}
int fs_manager_copy_files(fs_manager *manager,
Eina_List *source_list,
const char *dest_path,
fs_manager_complete_cb_func cb_func,
void *cb_data)
{
return _fs_manager_generate_operation(manager,
source_list,
dest_path,
OPERATION_TYPE_COPY,
cb_func,
cb_data);
}
int fs_manager_move_files(fs_manager *manager,
Eina_List *source_list,
const char *dest_path,
fs_manager_complete_cb_func cb_func,
void *cb_data)
{
return _fs_manager_generate_operation(manager,
source_list,
dest_path,
OPERATION_TYPE_MOVE,
cb_func,
cb_data);
}
int fs_manager_delete_files(fs_manager *manager,
Eina_List *source_list,
fs_manager_complete_cb_func cb_func,
void *cb_data)
{
return _fs_manager_generate_operation(manager,
source_list,
NULL,
OPERATION_TYPE_DELETE,
cb_func,
cb_data);
}
int fs_manager_create_folder(fs_manager *manager, const char *dir)
{
RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
RETVM_IF(!dir, RESULT_TYPE_INVALID_ARG, "Directory path is NULL");
if (manager->is_locked) {
ERR("File manager is busy");
return RESULT_TYPE_BUSY;
}
int option = FM_CHECK_DUPLICATED | FM_CHECK_PATH_VALID;
int ret = _fs_manager_check_error(dir, option);
if (ret != RESULT_TYPE_OK) {
return ret;
}
if (mkdir(dir, DIR_MODE) < 0) {
ERR("Failed to create folder '%s'", dir);
return RESULT_TYPE_FAIL;
}
return RESULT_TYPE_OK;
}
static Eina_Bool _fs_manager_check_path_valid(const char *path, int *ret)
{
*ret = model_utils_is_path_valid(path, model_utils_is_file_exists(path));
return (*ret == RESULT_TYPE_OK);
}
static Eina_Bool _fs_manager_check_parent_exists(const char *path, int *ret)
{
*ret = RESULT_TYPE_FAIL;
char *const parent_path = model_utils_get_dir_name(path);
if (parent_path) {
*ret = model_utils_is_file_exists(parent_path) ?
RESULT_TYPE_OK :
RESULT_TYPE_DIR_NOT_FOUND;
free(parent_path);
} else {
*ret = RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
}
return (*ret == RESULT_TYPE_OK);
}
static int _fs_manager_sort_by_name_cb(const void *d1, const void *d2)
{
node_info *txt1 = (node_info *)d1;
node_info *txt2 = (node_info *)d2;
char *name1 = NULL;
char *name2 = NULL;
int comp_res = FM_COMP_RES_D1_EQUAL_D2;
if (!txt1 || !txt1->name) {
return FM_COMP_RES_D1_GREATER_D2;
}
if (!txt2 || !txt2->name) {
return FM_COMP_RES_D1_LESS_D2;
}
name1 = strdup(txt1->name);
if (!name1) {
return FM_COMP_RES_D1_LESS_D2;
}
eina_str_tolower(&name1);
name2 = strdup(txt2->name);
if (!name2) {
free(name1);
name1 = NULL;
return FM_COMP_RES_D1_LESS_D2;
}
eina_str_tolower(&name2);
comp_res = strcmp(name1, name2);
free(name1);
free(name2);
return comp_res;
}
static int _fs_manager_check_error(const char *fullpath, int check_option)
{
int ret = RESULT_TYPE_OK;
int ret2 = RESULT_TYPE_OK;
if ((check_option & FM_CHECK_ARG_VALID) && !fullpath) {
ERR("Input argument is NULL");
ret = RESULT_TYPE_INVALID_ARG;
} else if ((check_option & FM_CHECK_EXIST) && (!model_utils_is_file_exists(fullpath))) {
ERR("'%s' doesn't exist", fullpath);
ret = RESULT_TYPE_NOT_EXIST;
} else if ((check_option & FM_CHECK_PATH_VALID) && (!_fs_manager_check_path_valid(fullpath, &ret2))) {
ERR("Path '%s' is invalid", fullpath);
ret = ret2;
} else if ((check_option & FM_CHECK_PARENT_DIR_EXIST) && (!_fs_manager_check_parent_exists(fullpath, &ret2))) {
ERR("Parent directory for '%s' doesn't exist", fullpath);
ret = ret2;
} else if ((check_option & FM_CHECK_DUPLICATED) && (model_utils_is_file_exists(fullpath))) {
ERR("Duplicated name. '%s' already exists", fullpath);
ret = RESULT_TYPE_DUPLICATED_NAME;
}
return ret;
}
static int _fs_manager_is_mmc_supported(bool *supported)
{
RETVM_IF(!supported, RESULT_TYPE_INVALID_ARG, "Input argument is NULL");
*supported = false;
struct statvfs st;
RETVM_IF(storage_get_external_memory_size(&st) < 0,
RESULT_TYPE_FAIL,
"Failed to get external memory size");
double total_size = (double)st.f_frsize * st.f_blocks;
if (total_size > 0) {
*supported = true;
}
return RESULT_TYPE_OK;
}
static int _fs_manager_generate_operation(fs_manager *manager,
Eina_List *source_list,
const char *dest_path,
operation_type oper_type,
fs_manager_complete_cb_func func,
void *data)
{
RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
RETVM_IF(!source_list, RESULT_TYPE_INVALID_ARG, "Source list is NULL");
RETVM_IF(oper_type == OPERATION_TYPE_NONE, RESULT_TYPE_INVALID_ARG, "No appropriate operation type");
RETVM_IF(!dest_path && (oper_type != OPERATION_TYPE_DELETE), RESULT_TYPE_INVALID_ARG, "Destination path is NULL");
if (manager->is_locked) {
ERR("File manager is busy");
return RESULT_TYPE_BUSY;
}
manager->user_cb_func = func;
manager->user_cb_data = data;
manager->operation = fs_operation_create();
if (!manager->operation) {
_fs_manager_clear_data(manager);
ERR("Failed to allocate memory for file operation");
return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
}
int result = fs_operation_set_data(manager->operation, source_list, dest_path, oper_type);
if (result != RESULT_TYPE_OK) {
_fs_manager_clear_data(manager);
ERR("Failed to set operation data");
return result;
}
fs_operation_cb_data *cb_data = calloc(1, sizeof(fs_operation_cb_data));
if (!cb_data) {
_fs_manager_clear_data(manager);
ERR("Failed to allocate memory for callback operation data");
return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
}
cb_data->manager = manager;
cb_data->result = RESULT_TYPE_FAIL;
/* Lock file system manager */
manager->is_locked = EINA_TRUE;
result = fs_operation_execute(manager->operation, _on_operation_completed, cb_data);
if (result != RESULT_TYPE_OK) {
free(cb_data);
_fs_manager_clear_data(manager);
manager->is_locked = EINA_FALSE;
ERR("Failed to execute operation");
}
return result;
}
static void _on_operation_completed(void *data)
{
RETM_IF(!data, "Callback data is NULL");
fs_operation_cb_data *operation_data = data;
fs_manager *manager = operation_data->manager;
RETM_IF(!manager, "File manager in callback data is NULL");
fs_operation_destroy(manager->operation);
manager->operation = NULL;
/* Unlock file system manager */
manager->is_locked = EINA_FALSE;
/* User callback calling */
if (manager->user_cb_func) {
manager->user_cb_func(manager->user_cb_data, operation_data->result);
manager->user_cb_func = NULL;
manager->user_cb_data = NULL;
}
free(operation_data);
}
static void _fs_manager_clear_data(fs_manager *manager)
{
manager->user_cb_func = NULL;
manager->user_cb_data = NULL;
fs_operation_destroy(manager->operation);
manager->operation = NULL;
}