2 * Copyright 2013 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.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
8 * http://floralicense.org/license/
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.
22 #include <sys/types.h>
28 #include <sys/syscall.h>
32 #include "mf-copy-internal.h"
33 #include "mf-cancel.h"
34 #include "mf-fo-common.h"
35 #include "mf-fo-internal.h"
36 #include "mf-media-content.h"
37 #include "mf-fo-debug.h"
39 struct _mf_copy_handle_intenal {
54 GSList *copy_list = NULL;
57 #define SAFE_FREE(x) do {\
64 #define MSG_REPORT_PERIOD (1)
65 #define DEF_ALLLOC_SIZE 16384 /*((4)*(1024))*/
66 #define DIR_MODE_BIT (01777)
67 #define FILE_MODE_BIT (S_IRWXU | S_IRWXG | S_IRWXO)
68 #define PER_HANDLE_MAX_SIZE (10*1024*1204)
69 #define PER_HANDLE_MIN_SIZE (1024*1024)
72 static gchar *__mf_copy_change_root_name(const char *name, const char *old_root, const char *new_root)
74 gchar *new_name = NULL;
76 if (name && old_root && new_root) {
77 int old_len = strlen(old_root);
78 int new_len = strlen(new_root);
79 int name_len = strlen(name);
80 const char *base = NULL;
83 if ((strstr(name, old_root) == NULL)
84 || (name_len <= old_len)
85 || ((name[old_len] == '/' && name[old_len + 1] == '\0'))
87 mf_fo_loge("invaild args - name : [%s], old_root : [%s]", name, old_root);
91 base = name + old_len;
92 if (name[old_len] == '/') {
96 n = g_string_new(new_root);
98 if (n->str[new_len - 1] == '/') {
99 g_string_append_printf(n, "%s", base);
101 g_string_append_printf(n, "/%s", base);
103 new_name = g_string_free(n, FALSE);
110 int _mf_copy_copy_regfile(const char *src, struct stat *src_statp, const char *dst, unsigned long buf_size, mf_cancel *cancel, _mf_fo_msg_cb msg_cb,
117 unsigned long alloc_size = DEF_ALLLOC_SIZE;
121 char err_buf[MF_ERR_BUF] = { 0, };
124 mf_fo_loge("check argument src");
125 err = MF_FO_ERR_SET(MF_FO_ERR_SRC_CLASS | MF_FO_ERR_ARGUMENT);
127 msg_cb(MF_MSG_ERROR, NULL, 0, err, msg_data);
132 mf_fo_loge("check argument dst");
133 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_ARGUMENT);
135 msg_cb(MF_MSG_ERROR, NULL, 0, err, msg_data);
141 src_mode = src_statp->st_mode;
143 struct stat src_info;
144 if (stat(src, &src_info)) {
145 mf_fo_loge("Fail to stat src file : %s", src);
146 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_SRC_CLASS);
148 msg_cb(MF_MSG_ERROR, src, 0, err, msg_data);
152 src_mode = src_info.st_mode;
156 if (!S_ISREG(src_mode)) {
157 mf_fo_loge("src[%s] is not regular file", src);
158 err = MF_FO_ERR_SET(MF_FO_ERR_SRC_CLASS | MF_FO_ERR_TYPE);
160 msg_cb(MF_MSG_ERROR, src, 0, err, msg_data);
164 src_f = fopen(src, "rb");
166 mf_fo_loge("Fail to fopen %s file", src);
167 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_SRC_CLASS);
169 msg_cb(MF_MSG_ERROR, src, 0, err, msg_data);
174 dst_f = fopen(dst, "wb");
176 mf_fo_loge("Fail to fopen %s file", dst);
177 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_DST_CLASS);
179 msg_cb(MF_MSG_ERROR, dst, 0, err, msg_data);
184 struct stat dst_info;
185 if (stat(dst, &dst_info)) {
186 MF_FILE_ERROR_LOG(err_buf, "Fail to stat dst file", dst);
187 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_DST_CLASS);
189 msg_cb(MF_MSG_ERROR, dst, 0, err, msg_data);
193 if (dst_info.st_blksize > 0) {
194 alloc_size = dst_info.st_blksize;
197 alloc_size = buf_size;
199 alloc_size = DEF_ALLLOC_SIZE;
201 buf = malloc(alloc_size);
203 mf_fo_loge("fail to alloc buf, alloc_size : %lu", alloc_size);
204 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_MEM);
206 msg_cb(MF_MSG_ERROR, dst, 0, err, msg_data);
212 ssize_t msg_size = 0;
214 struct _mf_copy_handle_intenal *cp_handle = NULL;
215 cp_handle = (struct _mf_copy_handle_intenal *)msg_data;
216 mf_debug("cp_handle->msg.total_size=%lld", cp_handle->msg.total_size);
217 int dynamic_size = 0;
218 if (cp_handle->msg.total_size > PER_HANDLE_MAX_SIZE) {
220 } else if (cp_handle->msg.total_size <= PER_HANDLE_MAX_SIZE &&
221 cp_handle->msg.total_size > PER_HANDLE_MIN_SIZE) {
227 while ((r_size = fread(buf, 1, alloc_size, src_f)) > 0)
229 ssize_t total = r_size;
235 w_size = fwrite(buf_p, 1, total, dst_f);
236 if (ferror(dst_f) != 0 || (r_size != w_size)) {
237 MF_FILE_ERROR_LOG(err_buf, "fail to write", dst_f);
238 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_DST_CLASS);
240 msg_cb(MF_MSG_ERROR, dst, 0, err, msg_data);
248 if (cancel && mf_cancel_check_cancel(cancel)) {
249 goto CANCEL_CLOSE_FD;
252 if (count == dynamic_size && msg_cb) {
254 msg_cb(MF_MSG_DOING, src, msg_size, 0, msg_data);
260 count = count % dynamic_size;
264 if (msg_size > 0 && msg_cb) {
266 msg_cb(MF_MSG_DOING, src, msg_size, 0, msg_data);
274 mf_fo_logd("success to copy file %s to %s", src, dst);
312 static int __get_copy_directory_hierarchies( const char *pathname, const struct stat *statptr, int type)
314 mf_fo_dir_list_info *info = NULL;
315 mf_debug("pathname is [%s]\t type is [%d]\t",
321 info = calloc(sizeof(mf_fo_dir_list_info), 1);
322 info->ftw_path = g_strdup(pathname);
324 copy_list = g_slist_append(copy_list, info);
325 mf_debug("File pathname is [%s]", pathname);
328 info = calloc(sizeof(mf_fo_dir_list_info), 1);
329 info->ftw_path = g_strdup(pathname);
331 copy_list = g_slist_append(copy_list, info);
332 mf_debug("Directory pathname is [%s]", pathname);
336 mf_debug("Default pathname is [%s]", pathname);
342 int _mf_copy_copy_directory(const char *src, struct stat *src_statp, const char *dst, mf_cancel *cancel, _mf_fo_msg_cb msg_cb, void *msg_data)
347 char err_buf[MF_ERR_BUF] = { 0, };
349 mf_fo_loge("check argument src");
350 err = MF_FO_ERR_SET(MF_FO_ERR_SRC_CLASS | MF_FO_ERR_ARGUMENT);
352 msg_cb(MF_MSG_ERROR, NULL, 0, err, msg_data);
357 mf_fo_loge("check argument dst");
358 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_ARGUMENT);
360 msg_cb(MF_MSG_ERROR, NULL, 0, err, msg_data);
366 src_mode = src_statp->st_mode;
368 struct stat src_info;
369 if (stat(src, &src_info)) {
370 mf_fo_loge("Fail to stat src file : %s", src);
371 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_SRC_CLASS);
373 msg_cb(MF_MSG_ERROR, src, 0, err, msg_data);
377 src_mode = src_info.st_mode;
380 if (access(dst, F_OK)) {
381 if (mkdir(dst, (src_mode & DIR_MODE_BIT))) {
382 MF_FILE_ERROR_LOG(err_buf, "Fail to make directory", dst);
383 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_DST_CLASS);
385 msg_cb(MF_MSG_ERROR, dst, 0, err, msg_data);
390 msg_cb(MF_MSG_DOING, dst, MF_VISUAL_FOLDER_SIZE, 0, msg_data);
394 mf_fo_logd("directory[%s] is already existed", dst);
397 ret = ftw(src, __get_copy_directory_hierarchies, 16);
400 mf_fo_dir_list_info *ent = NULL;
404 if (cancel && mf_cancel_check_cancel(cancel)) {
407 ent = (mf_fo_dir_list_info *)list->data;
408 mf_debug("name is [%s] type is [%d]", ent->ftw_path, ent->type);
409 if (ent->type == FTW_D) {
410 if (ent->ftw_path == NULL || strlen(ent->ftw_path) == 0) {
411 list = g_slist_next(list);
414 if (g_strcmp0(ent->ftw_path, src) == 0) {
415 list = g_slist_next(list);
418 char *new_dir = __mf_copy_change_root_name(ent->ftw_path, src, dst);
419 mf_fo_logi("copy dir %s to %s", ent->ftw_path, new_dir);
421 if (!_mf_fo_check_exist(new_dir)) {
423 if (stat(ent->ftw_path, &info) == 0) {
424 if (mkdir(new_dir, (info.st_mode & DIR_MODE_BIT))) {
425 /* fts_set(fts, ent, FTS_SKIP); */
426 mf_fo_loge("Fail to make directory [%s]", new_dir);
427 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_DST_CLASS);
429 msg_cb(MF_MSG_ERROR, new_dir, 0, err, msg_data);
435 msg_cb(MF_MSG_DOING, ent->ftw_path, 0, 0, msg_data);
439 MF_FILE_ERROR_LOG(err_buf, "Fail to stat ", ent->ftw_path);
440 /* fts_set(fts, ent, FTS_SKIP); */
441 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_SRC_CLASS);
443 msg_cb(MF_MSG_ERROR, ent->ftw_path, 0, err, msg_data);
449 struct stat new_dst_info;
450 if (stat(new_dir, &new_dst_info) == 0) {
451 if (S_ISDIR(new_dst_info.st_mode)) {
453 msg_cb(MF_MSG_DOING, ent->ftw_path, 0, 0, msg_data);
456 mf_fo_loge("[%s] is already existed, and this one is not directory", new_dir);
457 /*set FTS_SKIP to skip children of current*/
458 /*fts_set(fts, ent, FTS_SKIP);*/
459 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_ARGUMENT);
461 msg_cb(MF_MSG_ERROR, new_dir, 0, err, msg_data);
467 MF_FILE_ERROR_LOG(err_buf, "Fail to stat ", new_dir);
468 /*fts_set(fts, ent, FTS_SKIP);*/
469 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_DST_CLASS);
471 msg_cb(MF_MSG_ERROR, new_dir, 0, err, msg_data);
479 err = MF_FO_ERR_SET(MF_FO_ERR_COMMON_CLASS | MF_FO_ERR_MEM);
481 msg_cb(MF_MSG_ERROR, NULL, 0, err, msg_data);
485 } else if (ent->type == FTW_F) {
486 if (ent->ftw_path == NULL || strlen(ent->ftw_path) == 0) {
487 list = g_slist_next(list);
490 char *new_file = __mf_copy_change_root_name(ent->ftw_path, src, dst);
492 err = _mf_copy_copy_regfile(ent->ftw_path, NULL, new_file, 0, cancel, msg_cb, msg_data);
494 mf_media_content_scan_file(new_file);
499 } else if (err < 0) {
503 err = MF_FO_ERR_SET(MF_FO_ERR_COMMON_CLASS | MF_FO_ERR_MEM);
505 msg_cb(MF_MSG_ERROR, NULL, 0, err, msg_data);
510 mf_debug("ent->path is [%s]", ent->ftw_path);
511 list = g_slist_next(list);
515 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_SRC_CLASS);
517 msg_cb(MF_MSG_ERROR, src, 0, err, msg_data);
519 _mf_fo_free_directory_hierarchies(©_list);
523 _mf_fo_free_directory_hierarchies(©_list);
527 _mf_fo_free_directory_hierarchies(©_list);
531 _mf_fo_free_directory_hierarchies(©_list);
535 int _mf_copy_copy_internal(const char *src, const char *dst_dir, mf_cancel *cancel, _mf_fo_msg_cb msg_func, mf_req_callback req_func, void *msg_data)
538 char *src_basename = NULL;
539 char *new_dst = NULL;
540 char *next_name = NULL;
544 int alloc_size = 1; /*for null*/
545 struct stat src_info;
546 char err_buf[MF_ERR_BUF] = { 0, };
548 if (!src || strlen(src) <= 1) {
549 err = MF_FO_ERR_SET(MF_FO_ERR_SRC_CLASS | MF_FO_ERR_ARGUMENT);
551 msg_func(MF_MSG_ERROR, NULL, 0, err, msg_data);
557 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_ARGUMENT);
559 msg_func(MF_MSG_ERROR, NULL, 0, err, msg_data);
564 if (access(dst_dir, R_OK | W_OK)) {
565 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_REPORT_CLASS | MF_FO_ERR_PERMISSION);
567 msg_func(MF_MSG_ERROR, dst_dir, 0, err, msg_data);
572 if (stat(src, &src_info)) {
573 MF_FILE_ERROR_LOG(err_buf, "Fail to stat src ", src);
574 err = (_mf_fo_errno_to_mferr(errno) | MF_FO_ERR_SRC_CLASS);
576 msg_func(MF_MSG_ERROR, src, 0, err, msg_data);
581 if (S_ISDIR(src_info.st_mode)) {
582 if (g_strcmp0(dst_dir, src) == 0) {
583 mf_fo_loge("dst is child of src - src : %s, dst : %s", src, dst_dir);
584 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_REPORT_CLASS | MF_FO_ERR_ARGUMENT);
586 msg_func(MF_MSG_ERROR, dst_dir, 0, err, msg_data);
591 src_basename = g_path_get_basename(src);
593 mf_fo_loge("fail to get basename from src[%s]", src);
594 err = MF_FO_ERR_SET(MF_FO_ERR_COMMON_CLASS | MF_FO_ERR_MEM);
596 msg_func(MF_MSG_ERROR, NULL, 0, err, msg_data);
601 base_size = strlen(src_basename);
602 root_size = strlen(dst_dir);
604 if (dst_dir[root_size - 1] != '/') {
609 alloc_size += (base_size + root_size);
611 new_dst = malloc(sizeof(char) * (alloc_size));
613 mf_fo_loge("fail to alloc new dst");
614 err = MF_FO_ERR_SET(MF_FO_ERR_COMMON_CLASS | MF_FO_ERR_MEM);
616 msg_func(MF_MSG_ERROR, NULL, 0, err, msg_data);
622 snprintf(new_dst, alloc_size, "%s%s", dst_dir, src_basename);
624 snprintf(new_dst, alloc_size, "%s/%s", dst_dir, src_basename);
626 SAFE_FREE(src_basename);
628 if (cancel && mf_cancel_check_cancel(cancel)) {
629 goto CANCEL_FREE_MEM;
632 if (access(new_dst, F_OK)) {
633 if (S_ISDIR(src_info.st_mode)) {
634 err = _mf_copy_copy_directory(src, &src_info, new_dst, cancel, msg_func, msg_data);
636 mf_media_content_scan_folder(new_dst);
638 } else if (S_ISREG(src_info.st_mode)) {
640 err = _mf_copy_copy_regfile(src, &src_info, new_dst, 0, cancel, msg_func, msg_data);
642 mf_media_content_scan_file(new_dst);
645 mf_fo_loge("item[%s] is not file or directory", src);
646 err = MF_FO_ERR_SET(MF_FO_ERR_SRC_CLASS | MF_FO_ERR_TYPE | MF_FO_ERR_REPORT_CLASS);
648 msg_func(MF_MSG_ERROR, src, 0, err, msg_data);
653 mf_request_type result = MF_REQ_NONE;
655 mf_fo_request *req = mf_request_new();
657 mf_request_set_path(req, new_dst);
658 mf_fo_logd("~~~~~~ waiting for request");
659 req_func(req, msg_data);
660 result = mf_request_get_result(req);
661 mf_fo_logd("~~~~~~ get request : %d", result);
662 if (result == MF_REQ_RENAME) {
663 next_name = mf_request_get_new_name(req);
665 mf_request_free(req);
667 mf_fo_loge("Fail to alloc request");
668 err = MF_FO_ERR_SET(MF_FO_ERR_COMMON_CLASS | MF_FO_ERR_MEM);
670 msg_func(MF_MSG_ERROR, NULL, 0, err, msg_data);
680 struct stat dst_info;
681 if (stat(new_dst, &dst_info)) {
682 MF_FILE_ERROR_LOG(err_buf, "Fail to stat new_dst ", new_dst);
684 msg_func(MF_MSG_ERROR, NULL, 0, errno, msg_data);
689 if (S_ISDIR(src_info.st_mode)) {
690 if (!S_ISDIR(dst_info.st_mode)) {
691 mf_fo_loge("src[%s] is directory, but dst[%s] is already existed and not a directory", src, new_dst);
693 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_TYPE);
695 msg_func(MF_MSG_ERROR, new_dst, 0, err, msg_data);
700 err = _mf_copy_copy_directory(src, &src_info, new_dst, cancel, msg_func, msg_data);
702 mf_media_content_scan_folder(new_dst);
704 } else if (S_ISREG(src_info.st_mode)) {
705 if (!S_ISREG(dst_info.st_mode)) {
706 mf_fo_loge("src[%s] is file, but dst[%s] is already existed and not a file", src, new_dst);
707 err = MF_FO_ERR_SET(MF_FO_ERR_DST_CLASS | MF_FO_ERR_TYPE);
709 msg_func(MF_MSG_ERROR, new_dst, 0, err, msg_data);
714 err = _mf_copy_copy_regfile(src, &src_info, new_dst, dst_info.st_blksize, cancel, msg_func, msg_data);
716 mf_media_content_scan_file(new_dst);
725 if (S_ISDIR(src_info.st_mode)) {
726 err = _mf_copy_copy_directory(src, &src_info, next_name, cancel, msg_func, msg_data);
728 mf_media_content_scan_folder(next_name);
730 } else if (S_ISREG(src_info.st_mode)) {
731 err = _mf_copy_copy_regfile(src, &src_info, next_name, 0, cancel, msg_func, msg_data);
733 mf_media_content_scan_file(next_name);
736 SAFE_FREE(next_name);
738 if (S_ISDIR(src_info.st_mode)) {
740 next_name = _mf_fo_get_next_unique_dirname(new_dst, &errcode);
742 mf_fo_loge("Fail to get next directory name [%s]", new_dst);
743 err = (_mf_fo_errno_to_mferr(errcode) | MF_FO_ERR_DST_CLASS);
745 msg_func(MF_MSG_ERROR, NULL, 0, err, msg_data);
749 err = _mf_copy_copy_directory(src, &src_info, next_name, cancel, msg_func, msg_data);
751 mf_media_content_scan_folder(next_name);
753 } else if (S_ISREG(src_info.st_mode)) {
755 next_name = _mf_fo_get_next_unique_filename(new_dst, &errcode);
757 mf_fo_loge("Fail to get next file name [%s]", new_dst);
758 err = (_mf_fo_errno_to_mferr(errcode) | MF_FO_ERR_DST_CLASS);
760 msg_func(MF_MSG_ERROR, NULL, 0, err, msg_data);
764 err = _mf_copy_copy_regfile(src, &src_info, next_name, 0, cancel, msg_func, msg_data);
766 mf_media_content_scan_file(next_name);
769 SAFE_FREE(next_name);
776 unsigned long long size = 0;
777 /*1 TODO : Do i need to report error, if i fail to get size ?*/
778 _mf_fo_get_total_item_size(src, &size);
779 msg_func(MF_MSG_SKIP, NULL, size, 0, msg_data);
786 mf_cancel_do_cancel(cancel);
788 goto CANCEL_FREE_MEM;
800 goto CANCEL_FREE_MEM;
801 } else if (err < 0) {
808 mf_fo_logi("Copy error");
809 SAFE_FREE(src_basename);
816 mf_fo_logi("Copy cancelled");
818 SAFE_FREE(next_name);