diff options
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | examples/Makefile.am | 1 | ||||
-rw-r--r-- | examples/rename.c | 60 | ||||
-rw-r--r-- | include/mailutils/util.h | 11 | ||||
-rw-r--r-- | libmailutils/base/Makefile.am | 2 | ||||
-rw-r--r-- | libmailutils/base/copyfile.c | 354 | ||||
-rw-r--r-- | libmailutils/base/renamefile.c | 39 |
7 files changed, 469 insertions, 2 deletions
@@ -1,11 +1,11 @@ -GNU mailutils NEWS -- history of user-visible changes. 2016-12-14 +GNU mailutils NEWS -- history of user-visible changes. 2016-12-15 Copyright (C) 2002-2016 Free Software Foundation, Inc. See the end of file for copying conditions. Please send mailutils bug reports to <bug-mailutils@gnu.org>. -Version 3.1.1 - (Git) +Version 3.1.1 - 2016-12-15 Bugfixes diff --git a/examples/Makefile.am b/examples/Makefile.am index 55b53df9a..ca5e0947a 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -44,6 +44,7 @@ noinst_PROGRAMS = \ murun\ musocio\ $(NNTPCLIENT)\ + rename\ sa\ sfrom diff --git a/examples/rename.c b/examples/rename.c new file mode 100644 index 000000000..f12264ac7 --- /dev/null +++ b/examples/rename.c @@ -0,0 +1,60 @@ +#include <mailutils/mailutils.h> + +int copy_option; +int owner_option; +int mode_option; + +static struct mu_option rename_options[] = { + { "copy", 'c', NULL, MU_OPTION_DEFAULT, + "copy the file", + mu_c_bool, ©_option }, + { "owner", 'u', NULL, MU_OPTION_DEFAULT, + "copy ownership", + mu_c_bool, &owner_option }, + { "mode", 'm', NULL, MU_OPTION_DEFAULT, + "copy mode", + mu_c_bool, &mode_option }, + MU_OPTION_END +}, *options[] = { rename_options, NULL }; + +struct mu_cli_setup cli = { + options, + NULL, + "copy or rename file", + "SRC DST" +}; + +static char *capa[] = { + "debug", + NULL +}; + +int +main (int argc, char **argv) +{ + int rc; + + mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv); + + if (argc != 2) + { + mu_error ("wrong number of arguments"); + return 1; + } + + if (copy_option) + { + int flags = (owner_option ? MU_COPY_OWNER : 0) + | (mode_option ? MU_COPY_MODE : 0); + rc = mu_copy_file (argv[0], argv[1], flags); + } + else + rc = mu_rename_file (argv[0], argv[1]); + + if (rc) + mu_diag_funcall (MU_DIAG_ERROR, "mu_rename_file", NULL, rc); + + return !!rc; +} + + diff --git a/include/mailutils/util.h b/include/mailutils/util.h index 60bc99c65..23792e26f 100644 --- a/include/mailutils/util.h +++ b/include/mailutils/util.h @@ -203,7 +203,18 @@ typedef enum mu_c_type mu_c_type_t; extern char const *mu_c_type_str[]; int mu_str_to_c (char const *string, mu_c_type_t type, void *tgt, char **errmsg); + + /* -------------------------- */ + /* Safe file copy and rename */ + /* -------------------------- */ +#define MU_COPY_MODE 0x01 +#define MU_COPY_OWNER 0x02 +#define MU_COPY_SYMLINK 0x04 +#define MU_COPY_FORCE 0x08 +int mu_copy_file (const char *srcpath, const char *dstpath, int flags); +int mu_rename_file (const char *oldpath, const char *newpath); + /* ----------------------- */ /* Assorted functions. */ /* ----------------------- */ diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am index f47705ddd..e16fb4489 100644 --- a/libmailutils/base/Makefile.am +++ b/libmailutils/base/Makefile.am @@ -24,6 +24,7 @@ libbase_la_SOURCES = \ argcvjoin.c\ argcvrem.c\ assoc.c\ + copyfile.c\ ctparse.c\ daemon.c\ filesafety.c\ @@ -57,6 +58,7 @@ libbase_la_SOURCES = \ permstr.c\ registrar.c\ refcount.c\ + renamefile.c\ rfc2047.c\ schemeauto.c\ sha1.c\ diff --git a/libmailutils/base/copyfile.c b/libmailutils/base/copyfile.c new file mode 100644 index 000000000..f65d59c0e --- /dev/null +++ b/libmailutils/base/copyfile.c @@ -0,0 +1,354 @@ +#include <config.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <stdlib.h> +#include <unistd.h> +#include <mailutils/stream.h> +#include <mailutils/util.h> +#include <mailutils/diag.h> +#include <mailutils/error.h> +#include <mailutils/errno.h> +#include <mailutils/nls.h> + +static int copy_regular_file (const char *srcpath, const char *dstpath, + int flags, struct stat *st); +static int copy_symlink (const char *srcpath, const char *dstpath); +static int copy_dir (const char *srcpath, const char *dstpath, int flags); + +int +mu_copy_file (const char *srcpath, const char *dstpath, int flags) +{ + int rc = 0; + struct stat st; + + if (((flags & MU_COPY_SYMLINK) ? lstat : stat) (srcpath, &st)) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("can't stat file %s: %s"), + srcpath, mu_strerror (errno))); + return errno; + } + + switch (st.st_mode & S_IFMT) + { + case S_IFREG: + return copy_regular_file (srcpath, dstpath, flags, &st); + + case S_IFLNK: + return copy_symlink (srcpath, dstpath); + break; + + case S_IFDIR: + return copy_dir (srcpath, dstpath, flags); + break; + + case S_IFBLK: + case S_IFCHR: + if (mknod (dstpath, st.st_mode & 0777, st.st_dev)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: cannot create node: %s"), + dstpath, + mu_strerror (rc))); + } + break; + + case S_IFIFO: + if (mkfifo (dstpath, st.st_mode & 0777)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: cannot create node: %s"), + dstpath, + mu_strerror (rc))); + } + break; + + default: + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: don't know how to copy file of that type"), + srcpath)); + return ENOTSUP; + } + + return rc; +} + +static int +copy_regular_file (const char *srcpath, const char *dstpath, int flags, + struct stat *st) +{ + int rc; + mu_stream_t src, dst; + mode_t mask, mode; + + rc = mu_file_stream_create (&src, srcpath, MU_STREAM_READ); + if (rc) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("cannot open source file %s: %s"), + srcpath, mu_strerror (rc))); + return rc; + } + + mask = umask (077); + mode = ((flags & MU_COPY_MODE) ? st->st_mode : (0666 & ~mask)) & 0777; + + rc = mu_file_stream_create (&dst, dstpath, MU_STREAM_CREAT|MU_STREAM_WRITE); + umask (mask); + if (rc) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("cannot open destination file %s: %s"), + dstpath, mu_strerror (rc))); + mu_stream_destroy (&src); + return rc; + } + + rc = mu_stream_copy (dst, src, 0, NULL); + if (rc) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("failed to copy %s to %s: %s"), + srcpath, dstpath, mu_strerror (rc))); + } + else + { + mu_transport_t trans[2]; + + rc = mu_stream_ioctl (dst, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, trans); + if (rc == 0) + { + if (fchmod ((int) trans[0], mode)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: cannot chmod: %s"), + dstpath, mu_strerror (rc))); + } + else if (flags & MU_COPY_OWNER) + { + uid_t uid; + gid_t gid; + + if (getuid () == 0) + { + uid = st->st_uid; + gid = st->st_gid; + } + else if (getuid () == st->st_uid) + { + uid = -1; + gid = st->st_gid; + } + else + { + uid = -1; + gid = -1; + } + + if (gid != -1) + { + if (fchown ((int) trans[0], uid, gid)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: cannot chown to %lu.%lu: %s"), + dstpath, + (unsigned long) uid, + (unsigned long) gid, + mu_strerror (rc))); + } + } + } + } + else + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("can't change file mode and ownership after copying %s to %s;" + " cannot get file handle: %s"), + srcpath, dstpath, + mu_strerror (rc))); + } + } + + mu_stream_destroy (&src); + mu_stream_destroy (&dst); + + return rc; +} + +static int +copy_symlink (const char *srcpath, const char *dstpath) +{ + int rc; + char *buf = NULL; + size_t size = 0; + + rc = mu_readlink (srcpath, &buf, &size, NULL); + if (rc) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: cannot read link: %s"), + srcpath, mu_strerror (rc))); + return rc; + } + + if (symlink (buf, dstpath)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: can't link %s to %s: %s"), + srcpath, buf, dstpath, mu_strerror (rc))); + } + free (buf); + return rc; +} + +static int +copy_dir (const char *srcpath, const char *dstpath, int flags) +{ + DIR *dirp; + struct dirent *dp; + struct stat st, st1; + int rc; + int create = 0; + mode_t mode, mask; + + if (stat (srcpath, &st)) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("can't stat file %s: %s"), + srcpath, mu_strerror (errno))); + return errno; + } + + if (stat (dstpath, &st1)) + { + if (errno == ENOENT) + create = 1; + else + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("can't stat directory %s: %s"), + dstpath, mu_strerror (errno))); + return errno; + } + } + else if (!S_ISDIR (st1.st_mode)) + { + if (flags & MU_COPY_FORCE) + { + if (unlink (dstpath)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s is not a directory and cannot be unlinked: %s"), + dstpath, mu_strerror (rc))); + return rc; + } + create = 1; + } + else + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s is not a directory"), + dstpath)); + return EEXIST; + } + } + + mask = umask (077); + mode = ((flags & MU_COPY_MODE) ? st.st_mode : (0777 & ~mask)) & 0777; + + if (create) + { + rc = mkdir (dstpath, 0700); + umask (mask); + + if (rc) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("can't create directory %s: %s"), + dstpath, mu_strerror (errno))); + return errno; + } + } + else + umask (mask); + + dirp = opendir (srcpath); + if (dirp == NULL) + { + mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, + ("cannot open directory %s: %s", + srcpath, mu_strerror (errno))); + return 1; + } + + while ((dp = readdir (dirp))) + { + char const *ename = dp->d_name; + char *src, *dst; + + if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0) + continue; + + src = mu_make_file_name (srcpath, ename); + dst = mu_make_file_name (dstpath, ename); + rc = mu_copy_file (src, dst, flags); + free (dst); + free (src); + + if (rc) + break; + } + closedir (dirp); + + if (chmod (dstpath, mode)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: cannot chmod: %s"), + dstpath, mu_strerror (rc))); + } + else if (flags & MU_COPY_OWNER) + { + uid_t uid; + gid_t gid; + + if (getuid () == 0) + { + uid = st.st_uid; + gid = st.st_gid; + } + else if (getuid () == st.st_uid) + { + uid = -1; + gid = st.st_gid; + } + else + { + uid = -1; + gid = -1; + } + + if (gid != -1) + { + if (chown (dstpath, uid, gid)) + { + rc = errno; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("%s: cannot chown to %lu.%lu: %s"), + dstpath, + (unsigned long) uid, + (unsigned long) gid, + mu_strerror (rc))); + } + } + } + return rc; +} + diff --git a/libmailutils/base/renamefile.c b/libmailutils/base/renamefile.c new file mode 100644 index 000000000..5bc0befa9 --- /dev/null +++ b/libmailutils/base/renamefile.c @@ -0,0 +1,39 @@ +#include <config.h> +#include <stdio.h> +#include <unistd.h> +#include <mailutils/stream.h> +#include <mailutils/util.h> +#include <mailutils/diag.h> +#include <mailutils/error.h> +#include <mailutils/errno.h> +#include <mailutils/nls.h> + +int +mu_rename_file (const char *oldpath, const char *newpath) +{ + int rc; + + if (rename (oldpath, newpath) == 0) + return 0; + + if (errno == EXDEV) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("cannot rename %s to %s: %s"), + oldpath, newpath, mu_strerror (errno))); + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_TRACE1, + (_("attempting copy"))); + + rc = mu_copy_file (oldpath, newpath, MU_COPY_MODE|MU_COPY_OWNER); + if (rc == 0) + { + if (unlink (oldpath)) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + (_("copied %s to %s, but failed to remove the source: %s"), + oldpath, newpath, mu_strerror (errno))); + } + } + } + return rc; +} |