File Manager / src / model /

fs-operation.c

  1. /*
  2. * Copyright 2014 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17.  
  18. #include "model/fs-operation.h"
  19. #include "utils/common-utils.h"
  20. #include "utils/model-utils.h"
  21. #include "utils/logger.h"
  22.  
  23. #include <sys/types.h>
  24. #include <dirent.h>
  25. #include <Ecore.h>
  26.  
  27. #define FM_MAX_INT_DIGITS_COUNT 10
  28. #define FM_COPY_BUF_SIZE 16384
  29.  
  30. struct _fs_operation {
  31. Eina_List *source_list;
  32. char *dst_path;
  33. operation_type oper_type;
  34. fs_operation_cb_func cb_func;
  35. fs_operation_cb_data *cb_data;
  36. Ecore_Thread *exec_thread;
  37. Eina_Bool is_canceled;
  38. };
  39.  
  40. typedef struct _fs_operation_copy_info {
  41. char *source_path;
  42. char *destination_path;
  43. file_type type;
  44. } fs_operation_copy_info;
  45.  
  46. static int _fs_operation_copy(fs_operation *operation);
  47. static int _fs_operation_move(fs_operation *operation);
  48. static int _fs_operation_delete(fs_operation *operation);
  49. static int _fs_operation_rename(fs_operation *operation, const char *source_path, const char *dest_path);
  50. static void _fs_operation_clear_data(fs_operation *operation);
  51. static void _fs_operation_clear_copy_list(Eina_List **copy_list);
  52. static int _fs_operation_recursive_fill_copy_list(fs_operation *operation, Eina_List *src_list, Eina_List **copy_list,
  53. const char *destination_path);
  54. static int _fs_operation_proceed_copy(Eina_List *copy_list);
  55. static bool _fs_operation_file_copy(const char *src, const char *dst);
  56. static bool _fs_operation_file_recursive_rm(const char *dir);
  57. static int _fs_operation_get_items_list(Eina_List **list_to_fill, const char *source_path);
  58. static char *_fs_operation_remove_file_extention(const char *path);
  59. static char *_fs_operation_append_filename_if_duplicate(const char *path_to_check, const char *original_name);
  60.  
  61. static void _fs_operation_delete_exec_thread(fs_operation *operation);
  62. static void _fs_operation_run(void *data, Ecore_Thread *thread);
  63. static void _fs_operation_end(void *data, Ecore_Thread *thread);
  64.  
  65.  
  66. fs_operation *fs_operation_create()
  67. {
  68. fs_operation *operation = calloc(1, sizeof(fs_operation));
  69. if (operation) {
  70. operation->oper_type = OPERATION_TYPE_NONE;
  71. operation->is_canceled = EINA_FALSE;
  72. }
  73. return operation;
  74. }
  75.  
  76. void fs_operation_destroy(fs_operation *operation)
  77. {
  78. if (operation) {
  79. if (operation->exec_thread && !operation->is_canceled) {
  80. operation->is_canceled = EINA_TRUE;
  81. return;
  82. }
  83. _fs_operation_delete_exec_thread(operation);
  84. _fs_operation_clear_data(operation);
  85. free(operation);
  86. }
  87. }
  88.  
  89. int fs_operation_set_data(fs_operation *operation,
  90. Eina_List *file_list,
  91. const char *dst_path,
  92. operation_type type)
  93. {
  94. RETVM_IF(!operation, RESULT_TYPE_INVALID_ARG, "Operation object is NULL");
  95. RETVM_IF(!file_list, RESULT_TYPE_INVALID_ARG, "File list is NULL");
  96. RETVM_IF(!dst_path && (type != OPERATION_TYPE_DELETE), RESULT_TYPE_INVALID_ARG, "Destination path is NULL");
  97. RETVM_IF(type == OPERATION_TYPE_NONE, RESULT_TYPE_INVALID_ARG, "No appropriate operation type");
  98.  
  99. int res = common_util_copy_selected_file_list(file_list, &operation->source_list);
  100. if (res != RESULT_TYPE_OK) {
  101. _fs_operation_clear_data(operation);
  102. ERR("Failed to copy source list");
  103. return res;
  104. }
  105. if (dst_path) {
  106. operation->dst_path = strdup(dst_path);
  107. if (!operation->dst_path) {
  108. _fs_operation_clear_data(operation);
  109. ERR("Failed to allocate memory for destination path");
  110. return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
  111. }
  112. }
  113. operation->oper_type = type;
  114.  
  115. return RESULT_TYPE_OK;
  116. }
  117.  
  118.  
  119. void _fs_operation_delete_exec_thread(fs_operation *operation)
  120. {
  121. if (operation->exec_thread) {
  122. ecore_thread_cancel(operation->exec_thread);
  123. operation->exec_thread = NULL;
  124. }
  125. }
  126.  
  127. void _fs_operation_run(void *data, Ecore_Thread *thread)
  128. {
  129. fs_operation *operation = data;
  130. int res = RESULT_TYPE_FAIL;
  131.  
  132. switch (operation->oper_type) {
  133. case OPERATION_TYPE_COPY:
  134. res = _fs_operation_copy(operation);
  135. break;
  136. case OPERATION_TYPE_MOVE:
  137. res = _fs_operation_move(operation);
  138. break;
  139. case OPERATION_TYPE_DELETE:
  140. res = _fs_operation_delete(operation);
  141. break;
  142. default:
  143. ERR("Operation type not set");
  144. return;
  145. break;
  146. }
  147.  
  148. if (operation->is_canceled) {
  149. return;
  150. }
  151.  
  152. if (operation->cb_data) {
  153. operation->cb_data->result = res;
  154. }
  155.  
  156. }
  157.  
  158. void _fs_operation_end(void *data, Ecore_Thread *thread)
  159. {
  160. fs_operation *operation = data;
  161.  
  162. _fs_operation_delete_exec_thread(operation);
  163.  
  164. if (operation->is_canceled) {
  165. fs_operation_destroy(operation);
  166. return;
  167. }
  168.  
  169. if (operation->cb_func) {
  170. operation->cb_func(operation->cb_data);
  171. }
  172. }
  173.  
  174. int fs_operation_execute(fs_operation *operation,
  175. fs_operation_cb_func cb_func,
  176. fs_operation_cb_data *cb_data)
  177. {
  178. RETVM_IF(!operation, RESULT_TYPE_INVALID_ARG, "Operation object is NULL");
  179. RETVM_IF(!operation->source_list, RESULT_TYPE_FAIL, "File list not set");
  180. RETVM_IF(operation->oper_type == OPERATION_TYPE_NONE, RESULT_TYPE_FAIL, "Type of operation not set");
  181. RETVM_IF(!operation->dst_path && (operation->oper_type != OPERATION_TYPE_DELETE),
  182. RESULT_TYPE_FAIL, "Destination path not set");
  183.  
  184. operation->cb_func = cb_func;
  185. operation->cb_data = cb_data;
  186.  
  187. operation->exec_thread = ecore_thread_feedback_run(_fs_operation_run,
  188. NULL,
  189. _fs_operation_end,
  190. NULL,
  191. (void *)operation,
  192. EINA_TRUE);
  193.  
  194. RETVM_IF(!operation->exec_thread, RESULT_TYPE_FAIL, "Failed to create thread");
  195.  
  196. return RESULT_TYPE_OK;
  197. }
  198.  
  199. static void _fs_operation_clear_data(fs_operation *operation)
  200. {
  201. common_util_clear_file_list(&operation->source_list);
  202. operation->oper_type = OPERATION_TYPE_NONE;
  203. operation->cb_func = NULL;
  204. operation->cb_data = NULL;
  205. free(operation->dst_path);
  206. operation->dst_path = NULL;
  207. }
  208.  
  209. static bool _fs_operation_file_copy(const char *src, const char *dst)
  210. {
  211. FILE *source_file = NULL;
  212. FILE *destintation_file = NULL;
  213. char buf[FM_COPY_BUF_SIZE] = {0};
  214. char src_realpath[PATH_MAX] = {'\0'};
  215. char dst_realpath[PATH_MAX] = {'\0'};
  216. size_t num = 0;
  217.  
  218. if (!realpath(src, src_realpath)) {
  219. return false;
  220. }
  221. if (realpath(dst, dst_realpath) && !strcmp(src_realpath, dst_realpath)) {
  222. return false;
  223. }
  224.  
  225. source_file = fopen(src, "rb");
  226. if (!source_file) {
  227. return false;
  228. }
  229.  
  230. destintation_file = fopen(dst, "wb");
  231. if (!destintation_file) {
  232. fclose(source_file);
  233. return false;
  234. }
  235.  
  236. while ((num = fread(buf, 1, sizeof(buf), source_file)) > 0) {
  237. if (fwrite(buf, 1, num, destintation_file) != num) {
  238. fclose(source_file);
  239. fclose(destintation_file);
  240. return false;
  241. }
  242. }
  243.  
  244. fclose(source_file);
  245. fclose(destintation_file);
  246.  
  247. return true;
  248. }
  249.  
  250. static bool _fs_operation_file_recursive_rm(const char *dir)
  251. {
  252. DIR *dir_ptr = NULL;
  253. struct dirent *dp = NULL;
  254. char path[PATH_MAX] = {'\0'};
  255. struct stat st;
  256.  
  257. if (stat(dir, &st) == -1) {
  258. return false;
  259. }
  260.  
  261. if (S_ISDIR(st.st_mode)) {
  262. dir_ptr = opendir(dir);
  263. if (dir_ptr) {
  264. while ((dp = readdir(dir_ptr))) {
  265. if ((strcmp(dp->d_name, ".")) && (strcmp(dp->d_name, ".."))) {
  266. snprintf(path, PATH_MAX, "%s/%s", dir, dp->d_name);
  267. if (!_fs_operation_file_recursive_rm(path)) {
  268. closedir(dir_ptr);
  269. return false;
  270. }
  271. }
  272. }
  273. closedir(dir_ptr);
  274. }
  275. }
  276.  
  277. if (remove(dir) < 0) {
  278. return false;
  279. }
  280.  
  281. return true;
  282. }
  283.  
  284. static void _fs_operation_clear_copy_list(Eina_List **copy_list)
  285. {
  286. RETM_IF(!copy_list, "Copy list pointer is NULL");
  287.  
  288. if (*copy_list) {
  289. fs_operation_copy_info *pNode = NULL;
  290. EINA_LIST_FREE(*copy_list, pNode)
  291. {
  292. free(pNode->source_path);
  293. free(pNode->destination_path);
  294. free(pNode);
  295. }
  296. *copy_list = NULL;
  297. }
  298. }
  299.  
  300. static char *_fs_operation_remove_file_extention(const char *path)
  301. {
  302. int len = strlen(path);
  303. char *striped_path = calloc(1, (len + 1));
  304. char *extention = strrchr(path, '.');
  305.  
  306. if (!extention) {
  307. strncpy(striped_path, path, (len + 1));
  308. return striped_path;
  309. }
  310. strncpy(striped_path, path, (extention - path));
  311. striped_path[extention - path] = '\0';
  312.  
  313. return striped_path;
  314. }
  315.  
  316. static char *_fs_operation_append_filename_if_duplicate(const char *path_to_check, const char *striped_path)
  317. {
  318. if (model_utils_is_file_exists(path_to_check)) {
  319. int copy_index = 1;
  320. char *new_path = NULL;
  321. char index_buffer[FM_MAX_INT_DIGITS_COUNT] = { 0 };
  322. char *extention = NULL;
  323. Eina_Bool is_dir = model_utils_file_is_dir(path_to_check);
  324. if (!is_dir) {
  325. extention = strrchr(path_to_check, '.');
  326. }
  327.  
  328. while (true) {
  329. sprintf(index_buffer, "%d", copy_index);
  330. new_path = common_util_strconcat(striped_path, "(", index_buffer, ")", extention, NULL);
  331. if (model_utils_is_file_exists(new_path)) {
  332. free(new_path);
  333. copy_index++;
  334. } else {
  335. return new_path;
  336. }
  337. }
  338. }
  339.  
  340. return strdup(path_to_check);
  341. }
  342.  
  343. static int _fs_operation_get_items_list(Eina_List **list_to_fill, const char *source_path)
  344. {
  345. Eina_List *dir_list = NULL;
  346. Eina_List *file_list = NULL;
  347.  
  348. int res = model_utils_read_dir(source_path, &dir_list, &file_list);
  349. RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to get items list");
  350.  
  351. *list_to_fill = eina_list_merge(dir_list, file_list);
  352.  
  353. return RESULT_TYPE_OK;
  354. }
  355.  
  356. static int _fs_operation_recursive_fill_copy_list(fs_operation *operation, Eina_List *src_list,
  357. Eina_List **copy_list,
  358. const char *destination_path)
  359. {
  360. Eina_List *list = NULL;
  361. node_info *data = NULL;
  362.  
  363. EINA_LIST_FOREACH(src_list, list, data) {
  364. if (operation->is_canceled) {
  365. return RESULT_TYPE_OPERATION_INTERUPTED;
  366. }
  367.  
  368. char *striped_path = NULL;
  369. char *source_path = common_util_strconcat(data->parent_path, "/", data->name, NULL);
  370. char *dest_path = common_util_strconcat(destination_path, "/", data->name, NULL);
  371.  
  372. if (strcmp(source_path, destination_path) == 0) {
  373. free(source_path);
  374. free(dest_path);
  375. return RESULT_TYPE_OPERATION_INVALID_DEST;
  376. }
  377.  
  378. if (model_utils_file_is_dir(dest_path)) {
  379. striped_path = strdup(dest_path);
  380. } else {
  381. striped_path = _fs_operation_remove_file_extention(dest_path);
  382. }
  383.  
  384. char *checked_path = _fs_operation_append_filename_if_duplicate(dest_path, striped_path);
  385.  
  386. int res = RESULT_TYPE_OK;
  387.  
  388. fs_operation_copy_info *copy_item = calloc(1, sizeof(fs_operation_copy_info));
  389. copy_item->source_path = source_path;
  390. copy_item->destination_path = checked_path;
  391. copy_item->type = data->type;
  392.  
  393. if (data->type == FILE_TYPE_DIR) {
  394. Eina_List *new_src_list = NULL;
  395. res = _fs_operation_get_items_list(&new_src_list, source_path);
  396.  
  397. if (res == RESULT_TYPE_OK) {
  398. res = _fs_operation_recursive_fill_copy_list(operation, new_src_list, copy_list, checked_path);
  399. common_util_clear_file_list(&new_src_list);
  400. }
  401. }
  402.  
  403. *copy_list = eina_list_append(*copy_list, copy_item);
  404.  
  405. free(dest_path);
  406. free(striped_path);
  407.  
  408. RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to copy items");
  409. }
  410.  
  411. return RESULT_TYPE_OK;
  412. }
  413.  
  414. static int _fs_operation_proceed_copy(Eina_List *copy_list)
  415. {
  416. Eina_List *list = NULL;
  417. fs_operation_copy_info *data = NULL;
  418. int res = RESULT_TYPE_OK;
  419.  
  420. EINA_LIST_REVERSE_FOREACH(copy_list, list, data) {
  421. if (data->type == FILE_TYPE_DIR) {
  422. (void)mkdir(data->destination_path, DIR_MODE);
  423. } else if (!_fs_operation_file_copy(data->source_path, data->destination_path)) {
  424. res = RESULT_TYPE_FAIL;
  425. }
  426. }
  427. RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to proceed copy list");
  428.  
  429. return RESULT_TYPE_OK;
  430. }
  431.  
  432. static int _fs_operation_copy(fs_operation *operation)
  433. {
  434. Eina_List *copy_list = NULL;
  435.  
  436. int res = _fs_operation_recursive_fill_copy_list(operation, operation->source_list, &copy_list, operation->dst_path);
  437.  
  438. if (res == RESULT_TYPE_OK) {
  439. res = _fs_operation_proceed_copy(copy_list);
  440. }
  441.  
  442. _fs_operation_clear_copy_list(&copy_list);
  443.  
  444. RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to copy source");
  445.  
  446. return RESULT_TYPE_OK;
  447. }
  448.  
  449.  
  450. static int _fs_operation_rename(fs_operation *operation, const char *source_path, const char *dest_path)
  451. {
  452. int res = RESULT_TYPE_FAIL;
  453. struct stat st;
  454. if (stat(source_path, &st) != 0) {
  455. ERR("Failed to get stat for folder %s. ERRNO = %d", source_path, errno);
  456. return res;
  457. }
  458.  
  459. if (S_ISREG(st.st_mode)) {
  460. if (!_fs_operation_file_copy(source_path, dest_path)) {
  461. ERR("Failed to copy file %s", source_path);
  462. return res;
  463. }
  464.  
  465. chmod(dest_path, st.st_mode);
  466. int res_remove = remove(source_path);
  467. RETVM_IF(res_remove < 0, RESULT_TYPE_FAIL, "Failed to remove file %s", source_path);
  468. res = RESULT_TYPE_OK;
  469. } else {
  470. Eina_List *copy_list = NULL;
  471. Eina_List *src_list = NULL;
  472.  
  473. res = _fs_operation_get_items_list(&src_list, source_path);
  474. RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to get item list for dis %s", source_path);
  475.  
  476. res = _fs_operation_recursive_fill_copy_list(operation, src_list, &copy_list, dest_path);
  477. common_util_clear_file_list(&src_list);
  478. RETVM_IF(res != RESULT_TYPE_OK, res, "Failed to fill copy list recursivly");
  479.  
  480. fs_operation_copy_info *copy_item = calloc(1, sizeof(fs_operation_copy_info));
  481. copy_item->source_path = strdup(source_path);
  482. copy_item->destination_path = strdup(dest_path);
  483. copy_item->type = FILE_TYPE_DIR;
  484. copy_list = eina_list_append(copy_list, copy_item);
  485.  
  486. res = _fs_operation_proceed_copy(copy_list);
  487. _fs_operation_clear_copy_list(&copy_list);
  488. RETVM_IF(res != RESULT_TYPE_OK, res, "Failed perform operation copy");
  489.  
  490. if (!_fs_operation_file_recursive_rm(source_path)) {
  491. ERR("Failed to delete dir %s recursively", source_path);
  492. return RESULT_TYPE_FAIL;
  493. }
  494. }
  495.  
  496. return res;
  497. }
  498.  
  499. static int _fs_operation_move(fs_operation *operation)
  500. {
  501. Eina_List *list = NULL;
  502. node_info *data = NULL;
  503. int result = RESULT_TYPE_OK;
  504.  
  505. EINA_LIST_FOREACH(operation->source_list, list, data) {
  506. if (operation->is_canceled) {
  507. return RESULT_TYPE_OPERATION_INTERUPTED;
  508. }
  509.  
  510. char *striped_path = NULL;
  511. char *source_path = common_util_strconcat(data->parent_path, "/", data->name, NULL);
  512. char *dest_path = common_util_strconcat(operation->dst_path, "/", data->name, NULL);
  513.  
  514. if (strcmp(source_path, dest_path) == 0) {
  515. free(source_path);
  516. free(dest_path);
  517. return RESULT_TYPE_OPERATION_INVALID_DEST;
  518. }
  519.  
  520. if (model_utils_file_is_dir(dest_path)) {
  521. striped_path = strdup(dest_path);
  522. } else {
  523. striped_path = _fs_operation_remove_file_extention(dest_path);
  524. }
  525.  
  526. char *checked_path = _fs_operation_append_filename_if_duplicate(dest_path, striped_path);
  527.  
  528. if (rename(source_path, checked_path)) {
  529. if (errno == EINVAL) {
  530. result = RESULT_TYPE_OPERATION_INVALID_DEST;
  531. } else if (errno == EXDEV) {
  532. result = _fs_operation_rename(operation, source_path, checked_path);
  533. }
  534. }
  535.  
  536. free(dest_path);
  537. free(striped_path);
  538. free(source_path);
  539. free(checked_path);
  540.  
  541. RETVM_IF(result != RESULT_TYPE_OK, result, "Failed to move source ");
  542. }
  543.  
  544. return RESULT_TYPE_OK;
  545. }
  546.  
  547. static int _fs_operation_delete(fs_operation *operation)
  548. {
  549. Eina_List *list = NULL;
  550. node_info *data = NULL;
  551.  
  552. int result = RESULT_TYPE_OK;
  553. EINA_LIST_FOREACH(operation->source_list, list, data) {
  554. if (operation->is_canceled) {
  555. result = RESULT_TYPE_OPERATION_INTERUPTED;
  556. break;
  557. }
  558.  
  559. char *temp_name = common_util_strconcat(data->parent_path, "/", data->name, NULL);
  560.  
  561. if (data->type == FILE_TYPE_DIR && model_utils_file_is_dir(temp_name)) {
  562. if (!_fs_operation_file_recursive_rm(temp_name)) {
  563. ERR("Failed to delete dir %s recursively", temp_name);
  564. }
  565. } else if (model_utils_is_file_exists(temp_name)) {
  566. if (remove(temp_name) < 0) {
  567. ERR("Failed to delete file %s", temp_name);
  568. }
  569. } else {
  570. ERR("%s %s doesn't exist", (data->type == FILE_TYPE_DIR) ? "Dir" : "File", temp_name);
  571. }
  572. free(temp_name);
  573. }
  574.  
  575. return result;
  576. }