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
This commit is contained in:
parent
f0246686c6
commit
2689ba2720
127
bin/cp/utils.c
127
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user