summaryrefslogtreecommitdiffstats
path: root/bin/cp
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2009-09-02 08:08:57 +0000
committertrasz <trasz@FreeBSD.org>2009-09-02 08:08:57 +0000
commit383a57b980d6518f069910885048b1a4a4db07aa (patch)
treececef9d7348e2c6c50836231d725810278d32bd7 /bin/cp
parent6509b141933b73e13abca0f4d24ec0b1f4336a5f (diff)
downloadFreeBSD-src-383a57b980d6518f069910885048b1a4a4db07aa.zip
FreeBSD-src-383a57b980d6518f069910885048b1a4a4db07aa.tar.gz
Add NFSv4 ACL support to cp(1) and fix a few memory leaks.
Note that this changes error reporting behaviour somewhat - before, no error was reported if ACL couldn't be copied because the target filesystem doesn't support ACLs. Now, it will be reported - of course, only if there actually is an ACL to copy. Reviewed by: rwatson
Diffstat (limited to 'bin/cp')
-rw-r--r--bin/cp/utils.c123
1 files changed, 93 insertions, 30 deletions
diff --git a/bin/cp/utils.c b/bin/cp/utils.c
index c27c788..63eba42 100644
--- a/bin/cp/utils.c
+++ b/bin/cp/utils.c
@@ -377,24 +377,52 @@ setfile(struct stat *fs, int fd)
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)
+ acl_type_t acl_type;
+ int acl_supported = 0, ret, trivial;
+
+ ret = fpathconf(source_fd, _PC_ACL_NFS4);
+ if (ret > 0 ) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_NFS4;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
+ return (1);
+ }
+ if (acl_supported == 0) {
+ ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
+ if (ret > 0 ) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_ACCESS;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
+ to.p_path);
+ return (1);
+ }
+ }
+ if (acl_supported == 0)
return (0);
- acl = acl_get_fd(source_fd);
+
+ acl = acl_get_fd_np(source_fd, acl_type);
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)
+ if (acl_is_trivial_np(acl, &trivial)) {
+ warn("acl_is_trivial() failed for %s", to.p_path);
+ acl_free(acl);
+ return (1);
+ }
+ if (trivial) {
+ acl_free(acl);
return (0);
- if (acl_set_fd(dest_fd, acl) < 0) {
+ }
+ if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
warn("failed to set acl entries for %s", to.p_path);
+ acl_free(acl);
return (1);
}
+ acl_free(acl);
return (0);
}
@@ -405,10 +433,31 @@ preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
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)
+ acl_type_t acl_type;
+ int acl_supported = 0, ret, trivial;
+
+ ret = pathconf(source_dir, _PC_ACL_NFS4);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_NFS4;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
+ return (1);
+ }
+ if (acl_supported == 0) {
+ ret = pathconf(source_dir, _PC_ACL_EXTENDED);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_ACCESS;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
+ source_dir);
+ return (1);
+ }
+ }
+ if (acl_supported == 0)
return (0);
+
/*
* If the file is a link we will not follow it
*/
@@ -419,34 +468,48 @@ preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
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_type == ACL_TYPE_ACCESS) {
+ /*
+ * 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);
+ acl_free(acl);
+ return (1);
+ }
+ acl_free(acl);
+ }
+ acl = aclgetf(source_dir, acl_type);
if (acl == NULL) {
- warn("failed to get default acl entries on %s",
- source_dir);
+ warn("failed to get 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);
+ if (acl_is_trivial_np(acl, &trivial)) {
+ warn("acl_is_trivial() failed on %s", source_dir);
+ acl_free(acl);
return (1);
}
- acl = aclgetf(source_dir, ACL_TYPE_ACCESS);
- if (acl == NULL) {
- warn("failed to get acl entries on %s", source_dir);
- return (1);
+ if (trivial) {
+ acl_free(acl);
+ return (0);
}
- aclp = &acl->ats_acl;
- if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) {
+ if (aclsetf(dest_dir, acl_type, acl) < 0) {
warn("failed to set acl entries on %s", dest_dir);
+ acl_free(acl);
return (1);
}
+ acl_free(acl);
return (0);
}
OpenPOWER on IntegriCloud