From 2689ba27207a4511b743a651793173e8ddf041c2 Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 2 Sep 2009 08:08:57 +0000 Subject: [PATCH] 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 --- bin/cp/utils.c | 127 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 32 deletions(-) diff --git a/bin/cp/utils.c b/bin/cp/utils.c index c27c788a3580..63eba422f3da 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; + acl_type_t acl_type; + int acl_supported = 0, ret, trivial; - if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 || - fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1) + 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) - return (0); - if (acl_set_fd(dest_fd, acl) < 0) { - warn("failed to set acl entries for %s", to.p_path); + 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_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; + acl_type_t acl_type; + int acl_supported = 0, ret, trivial; - if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 || - pathconf(dest_dir, _PC_ACL_EXTENDED) != 1) + 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 == NULL) { - warn("failed to get default acl entries on %s", - source_dir); - return (1); + 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); } - 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); + acl = aclgetf(source_dir, acl_type); 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); + if (acl_is_trivial_np(acl, &trivial)) { + warn("acl_is_trivial() failed on %s", source_dir); + acl_free(acl); return (1); } + if (trivial) { + acl_free(acl); + return (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); }