summaryrefslogtreecommitdiffstats
path: root/bin/cp
diff options
context:
space:
mode:
authorcsjp <csjp@FreeBSD.org>2005-09-05 04:36:08 +0000
committercsjp <csjp@FreeBSD.org>2005-09-05 04:36:08 +0000
commit549c6812a94339302b34e899ce15d452ed69ef23 (patch)
tree4d654ae429b6264f02e5490e9c9b223e74cd7b60 /bin/cp
parent57d500e0a77c3bb556a8303725a9195b630c5771 (diff)
downloadFreeBSD-src-549c6812a94339302b34e899ce15d452ed69ef23.zip
FreeBSD-src-549c6812a94339302b34e899ce15d452ed69ef23.tar.gz
Attempt to complete the userspace integration of POSIX.1e extended ACLs.
This includes adding support for ACLs into cp(1) and mv(1) userspace utilities. For mv(1), if _PC_ACL_EXTENDED is in effect for the source AND destination operands, the destination file's ACLs shall reflect the source. For cp(1), if _PC_ACL_EXTENDED is in effect for both source and destination operands, and -p has been specified, the ACLs from the source shall be preserved on the destination. MFC after: 1 month
Diffstat (limited to 'bin/cp')
-rw-r--r--bin/cp/cp.c5
-rw-r--r--bin/cp/extern.h2
-rw-r--r--bin/cp/utils.c80
3 files changed, 86 insertions, 1 deletions
diff --git a/bin/cp/cp.c b/bin/cp/cp.c
index bbf73cd..0d0040a 100644
--- a/bin/cp/cp.c
+++ b/bin/cp/cp.c
@@ -363,7 +363,10 @@ copy(char *argv[], enum op type, int fts_options)
*/
if (pflag) {
if (setfile(curr->fts_statp, -1))
- rval = 1;
+ rval = 1;
+ if (preserve_dir_acls(curr->fts_statp,
+ curr->fts_accpath, to.p_path) != 0)
+ rval = 1;
} else {
mode = curr->fts_statp->st_mode;
if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
diff --git a/bin/cp/extern.h b/bin/cp/extern.h
index a6b53c2..25077f8 100644
--- a/bin/cp/extern.h
+++ b/bin/cp/extern.h
@@ -46,5 +46,7 @@ int copy_file(const FTSENT *, int);
int copy_link(const FTSENT *, int);
int copy_special(struct stat *, int);
int setfile(struct stat *, int);
+int preserve_dir_acls(struct stat *, char *, char *);
+int preserve_fd_acls(int, int);
void usage(void);
__END_DECLS
diff --git a/bin/cp/utils.c b/bin/cp/utils.c
index 3505225..5b29e6a 100644
--- a/bin/cp/utils.c
+++ b/bin/cp/utils.c
@@ -35,6 +35,8 @@ static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/types.h>
+#include <sys/acl.h>
#include <sys/param.h>
#include <sys/stat.h>
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
@@ -204,6 +206,8 @@ copy_file(const FTSENT *entp, int dne)
if (pflag && setfile(fs, to_fd))
rval = 1;
+ if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
+ rval = 1;
(void)close(from_fd);
if (close(to_fd)) {
warn("%s", to.p_path);
@@ -326,6 +330,82 @@ setfile(struct stat *fs, int fd)
return (rval);
}
+int
+preserve_fd_acls(int source_fd, int dest_fd)
+{
+ struct acl *aclp;
+ acl_t acl;
+
+ if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 ||
+ fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1)
+ return (0);
+ acl = acl_get_fd(source_fd);
+ if (acl == NULL) {
+ warn("failed to get acl entries while setting %s", to.p_path);
+ return (1);
+ }
+ aclp = &acl->ats_acl;
+ if (aclp->acl_cnt == 3)
+ return (0);
+ if (acl_set_fd(dest_fd, acl) < 0) {
+ warn("failed to set acl entries for %s", to.p_path);
+ return (1);
+ }
+ return (0);
+}
+
+int
+preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
+{
+ acl_t (*aclgetf)(const char *, acl_type_t);
+ int (*aclsetf)(const char *, acl_type_t, acl_t);
+ struct acl *aclp;
+ acl_t acl;
+
+ if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 ||
+ pathconf(dest_dir, _PC_ACL_EXTENDED) != 1)
+ return (0);
+ /*
+ * If the file is a link we will not follow it
+ */
+ if (S_ISLNK(fs->st_mode)) {
+ aclgetf = acl_get_link_np;
+ aclsetf = acl_set_link_np;
+ } else {
+ aclgetf = acl_get_file;
+ aclsetf = acl_set_file;
+ }
+ /*
+ * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
+ * size ACL will be returned. So it is not safe to simply
+ * check the pointer to see if the default ACL is present.
+ */
+ acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
+ if (acl == NULL) {
+ warn("failed to get default acl entries on %s",
+ source_dir);
+ return (1);
+ }
+ aclp = &acl->ats_acl;
+ if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
+ ACL_TYPE_DEFAULT, acl) < 0) {
+ warn("failed to set default acl entries on %s",
+ dest_dir);
+ return (1);
+ }
+ acl = aclgetf(source_dir, ACL_TYPE_ACCESS);
+ if (acl == NULL) {
+ warn("failed to get acl entries on %s", source_dir);
+ return (1);
+ }
+ aclp = &acl->ats_acl;
+ if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) {
+ warn("failed to set acl entries on %s", dest_dir);
+ return (1);
+ }
+ return (0);
+}
+
void
usage(void)
{
OpenPOWER on IntegriCloud