diff --git a/legacy/eio/src/lib/Makefile.am b/legacy/eio/src/lib/Makefile.am index 5729bf7ea6..2f1347a998 100644 --- a/legacy/eio/src/lib/Makefile.am +++ b/legacy/eio/src/lib/Makefile.am @@ -12,7 +12,8 @@ includesdir = $(includedir)/eio-@VMAJ@ libeio_la_SOURCES = \ eio_file.c \ eio_main.c \ -eio_single.c +eio_single.c \ +eio_dir.c libeio_la_LIBADD = @EIO_LIBS@ libeio_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ @release_info@ diff --git a/legacy/eio/src/lib/eio_dir.c b/legacy/eio/src/lib/eio_dir.c new file mode 100644 index 0000000000..01151577a1 --- /dev/null +++ b/legacy/eio/src/lib/eio_dir.c @@ -0,0 +1,328 @@ +/* EIO - EFL data type library + * Copyright (C) 2010 Enlightenment Developers: + * Cedric Bail + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#define _FILE_OFFSET_BITS 64 +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#ifndef _MSC_VER +# include +# include +#endif + +#ifdef HAVE_FEATURES_H +# include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "eio_private.h" + +#include "Eio.h" + +static int +eio_strcmp(const void *a, const void *b) +{ + return strcmp(a, b); +} + +static Eina_Bool +_eio_dir_recursiv_ls(Ecore_Thread *thread, Eio_Dir_Copy *copy, const char *target) +{ + const Eina_File_Direct_Info *info; + Eina_Iterator *it; + Eina_List *dirs = NULL; + Eina_List *l; + const char *dir; + struct stat buffer; + + it = eina_file_direct_ls(target); + if (!it) + { + eio_file_thread_error(©->progress.common); + return EINA_FALSE; + } + + EINA_ITERATOR_FOREACH(it, info) + { + fprintf(stderr, "entry: %s\n", info->path); + switch (info->dirent->d_type) + { + case DT_UNKNOWN: + if (stat(info->path, &buffer) != 0) + { + eio_file_thread_error(©->progress.common); + goto on_error; + } + + if (S_ISDIR(buffer.st_mode)) + dirs = eina_list_append(dirs, eina_stringshare_add(info->path)); + else + copy->files = eina_list_append(copy->files, eina_stringshare_add(info->path)); + break; + case DT_DIR: + dirs = eina_list_append(dirs, eina_stringshare_add(info->path)); + break; + default: + copy->files = eina_list_append(copy->files, eina_stringshare_add(info->path)); + break; + } + + if (ecore_thread_check(thread)) + goto on_error; + } + + eina_iterator_free(it); + + EINA_LIST_FOREACH(dirs, l, dir) + if (!_eio_dir_recursiv_ls(thread, copy, dir)) + { + EINA_LIST_FREE(dirs, dir) + eina_stringshare_del(dir); + return EINA_FALSE; + } + + copy->dirs = eina_list_merge(copy->dirs, dirs); + return EINA_TRUE; + + on_error: + eina_iterator_free(it); + + EINA_LIST_FREE(dirs, dir) + eina_stringshare_del(dir); + + return EINA_FALSE; +} + +static void +_eio_dir_copy_heavy(Ecore_Thread *thread, void *data) +{ + Eio_Dir_Copy *copy = data; + const char *file = NULL; + const char *dir = NULL; + + Eio_File_Progress file_copy; + char target[PATH_MAX]; + struct stat buffer; + + int length_source; + int length_dest; + off_t count; + off_t step; + + /* list all the content that should be copied */ + if (!_eio_dir_recursiv_ls(thread, copy, copy->progress.source)) + return ; + + /* notify main thread of the amount of work todo */ + step = 0; + count = eina_list_count(copy->files) + eina_list_count(copy->dirs); + eio_progress_send(thread, ©->progress, step, count); + + /* sort the content, so we create the directory in the right order */ + copy->dirs = eina_list_sort(copy->dirs, -1, eio_strcmp); + copy->files = eina_list_sort(copy->files, -1, eio_strcmp); + + /* prepare stuff */ + length_source = eina_stringshare_strlen(copy->progress.source); + length_dest = eina_stringshare_strlen(copy->progress.dest); + + memcpy(&file_copy, ©->progress, sizeof (Eio_File_Progress)); + file_copy.op = EIO_FILE_COPY; + file_copy.source = NULL; + file_copy.dest = NULL; + + /* create destination dir if not available */ + if (stat(copy->progress.dest, &buffer) != 0) + { + if (stat(copy->progress.source, &buffer) != 0) + goto on_error; + + if (mkdir(copy->progress.dest, buffer.st_mode) != 0) + goto on_error; + } + + /* create all directory */ + EINA_LIST_FREE(copy->dirs, dir) + { + /* build target dir path */ + strcpy(target, copy->progress.dest); + target[length_dest] = '/'; + strcpy(target + length_dest + 1, dir + length_source); + + /* stat the original file for mode info */ + /* FIXME: in some case we already did a stat call, so would be nice to reuse previous result here */ + /* FIXME: apply mode later so that readonly could be copied and property will be set correctly */ + if (stat(dir, &buffer) != 0) + goto on_error; + + /* create the directory */ + if (mkdir(target, buffer.st_mode) != 0) + goto on_error; + + step++; + eio_progress_send(thread, ©->progress, step, count); + + if (ecore_thread_check(thread)) + goto on_error; + + eina_stringshare_del(dir); + } + dir = NULL; + + /* Copy all files */ + EINA_LIST_FREE(copy->files, file) + { + /* build target dir path */ + strcpy(target, copy->progress.dest); + target[length_dest] = '/'; + strcpy(target + length_dest + 1, file + length_source); + + file_copy.source = file; + file_copy.dest = eina_stringshare_add(target); + + if (!eio_file_copy_do(thread, &file_copy)) + { + copy->progress.common.error = file_copy.common.error; + goto on_error; + } + + step++; + eio_progress_send(thread, ©->progress, step, count); + + if (ecore_thread_check(thread)) + goto on_error; + + eina_stringshare_del(file_copy.dest); + eina_stringshare_del(file); + } + file_copy.dest = NULL; + file = NULL; + + on_error: + /* cleanup the mess */ + if (file_copy.dest) eina_stringshare_del(file_copy.dest); + if (dir) eina_stringshare_del(dir); + if (file) eina_stringshare_del(file); + + EINA_LIST_FREE(copy->files, file) + eina_stringshare_del(file); + EINA_LIST_FREE(copy->dirs, dir) + eina_stringshare_del(dir); + + if (!ecore_thread_check(thread)) + eio_progress_send(thread, ©->progress, count, count); + + return ; +} + +static void +_eio_dir_copy_notify( __UNUSED__ Ecore_Thread *thread, void *msg_data, void *data) +{ + Eio_Dir_Copy *copy = data; + Eio_Progress *progress = msg_data; + + eio_progress_cb(progress, ©->progress); +} + +static void +_eio_dir_copy_end(void *data) +{ + Eio_Dir_Copy *copy = data; + + copy->progress.common.done_cb((void*) copy->progress.common.data); + + eina_stringshare_del(copy->progress.source); + eina_stringshare_del(copy->progress.dest); + free(copy); +} + +static void +_eio_dir_copy_error(void *data) +{ + Eio_Dir_Copy *copy = data; + + eio_file_error(©->progress.common); + + eina_stringshare_del(copy->progress.source); + eina_stringshare_del(copy->progress.dest); + free(copy); +} + +/** + * @brief Copy a directory and it's content asynchronously + * @param source Should be the name of the directory to copy the data from. + * @param dest Should be the name of the directory to copy the data to. + * @param progress_cb Callback called to know the progress of the copy. + * @param done_cb Callback called when the copy is done. + * @param error_cb Callback called when something goes wrong. + * @param data Private data given to callback. + * + * This function will copy a directory and all it's content from source to dest. + * It will try to use splice if possible, if not it will fallback to mmap/write. + * It will try to preserve access right, but not user/group identification. + */ +EAPI Eio_File * +eio_dir_copy(const char *source, + const char *dest, + Eio_Progress_Cb progress_cb, + Eio_Done_Cb done_cb, + Eio_Error_Cb error_cb, + const void *data) +{ + Eio_Dir_Copy *copy = NULL; + + if (!source || !dest || !done_cb || !error_cb) + return NULL; + + copy = malloc(sizeof (Eio_Dir_Copy)); + if (!copy) return NULL; + + copy->progress.op = EIO_DIR_COPY; + copy->progress.progress_cb = progress_cb; + copy->progress.source = eina_stringshare_add(source); + copy->progress.dest = eina_stringshare_add(dest); + copy->files = NULL; + + if (!eio_long_file_set(©->progress.common, + done_cb, + error_cb, + data, + _eio_dir_copy_heavy, + _eio_dir_copy_notify, + _eio_dir_copy_end, + _eio_dir_copy_error)) + return NULL; + + return ©->progress.common; +} +