aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/cp.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/posix/cp.c')
-rw-r--r--toys/posix/cp.c119
1 files changed, 86 insertions, 33 deletions
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 803bff85..d5e92f20 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"USE_CP_PRESERVE("(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))
@@ -44,6 +44,21 @@ config CP_MORE
-s symlink instead of copy
-v verbose
+config CP_PRESERVE
+ bool "cp --preserve support"
+ default y
+ depends on CP_MORE
+ help
+ usage: cp [--preserve=mota]
+
+ --preserve takes either a comma separated list of attributes, or the first
+ letter(s) of:
+
+ mode - permissions (ignore umask for rwx, copy suid and sticky bit)
+ ownership - user and group
+ timestamps - file creation, modification, and access times.
+ all - all of the above
+
config MV
bool "mv"
default y
@@ -87,16 +102,24 @@ config INSTALL
#include "toys.h"
GLOBALS(
- // install's options
- char *group;
- char *user;
- char *mode;
+ union {
+ struct {
+ // install's options
+ char *group;
+ char *user;
+ char *mode;
+ } i;
+ struct {
+ char *preserve;
+ } c;
+ };
char *destname;
struct stat top;
int (*callback)(struct dirtree *try);
uid_t uid;
gid_t gid;
+ int pflags;
)
// Callback from dirtree_read() for each file/directory under a source dir.
@@ -245,28 +268,27 @@ int cp_node(struct dirtree *try)
if (fdin < 0) {
catch = try->name;
break;
- } else {
- fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode);
- if (fdout >= 0) {
- xsendfile(fdin, fdout);
- err = 0;
- }
- close(fdin);
}
+ fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode);
+ if (fdout >= 0) {
+ xsendfile(fdin, fdout);
+ err = 0;
+ }
+ close(fdin);
}
} while (err && (flags & (FLAG_f|FLAG_n)) && !unlinkat(cfd, catch, 0));
}
+ // Did we make a thing?
if (fdout != -1) {
- if (flags & (FLAG_a|FLAG_p)) {
- struct timespec times[2];
- int rc;
+ int rc;
- // Inability to set these isn't fatal, some require root access.
+ // Inability to set --preserve isn't fatal, some require root access.
- times[0] = try->st.st_atim;
- times[1] = try->st.st_mtim;
+ // ownership
+ if (TT.pflags & 2) {
+ // permission bits already correct for mknod and don't apply to symlink
// 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,
@@ -278,16 +300,21 @@ int cp_node(struct dirtree *try)
perror_msg("chown '%s'", pp = dirtree_path(try, 0));
free(pp);
}
+ }
+
+ // timestamp
+ if (TT.pflags & 4) {
+ struct timespec times[] = {try->st.st_atim, try->st.st_mtim};
- // permission bits already correct for mknod and don't apply to symlink
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);
+ // mode comes last because other syscalls can strip suid bit
+ if (fdout != AT_FDCWD) {
+ if (TT.pflags & 1) fchmod(fdout, try->st.st_mode);
+ xclose(fdout);
+ }
if (CFG_MV && toys.which->name[0] == 'm')
if (unlinkat(tfd, try->name, S_ISDIR(try->st.st_mode) ? AT_REMOVEDIR :0))
@@ -300,17 +327,41 @@ int cp_node(struct dirtree *try)
void cp_main(void)
{
- char *destname = toys.optargs[--toys.optc];
+ char *destname = toys.optargs[--toys.optc],
+ *preserve[] = {"mode", "ownership", "timestamps"};
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.optflags & (FLAG_a|FLAG_p)) {
+ TT.pflags = 7; // preserve=mot
+ umask(0);
+ }
+ if (CFG_CP_PRESERVE && TT.c.preserve) {
+ char *pre = xstrdup(TT.c.preserve), *s;
+
+ if (comma_scan(pre, "all", 1)) TT.pflags = ~0;
+ for (i=0; i<ARRAY_LEN(preserve); i++)
+ if (comma_scan(pre, preserve[i], 1)) TT.pflags |= 1<<i;
+ if (*pre) {
+
+ // Try to interpret as letters, commas won't set anything this doesn't.
+ for (s = TT.c.preserve; *s; s++) {
+ for (i=0; i<ARRAY_LEN(preserve); i++)
+ if (*s == *preserve[i]) TT.pflags |= 1<<i;
+ if (i == ARRAY_LEN(preserve)) {
+ if (*s == 'a') TT.pflags = ~0;
+ else break;
+ }
+ }
+
+ if (*s) error_exit("bad --preserve=%s", pre);
+ }
+ free(pre);
+ }
if (!TT.callback) TT.callback = cp_node;
// Loop through sources
-
for (i=0; i<toys.optc; i++) {
struct dirtree *new;
char *src = toys.optargs[i];
@@ -351,6 +402,8 @@ void cp_main(void)
void mv_main(void)
{
+ toys.optflags |= FLAG_d|FLAG_p|FLAG_R;
+
cp_main();
}
@@ -360,9 +413,9 @@ void mv_main(void)
static int install_node(struct dirtree *try)
{
- if (TT.mode) try->st.st_mode = string_to_mode(TT.mode, try->st.st_mode);
- if (TT.group) try->st.st_gid = TT.gid;
- if (TT.user) try->st.st_uid = TT.uid;
+ if (TT.i.mode) try->st.st_mode = string_to_mode(TT.i.mode, try->st.st_mode);
+ if (TT.i.group) try->st.st_gid = TT.gid;
+ if (TT.i.user) try->st.st_uid = TT.uid;
// Always returns 0 because no -r
cp_node(try);
@@ -401,8 +454,8 @@ void install_main(void)
if (flags & FLAG_v) toys.optflags |= 8; // cp's FLAG_v
if (flags & (FLAG_p|FLAG_o|FLAG_g)) toys.optflags |= 512; // cp's FLAG_p
- if (TT.user) TT.uid = xgetpwnamid(TT.user)->pw_uid;
- if (TT.group) TT.gid = xgetgrnamid(TT.group)->gr_gid;
+ if (TT.i.user) TT.uid = xgetpwnamid(TT.i.user)->pw_uid;
+ if (TT.i.group) TT.gid = xgetgrnamid(TT.i.group)->gr_gid;
TT.callback = install_node;
cp_main();