diff options
author | Rob Landley <rob@landley.net> | 2023-08-19 12:47:26 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2023-08-19 12:47:26 -0500 |
commit | 896b64db351a2ba94df82f4f604004c4fb84fb67 (patch) | |
tree | e3a13e47105a51f19d654cb8acecc4631bdffec1 /toys/posix/cp.c | |
parent | 4c535fe4adb1b282b58354dfa442f858128e2168 (diff) | |
download | toybox-896b64db351a2ba94df82f4f604004c4fb84fb67.tar.gz |
Copy xattrs for directories too.
Diffstat (limited to 'toys/posix/cp.c')
-rw-r--r-- | toys/posix/cp.c | 79 |
1 files changed, 42 insertions, 37 deletions
diff --git a/toys/posix/cp.c b/toys/posix/cp.c index a9ab42d6..c2b838ed 100644 --- a/toys/posix/cp.c +++ b/toys/posix/cp.c @@ -119,12 +119,40 @@ struct cp_preserve { {"mode"}, {"ownership"}, {"timestamps"}, {"context"}, {"xattr"}, ); +void cp_xattr(int fdin, int fdout, char *file) +{ + ssize_t listlen, len; + char *name, *value, *list; + + if (!(TT.pflags&(_CP_xattr|_CP_context))) return; + if ((listlen = xattr_flist(fdin, 0, 0))<1) return; + + list = xmalloc(listlen); + xattr_flist(fdin, list, listlen); + for (name = list; name-list < listlen; name += strlen(name)+1) { + // context copies security, xattr copies everything else + len = strncmp(name, "security.", 9) ? _CP_xattr : _CP_context; + if (!(TT.pflags&len)) continue; + if ((len = xattr_fget(fdin, name, 0, 0))>0) { + value = xmalloc(len); + if (len == xattr_fget(fdin, name, value, len)) + if (xattr_fset(fdout, name, value, len, 0)) + perror_msg("%s setxattr(%s=%s)", file, name, value); + free(value); + } + } + free(list); +} + // Callback from dirtree_read() for each file/directory under a source dir. +// traverses two directories in parallel: try->dirfd is source dir, +// try->extra is dest dir. TODO: filehandle exhaustion? + static int cp_node(struct dirtree *try) { int fdout = -1, cfd = try->parent ? try->parent->extra : AT_FDCWD, - save = DIRTREE_SAVE*(CFG_MV && toys.which->name[0] == 'm'), rc = 0, + save = DIRTREE_SAVE*(CFG_MV && *toys.which->name == 'm'), rc = 0, tfd = dirtree_parentfd(try); unsigned flags = toys.optflags; char *s = 0, *catch = try->parent ? try->name : TT.destname, *err = "%s"; @@ -142,6 +170,8 @@ static int cp_node(struct dirtree *try) save = 0; llist_traverse(try->child, free); } + + cp_xattr(try->dirfd, try->extra, catch); } else { // -d is only the same as -r for symlinks, not for directories if (S_ISLNK(try->st.st_mode) && (flags & FLAG_d)) flags |= FLAG_r; @@ -180,6 +210,7 @@ static int cp_node(struct dirtree *try) // Loop for -f retry after unlink do { + int ii, fdin = -1; // directory, hardlink, symlink, mknod (char, block, fifo, socket), file @@ -215,19 +246,17 @@ static int cp_node(struct dirtree *try) // appending the right number of .. entries as you go down the tree. } else if (flags & FLAG_s) { - char *s; + char *s, *s2; struct dirtree *or; - int dotdots = 0; s = dirtree_path(try, 0); - for (or = try; or->parent; or = or->parent) dotdots++; - - if (*or->name == '/') dotdots = 0; - if (dotdots) { - char *s2 = xmprintf("%*c%s", 3*dotdots, ' ', s); + for (ii = 0, or = try; or->parent; or = or->parent) ii++; + if (*or->name == '/') ii = 0; + if (ii) { + s2 = xmprintf("%*c%s", 3*ii, ' ', s); free(s); s = s2; - while(dotdots--) { + while(ii--) { memcpy(s2, "../", 3); s2 += 3; } @@ -255,13 +284,12 @@ static int cp_node(struct dirtree *try) // Copy contents of file. } else { - int fdin, ii; - fdin = openat(tfd, try->name, O_RDONLY); if (fdin < 0) { catch = try->name; break; } + // When copying contents use symlink target's attributes if (S_ISLNK(try->st.st_mode)) fstat(fdin, &try->st); fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode); @@ -270,33 +298,9 @@ static int cp_node(struct dirtree *try) err = 0; } - // We only copy xattrs for files because there's no flistxattrat() - if (TT.pflags&(_CP_xattr|_CP_context)) { - ssize_t listlen = xattr_flist(fdin, 0, 0), len; - char *name, *value, *list; - - if (listlen>0) { - list = xmalloc(listlen); - xattr_flist(fdin, list, listlen); - list[listlen-1] = 0; // I do not trust this API. - for (name = list; name-list < listlen; name += strlen(name)+1) { - // context copies security, xattr copies everything else - ii = strncmp(name, "security.", 9) ? _CP_xattr : _CP_context; - if (!(TT.pflags&ii)) continue; - if ((len = xattr_fget(fdin, name, 0, 0))>0) { - value = xmalloc(len); - if (len == xattr_fget(fdin, name, value, len)) - if (xattr_fset(fdout, name, value, len, 0)) - perror_msg("%s setxattr(%s=%s)", catch, name, value); - free(value); - } - } - free(list); - } - } - - close(fdin); + cp_xattr(fdin, fdout, catch); } + if (fdin != -1) close(fdin); } while (err && (flags & (FLAG_f|FLAG_n)) && !unlinkat(cfd, catch, 0)); } @@ -351,6 +355,7 @@ static int cp_node(struct dirtree *try) perror_msg(err, catch); free(s); } + return 0; } |