[Toybox] [PATCH] cp: implements -p and --preserve options
José Bollo
jobol at nonadev.net
Wed May 20 03:22:10 PDT 2015
---
toys/posix/cp.c | 121
++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 101 insertions(+), 20 deletions(-)
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 803bff8..ecef0f6 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -7,7 +7,7 @@
// options shared between mv/cp must be in same order (right to left)
// for FLAG macros to work out right in shared infrastructure.
-USE_CP(NEWTOY(cp,
"<2RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_CP(NEWTOY(cp,
"<2(preserve):RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
USE_MV(NEWTOY(mv, "<2"USE_CP_MORE("vnF")"fi"USE_CP_MORE("[-ni]"),
TOYFLAG_BIN))
USE_INSTALL(NEWTOY(install, "<1cdDpsvm:o:g:", TOYFLAG_USR|TOYFLAG_BIN))
@@ -23,12 +23,14 @@ config CP
-f delete destination files we can't write to
-F delete any existing destination file first
(--remove-destination)
-i interactive, prompt before overwriting existing DEST
- -p preserve timestamps, ownership, and permissions
+ -p preserve timestamps, ownership, and mode
-R recurse into subdirectories (DEST must be a directory)
-H Follow symlinks listed on command line
-L Follow all symlinks
-P Do not follow symlinks [default]
-
+ --preserve[=LISTE_ATT] preserve the specified attributes:
+ mode,ownership,timestamps,context,links,xattr,all
+
config CP_MORE
bool "cp -adlnrsv options"
default y
@@ -86,7 +88,12 @@ config INSTALL
#define FOR_cp
#include "toys.h"
+#include <sys/xattr.h>
+
GLOBALS(
+ // cp preserve
+ char *arg_preserve;
+
// install's options
char *group;
char *user;
@@ -97,8 +104,76 @@ GLOBALS(
int (*callback)(struct dirtree *try);
uid_t uid;
gid_t gid;
+ enum {
+ p_mode = 1,
+ p_ownership = 2,
+ p_timestamps = 4,
+ p_context = 8,
+ p_links = 16,
+ p_xattr = 32,
+ p_verbose = 64,
+ p_p = 7,
+ p_all = 127 } preserve;
)
+// interprete '--preserve' list of attributes
+
+static int set_preserve(const char *list)
+{
+ int p = 0;
+ char *s;
+ while(*list) {
+ while (*list == ',') list++;
+ switch (*list) {
+ case 'a': s = "all"; p |= p_all; break;
+ case 'c': s = "context"; p |= p_context; break;
+ case 'l': s = "links"; p |= p_links; break;
+ case 'm': s = "mode"; p |= p_mode; break;
+ case 'o': s = "ownership"; p |= p_ownership; break;
+ case 't': s = "timestamps"; p |= p_timestamps; break;
+ case 'x': s = "xattr"; p |= p_xattr; break;
+ default: return -1;
+ }
+ while(*s) if (*s++ != *list++) return -1;
+ if (*list == 0) break;
+ if (*list++ != ',') return -1;
+ }
+ TT.preserve = p;
+ return 0;
+}
+
+// Copies extended attributes
+
+static int cp_xattrs(int fdin, int fdout)
+{
+ int err = 0;
+ ssize_t len, listlen;
+ char *list, *name, *value;
+
+ if ((listlen = flistxattr(fdin, NULL, 0)) > 0 && (list =
malloc(listlen))) {
+ flistxattr(fdin, list, listlen);
+ for (name = list ; (name - list) < listlen ; name +=
strlen(name)+1) {
+ /* test if the xattribute have to be copied */
+ if ((TT.preserve & p_xattr)
+ || ((TT.preserve & p_mode) &&
+ !
strncmp(name,"system.posix_acl_",sizeof("system.posix_acl_")-1))
+ || ((TT.preserve & p_context) &&
+ !strncmp(name,"security.",sizeof("security.")-1)))
+ {
+ /* yes, try to copy it */
+ if ((len = fgetxattr(fdin, name, 0, 0)) > 0 && (value =
malloc(len))) {
+ fgetxattr(fdin, name, value, len);
+ err += !!fsetxattr(fdout, name, value, len, 0);
+ free(value);
+ } else err++;
+ }
+ }
+ if (err && (TT.preserve & p_verbose))
+ return -1;
+ }
+ return 0;
+}
+
// Callback from dirtree_read() for each file/directory under a source
dir.
int cp_node(struct dirtree *try)
@@ -249,6 +324,10 @@ int cp_node(struct dirtree *try)
fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC,
try->st.st_mode);
if (fdout >= 0) {
xsendfile(fdin, fdout);
+
+ if (TT.preserve & (p_xattr | p_mode | p_context))
+ cp_xattrs(fdin, fdout);
+
err = 0;
}
close(fdin);
@@ -258,15 +337,13 @@ int cp_node(struct dirtree *try)
}
if (fdout != -1) {
- if (flags & (FLAG_a|FLAG_p)) {
- struct timespec times[2];
+ if (TT.preserve & p_mode) {
+ // permission bits already correct for mknod and don't apply to
symlink
+ if (fdout != AT_FDCWD)
+ fchmod(fdout, try->st.st_mode);
+ }
+ if (TT.preserve & p_ownership) {
int rc;
-
- // Inability to set these isn't fatal, some require root access.
-
- times[0] = try->st.st_atim;
- times[1] = try->st.st_mtim;
-
// If we can't get a filehandle to the actual object, use racy
functions
if (fdout == AT_FDCWD)
rc = fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
@@ -274,17 +351,15 @@ int cp_node(struct dirtree *try)
else rc = fchown(fdout, try->st.st_uid, try->st.st_gid);
if (rc) {
char *pp;
-
perror_msg("chown '%s'", pp = dirtree_path(try, 0));
free(pp);
}
-
- // permission bits already correct for mknod and don't apply to
symlink
+ }
+ if (TT.preserve & p_timestamps) {
+ struct timespec times[2] = { try->st.st_atim, try->st.st_mtim };
+ // Inability to set these isn't fatal, some require root access.
if (fdout == AT_FDCWD) utimensat(cfd, catch, times,
AT_SYMLINK_NOFOLLOW);
- else {
- futimens(fdout, times);
- fchmod(fdout, try->st.st_mode);
- }
+ else futimens(fdout, times);
}
if (fdout != AT_FDCWD) xclose(fdout);
@@ -304,8 +379,14 @@ void cp_main(void)
int i, destdir = !stat(destname, &TT.top) && S_ISDIR(TT.top.st_mode);
if (toys.optc>1 && !destdir) error_exit("'%s' not directory",
destname);
- if (toys.which->name[0] == 'm') toys.optflags |= FLAG_d|FLAG_p|
FLAG_R;
- if (toys.optflags & (FLAG_a|FLAG_p)) umask(0);
+ if (toys.which->name[0] == 'm') toys.optflags |= FLAG_d|FLAG_a|
FLAG_R;
+ if ((toys.optflags & FLAG_preserve) && set_preserve(TT.arg_preserve)
< 0)
+ error_exit("bad --preserve value '%s'",TT.arg_preserve);
+ if (toys.optflags & FLAG_p) TT.preserve |= p_p;
+ if (toys.optflags & FLAG_a) TT.preserve |= p_all;
+ if (TT.preserve & p_mode) umask(0);
+ if (TT.preserve & p_links)
+ fprintf(stderr, "warning: --preserve=links not supported\n");
if (!TT.callback) TT.callback = cp_node;
--
2.1.2
More information about the Toybox
mailing list