cleaner code - move file cp to own func etc.

This commit is contained in:
Carsten Haitzler 2024-04-30 17:07:37 +01:00
parent f603296834
commit 3519ab0f91
1 changed files with 153 additions and 139 deletions

View File

@ -15,22 +15,30 @@
#include "status.h"
#include "fs.h"
// generic error handler. special case handling all errnos for everything is
// pretty insane - so handle non-errors in switches and otherwise pass to
// this tio handle the reast in a generic way.
static void
_error_handle(const char *src, const char *dst, const char *op, int errno_in)
static Eina_Strbuf *
error_strbuf_new(const char *op)
{
Eina_Strbuf *buf = eina_strbuf_new();
if (!buf) abort();
eina_strbuf_append(buf, op);
eina_strbuf_append(buf, ": ");
#define HNDL(_err, _str) \
_err: \
eina_strbuf_append(buf, _str); \
status_error(src, dst, eina_strbuf_string_get(buf)); \
break
return buf;
}
// generic error handler. special case handling all errnos for everything is
// pretty insane - so handle non-errors in switches and otherwise pass to
// this tio handle the reast in a generic way.
static void
_error_handle(const char *src, const char *dst, const char *op, int errno_in)
{
Eina_Strbuf *buf = error_strbuf_new(op);
#define HNDL(_err, _str) \
_err: \
eina_strbuf_append(buf, _str); \
status_error(src, dst, eina_strbuf_string_get(buf)); \
break
switch (errno_in)
{
HNDL(case EACCES, "Access denied");
@ -61,6 +69,120 @@ _error_handle(const char *src, const char *dst, const char *op, int errno_in)
eina_strbuf_free(buf);
}
static void
error_ok_pos(const char *src, const char *op, const char *str)
{
Eina_Strbuf *buf = error_strbuf_new(op);
eina_strbuf_append(buf, str);
status_pos(1, eina_strbuf_string_get(buf));
eina_strbuf_free(buf);
}
static Eina_Bool
fs_cp_file(const char *src, const char *dst, const char *op, struct stat src_st)
{
// copy a normal file from src to dst - use optimized copy range if possible
// and fall abck to read + write into userspace buffer otherwise. use the
// struct stat mode passed in for created file. return true if fully
// successful or false otherwise
int fd_in, fd_ou;
void *old_copy_buf = NULL;
Eina_Bool res = EINA_TRUE;
fd_in = open(src, O_RDONLY);
fd_ou = open(dst, O_WRONLY | O_CREAT, src_st.st_mode);
if ((fd_in >= 0) && (fd_ou >= 0))
{
ssize_t size = 1 * 1024 * 1024; // 1mb default for copy range
ssize_t ret, ret2;
off_t off_in = 0, off_ou = 0;
Eina_Bool old_copy = EINA_FALSE;
for (;;)
{
if (old_copy)
{
if (!old_copy_buf)
{
size = 256 * 1024; // drop to 256k buffer for r+w
old_copy_buf = malloc(size);
if (!old_copy_buf)
{
res = EINA_FALSE;
goto err;
}
}
again_read:
ret = read(fd_in, old_copy_buf, size);
if (ret < 0)
{
switch (errno)
{
case EAGAIN:
case EINTR:
goto again_read;
default:
_error_handle(src, NULL, op, errno);
res = EINA_FALSE;
goto err;
}
}
else
{
off_in += ret;
again_write:
ret2 = write(fd_ou, old_copy_buf, ret);
if (ret2 < 0)
{
switch (errno)
{
case EAGAIN:
case EINTR:
goto again_write;
default:
_error_handle(NULL, dst, op, errno);
res = EINA_FALSE;
goto err;
}
}
else if (ret2 == ret)
{
off_ou += ret;
if (ret < size) break; // end of file
}
}
}
else
{
ret = copy_file_range(fd_in, &off_in, fd_ou, &off_ou, size, 0);
if (ret < 0)
{
switch (errno)
{
case EOPNOTSUPP:
case EXDEV:
// fall back to old read+write copy into userspace buf
old_copy = EINA_TRUE;
break;
default:
_error_handle(src, dst, op, errno);
res = EINA_FALSE;
goto err;
}
}
else if (ret < size) break; // end of file
}
}
}
else res = EINA_FALSE;
err:
if (old_copy_buf) free(old_copy_buf);
if (fd_in >= 0) close(fd_in);
if (fd_ou >= 0) close(fd_ou);
return res;
}
// this scans a tree to build a potential operation progress count. it may
// not be 100% right as the fs can change while the scan happens and after
// so any move that devolves into a cp + rm isn't going to be atomic and
@ -94,10 +216,7 @@ fs_scan(const char *src)
{
EINA_ITERATOR_FOREACH(it, s)
{
if (res)
{
if (!fs_scan(s)) res = EINA_FALSE;
}
if ((res) && (!fs_scan(s))) res = EINA_FALSE;
eina_stringshare_del(s);
}
eina_iterator_free(it);
@ -125,9 +244,8 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
mode_t old_umask;
struct timeval times[2];
const char *op = "";
Eina_Strbuf *sbuf = NULL;
if (strlen(src) < 1) return EINA_FALSE;
if ((!src) || (!dst) || (strlen(src) < 1)) return EINA_FALSE;
// first count how much work needs doing
if (!fs_scan(src)) return EINA_FALSE;
@ -142,11 +260,7 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
switch (errno)
{
case ENOENT: // ignore this error - file removed during scan ?
eina_strbuf_reset(sbuf);
eina_strbuf_append(sbuf, op);
eina_strbuf_append(sbuf, ": ");
eina_strbuf_append(sbuf, "File vanished");
status_pos(1, eina_strbuf_string_get(sbuf));
error_ok_pos(src, op, "File vanished");
goto err;
default:
_error_handle(src, dst, op, errno);
@ -154,7 +268,6 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
goto err;
}
}
sbuf = eina_strbuf_new();
if (S_ISDIR(st.st_mode))
{ // it's a dir - scan this recursively
if (cp)
@ -165,7 +278,7 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
{
case EEXIST: // ignore - mv would mv over this anyway
break;
default: // WAT
default:
_error_handle(NULL, dst, op, errno);
res = EINA_FALSE;
goto err;
@ -175,15 +288,18 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
it = eina_file_ls(src);
if (it)
{
Eina_Strbuf *buf = eina_strbuf_new();
if (!buf) abort();
EINA_ITERATOR_FOREACH(it, s)
{
Eina_Strbuf *buf = eina_strbuf_new();
const char *fs = ecore_file_file_get(s);
if (buf)
{
if ((res) && (fs))
{
eina_strbuf_reset(buf);
eina_strbuf_append(buf, dst);
eina_strbuf_append(buf, "/");
eina_strbuf_append(buf, fs);
@ -191,11 +307,11 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
report_err, cp, rm))
res = EINA_FALSE;
}
eina_strbuf_free(buf);
}
eina_stringshare_del(s);
}
eina_iterator_free(it);
eina_strbuf_free(buf);
}
}
else if (S_ISLNK(st.st_mode))
@ -210,11 +326,7 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
{
if (symlink(link, dst) < 0)
{ // soft error? e.g. mv on FAT fs? report but move on
eina_strbuf_reset(sbuf);
eina_strbuf_append(sbuf, op);
eina_strbuf_append(sbuf, ": ");
eina_strbuf_append(sbuf, "Error creating symlink");
status_pos(1, eina_strbuf_string_get(sbuf));
error_ok_pos(dst, op, "Error creating symlink");
}
}
else if (lnsz < 0)
@ -222,24 +334,16 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
switch (errno)
{
case ENOENT: // ignore this error - file removed during scan ?
eina_strbuf_reset(sbuf);
eina_strbuf_append(sbuf, op);
eina_strbuf_append(sbuf, ": ");
eina_strbuf_append(sbuf, "File vanished");
status_pos(1, eina_strbuf_string_get(sbuf));
error_ok_pos(src, op, "File vanished");
break;
default:
_error_handle(src, dst, op, errno);
return EINA_FALSE;
}
}
else // 0 sized symlink ... WAT?
{
eina_strbuf_reset(sbuf);
eina_strbuf_append(sbuf, op);
eina_strbuf_append(sbuf, ": ");
eina_strbuf_append(sbuf, "Zero sized symlink");
status_error(src, NULL, eina_strbuf_string_get(sbuf));
else
{ // 0 sized symlink ... WAT?
error_ok_pos(src, op, "Zero sized symlink");
}
}
}
@ -255,8 +359,7 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
else if (S_ISSOCK(st.st_mode))
{
if (cp)
{
// we can't just make sockets - so ignore but document here
{ // we can't just make sockets - so ignore but document here
}
}
else if ((S_ISCHR(st.st_mode)) || (S_ISBLK(st.st_mode)))
@ -272,99 +375,7 @@ fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err, Eina_Bool cp,
{
if (cp)
{
int fd_in, fd_ou;
void *old_copy_buf = NULL;
fd_in = open(src, O_RDONLY);
fd_ou = open(dst, O_WRONLY | O_CREAT, st.st_mode);
if ((fd_in >= 0) && (fd_ou >= 0))
{
ssize_t size = 1 * 1024 * 1024; // 1mb default
ssize_t ret, ret2;
off_t off_in = 0, off_ou = 0;
Eina_Bool old_copy = EINA_FALSE;
for (;;)
{
if (old_copy)
{
if (!old_copy_buf)
{
size = 256 * 1024; // drop to 256k buffer
old_copy_buf = malloc(size);
if (!old_copy_buf)
{
res = EINA_FALSE;
goto err_copy;
}
}
again_read:
ret = read(fd_in, old_copy_buf, size);
if (ret < 0)
{
switch (errno)
{
case EAGAIN:
case EINTR:
goto again_read;
default:
_error_handle(src, NULL, op, errno);
res = EINA_FALSE;
goto err_copy;
}
}
else
{
off_in += ret;
again_write:
ret2 = write(fd_ou, old_copy_buf, ret);
if (ret2 < 0)
{
switch (errno)
{
case EAGAIN:
case EINTR:
goto again_write;
default:
_error_handle(NULL, dst, op, errno);
res = EINA_FALSE;
goto err_copy;
}
}
else if (ret2 == ret)
{
off_ou += ret;
if (ret < size) break; // end of file
}
}
}
else
{
ret = copy_file_range(fd_in, &off_in, fd_ou, &off_ou,
size, 0);
if (ret < 0)
{
switch (errno)
{
case EOPNOTSUPP:
case EXDEV:
old_copy = EINA_TRUE;
break;
case EBADF:
default: // WAT
_error_handle(src, dst, op, errno);
res = EINA_FALSE;
goto err_copy;
}
}
else if (ret < size) break; // end of file
}
}
}
err_copy:
if (old_copy_buf) free(old_copy_buf);
if (fd_in >= 0) close(fd_in);
if (fd_ou >= 0) close(fd_ou);
res = fs_cp_file(src, dst, op, st);
}
}
if ((rm) && (res))
@ -380,9 +391,12 @@ err_copy:
default:
_error_handle(src, NULL, op, errno);
res = EINA_FALSE;
goto err;
goto err_unlink;
}
}
}
else
{
if (unlink(src) != 0)
{
switch (errno)
@ -399,6 +413,7 @@ err_copy:
}
err_unlink:
chown(dst, st.st_uid, st.st_gid); // ignore err
// duplicate mtime+atime from src down to msic/nsec if possible
#ifdef STAT_NSEC
#ifdef st_mtime
#define STAT_NSEC_ATIME(st) (unsigned long long)((st)->st_atim.tv_nsec)
@ -421,7 +436,6 @@ err_unlink:
utimes(dst, times); // ingore err
err:
umask(old_umask);
if (sbuf) eina_strbuf_free(sbuf);
return res;
}