Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 2 Sep 2009 08:08:57 +0000 (UTC)
From:      Edward Tomasz Napierala <trasz@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r196754 - head/bin/cp
Message-ID:  <200909020808.n8288vJK086081@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trasz
Date: Wed Sep  2 08:08:57 2009
New Revision: 196754
URL: http://svn.freebsd.org/changeset/base/196754

Log:
  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

Modified:
  head/bin/cp/utils.c

Modified: head/bin/cp/utils.c
==============================================================================
--- head/bin/cp/utils.c	Wed Sep  2 05:26:59 2009	(r196753)
+++ head/bin/cp/utils.c	Wed Sep  2 08:08:57 2009	(r196754)
@@ -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)
+	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 
 	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 
 		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);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200909020808.n8288vJK086081>