diff --git a/bin/cp/cp.c b/bin/cp/cp.c index bbf73cd0abf3..0d0040a58779 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 a6b53c240d3a..25077f8a6bf6 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 350522596e4d..5b29e6a27f8b 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 __FBSDID("$FreeBSD$"); +#include +#include #include #include #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) { diff --git a/bin/mv/mv.c b/bin/mv/mv.c index e400729a785e..54c035da6922 100644 --- a/bin/mv/mv.c +++ b/bin/mv/mv.c @@ -44,6 +44,8 @@ static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; #include __FBSDID("$FreeBSD$"); +#include +#include #include #include #include @@ -252,6 +254,7 @@ fastcopy(char *from, char *to, struct stat *sbp) static char *bp; mode_t oldmode; int nread, from_fd, to_fd; + acl_t acl; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("%s", from); @@ -288,7 +291,6 @@ err: if (unlink(to)) (void)close(to_fd); return (1); } - (void)close(from_fd); oldmode = sbp->st_mode & ALLPERMS; if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { @@ -301,6 +303,21 @@ err: if (unlink(to)) sbp->st_mode &= ~(S_ISUID | S_ISGID); } } + /* + * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect + * for dest_file, then it's ACLs shall reflect the ACLs of the + * source_file. + */ + if (fpathconf(to_fd, _PC_ACL_EXTENDED) == 1 && + fpathconf(from_fd, _PC_ACL_EXTENDED) == 1) { + acl = acl_get_fd(from_fd); + if (acl == NULL) + warn("failed to get acl entries while setting %s", + from); + else if (acl_set_fd(to_fd, acl) < 0) + warn("failed to set acl entries for %s", to); + } + (void)close(from_fd); if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode (was: 0%03o)", to, oldmode); /*