summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-12-15 17:25:40 +0200
committerSergey Poznyakoff <gray@gnu.org>2016-12-16 07:52:13 +0200
commitf0461be42ccc00ea751e93abc6eec51e455e925b (patch)
tree3017d829cb0d162c374b846ba22b0570e5759295
parent7c9c6b5f16940e8bf263e0eb0ee3fbc4b55d5aad (diff)
downloadmailutils-f0461be42ccc00ea751e93abc6eec51e455e925b.tar.gz
mailutils-f0461be42ccc00ea751e93abc6eec51e455e925b.tar.bz2
Add functions for safe renaming and copying of files
* libmailutils/base/copyfile.c: New file. * libmailutils/base/renamefile.c: New file. * include/mailutils/util.h (mu_copy_file) (mu_rename_file): New protos. * libmailutils/base/Makefile.am: Add new files. * examples/rename.c: New file. * examples/Makefile.am: Add new files. * NEWS: Update.
-rw-r--r--NEWS4
-rw-r--r--examples/Makefile.am1
-rw-r--r--examples/rename.c60
-rw-r--r--include/mailutils/util.h11
-rw-r--r--libmailutils/base/Makefile.am2
-rw-r--r--libmailutils/base/copyfile.c354
-rw-r--r--libmailutils/base/renamefile.c39
7 files changed, 469 insertions, 2 deletions
diff --git a/NEWS b/NEWS
index ef32de61f..12a69a5b1 100644
--- a/NEWS
+++ b/NEWS
@@ -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, &copy_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;
+}

Return to:

Send suggestions and report system problems to the System administrator.