Richard P | 9 Jun 2012 23:19
Picon

[patch] NFSv4/ZFS ACLs

This is a PoC patch for NFSv4/ZFS ACLs.

The objective of the patch is that rsync --acls support NFSv4/ZFS ACLs without requiring a new command line option

NFSv4 ACLs can't be represented using POSIX draft ACLs, if an NFSv4 ACL is
present a separate POSIX draft ACL will not be present and there are new APIs
to access NFSv4 ACLs.  So we need to distinguish between NFSv4 ACLs and POSIX
ACLs in rsync.  I've done this by introducing an "ACL type" on the wire, and
revving the protocol version.  If rsync encounters a NFSv4 ACL and the peer
doesn't support the higher protocol version the process is terminated.

Linux and Solaris represent NFSv4 ACLs differently and provide no API to
serialize them in a platform-independent format so this patch treats the ACLs as
platform-dependent opaque data.

On Linux NFSv4 ACLs are implemented using xattrs so xattr support must be
compiled in.  The implementation re-uses xattrs.c:get_xattr_data so the static
directive is removed from this function.

All the NFSv4 ACL handling code is surrounded by
#ifdef SUPPORT_NFS4_ACLS
- the parts that handle sending and receiving the ACL type used in the higher protocol version are
compiled in unconditionally

The remaining to-dos are:

 * autoconf integration
 * move platform-specific code from acl.c to lib/sysacls.c or a new file lib/sysnfs4acls.c ?
 * wasn't sure how to SUBPROTOCOL_VERSION worked so I've revved PROTOCOL_VERSION to 31
 - if this is agreed we need to set all the code that references the higher protocol version to use the right number
 * rebase off HEAD

fake-super support is done.

I can work on the remaining to-dos but before proceeding further I wanted to know if the basis of this patch would be accepted.

Attachment (nfsv4acl-1.patch): application/octet-stream, 15 KiB
--

-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html
Richard P | 9 Jun 2012 23:33
Picon

Re: [patch] NFSv4/ZFS ACLs

This time with a non-stripped attachment (hopefully)

 
diff -uNr rsync-3.0.9.orig/acls.c rsync-3.0.9/acls.c
--- rsync-3.0.9.orig/acls.c	2011-05-31 00:39:07.000000000 +0900
+++ rsync-3.0.9/acls.c	2012-06-05 11:05:42.000000000 +0900
 <at>  <at>  -33,6 +33,7  <at>  <at> 
 extern int inc_recurse;
 extern int preserve_devices;
 extern int preserve_specials;
+extern int protocol_version;

 /* Flags used to indicate what items are being transmitted for an entry. */
 #define XMIT_USER_OBJ (1<<0)
 <at>  <at>  -52,6 +53,9  <at>  <at> 
 #define XFLAG_NAME_FOLLOWS 0x0001u
 #define XFLAG_NAME_IS_USER 0x0002u

+ /* ordinary ACLs have this code; NFSv4 ACLs have a platform-specific code */
+#define SMB_ACL_TYPE 1
+
 /* === ACL structures === */

 typedef struct {
 <at>  <at>  -76,6 +80,10  <at>  <at> 
 	uchar group_obj;
 	uchar mask_obj;
 	uchar other_obj;
+#ifdef SUPPORT_NFS4_ACLS
+	size_t nfs4_acl_len;
+	char *nfs4_acl_data;
+#endif
 } rsync_acl;

 typedef struct {
 <at>  <at>  -84,7 +92,11  <at>  <at> 
 } acl_duo;

 static const rsync_acl empty_rsync_acl = {
+#ifdef SUPPORT_NFS4_ACLS
+	{NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY, 0, NULL
+#else
 	{NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY
+#endif
 };

 static item_list access_acl_list = EMPTY_ITEM_LIST;
 <at>  <at>  -192,6 +204,11  <at>  <at> 

 static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2)
 {
+#ifdef SUPPORT_NFS4_ACLS
+	if (racl1->nfs4_acl_len || racl2->nfs4_acl_len)
+		return racl1->nfs4_acl_len == racl2->nfs4_acl_len
+		  && !memcmp(racl1->nfs4_acl_data, racl2->nfs4_acl_data, racl1->nfs4_acl_len);
+#endif
 	return racl1->user_obj == racl2->user_obj
 	    && racl1->group_obj == racl2->group_obj
 	    && racl1->mask_obj == racl2->mask_obj
 <at>  <at>  -207,6 +224,12  <at>  <at> 
 static BOOL rsync_acl_equal_enough(const rsync_acl *racl1,
 				   const rsync_acl *racl2, mode_t m)
 {
+
+#ifdef SUPPORT_NFS4_ACLS
+	if (racl1->nfs4_acl_len || racl2->nfs4_acl_len)
+		return racl1->nfs4_acl_len == racl2->nfs4_acl_len
+		    && !memcmp(racl1->nfs4_acl_data, racl2->nfs4_acl_data, racl1->nfs4_acl_len);
+#endif
 	if ((racl1->mask_obj ^ racl2->mask_obj) & NO_ENTRY)
 		return False; /* One has a mask and the other doesn't */

 <at>  <at>  -229,6 +252,10  <at>  <at> 
 {
 	if (racl->names.idas)
 		free(racl->names.idas);
+#ifdef SUPPORT_NFS4_ACLS
+	if (racl->nfs4_acl_data)
+		free(racl->nfs4_acl_data);
+#endif
 	*racl = empty_rsync_acl;
 }

 <at>  <at>  -541,6 +568,60  <at>  <at> 
 	return 0;
 }

+#ifdef SUPPORT_NFS4_ACLS
+#ifdef HAVE_LINUX_XATTRS
+#define NFS4_ACL_TYPE 2
+#define NFS4_ACL_ATTR "system.nfs4_acl"
+#include "lib/sysxattrs.h"
+static int sys_nfs4acl_get_file(const char* fname, rsync_acl *racl)
+{
+	size_t len = 0; /* no extra data alloc needed from get_xattr_data() */
+	if ((racl->nfs4_acl_data = get_xattr_data(fname, NFS4_ACL_ATTR, &len, 1)) != NULL) {
+		racl->nfs4_acl_len = len;
+		return 0;
+	}
+	return errno == ENOTSUP ? 1 : -1;
+}
+
+static int sys_nfs4acl_set_file(const char* fname, const rsync_acl *racl)
+{
+	return sys_lsetxattr(fname, NFS4_ACL_ATTR, racl->nfs4_acl_data, racl->nfs4_acl_len);
+}
+
+#elif defined(HAVE_SOLARIS_ACLS)
+#define NFS4_ACL_TYPE 3
+static int sys_nfs4acl_get_file(const char* fname, rsync_acl *racl)
+{
+	acl_t *aclp;
+	char *acltextp;
+
+	if (acl(fname, ACE_GETACLCNT, 0, NULL) == -1)
+		return errno = EINVAL ? 1 : -1;
+	if (acl_get(fname, 0, &aclp))
+		return errno == ENOSYS ? 1 : -1;
+	acltextp = acl_totext(aclp, ACL_COMPACT_FMT);
+	racl->nfs4_acl_data = acltextp;
+	racl->nfs4_acl_len = strlen(acltextp) + 1;
+	acl_free(aclp);
+	return 0;
+}
+
+static int sys_nfs4acl_set_file(const char* fname, const rsync_acl *racl)
+{
+	acl_t *aclp;
+	int rc;
+
+	if (acl_fromtext(racl->nfs4_acl_data, &aclp)) {
+		errno = EINVAL;
+		return -1;
+	}
+	rc = acl_set(fname, aclp);
+	acl_free(aclp);
+	return rc;
+}
+#endif /* HAVE_LINUX_XATTRS */
+#endif /* SUPPORT_NFS4_ACLS */
+
 /* Return the Access Control List for the given filename. */
 int get_acl(const char *fname, stat_x *sxp)
 {
 <at>  <at>  -562,6 +643,25  <at>  <at> 
 			return 0;
 	}

+#ifdef SUPPORT_NFS4_ACLS
+#ifdef SUPPORT_XATTRS
+	/* --fake-super support: load ACLs from an xattr. */
+	if (am_root < 0) {
+		sxp->acc_acl->nfs4_acl_data = get_xattr_nfs4acl(fname, &sxp->acc_acl->nfs4_acl_len);
+		return 0;
+	}
+#endif
+	switch (sys_nfs4acl_get_file(fname, sxp->acc_acl)) {
+	case -1:
+		rsyserr(FERROR_XFER, errno, "get_acl: sys_nfs4acl_get_file(%s)",
+			fname);
+		free_acl(sxp);
+		return -1;
+	case 0:
+		return 0;
+	/* default: 1 - NFSv4 ACLs not supported so fall through to standard ACLs */
+	}
+#endif
 	if (get_rsync_acl(fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS,
 			  sxp->st.st_mode) < 0) {
 		free_acl(sxp);
 <at>  <at>  -609,7 +709,7  <at>  <at> 
 	}
 }

-static void send_rsync_acl(int f, rsync_acl *racl, SMB_ACL_TYPE_T type,
+static void send_rsync_acl(int f, int acl_type, rsync_acl *racl, SMB_ACL_TYPE_T type,
 			   item_list *racl_list)
 {
 	int ndx = find_matching_rsync_acl(racl, type, racl_list);
 <at>  <at>  -621,30 +721,36  <at>  <at> 
 		rsync_acl *new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000);
 		uchar flags = 0;

-		if (racl->user_obj != NO_ENTRY)
-			flags |= XMIT_USER_OBJ;
-		if (racl->group_obj != NO_ENTRY)
-			flags |= XMIT_GROUP_OBJ;
-		if (racl->mask_obj != NO_ENTRY)
-			flags |= XMIT_MASK_OBJ;
-		if (racl->other_obj != NO_ENTRY)
-			flags |= XMIT_OTHER_OBJ;
-		if (racl->names.count)
-			flags |= XMIT_NAME_LIST;
-
-		write_byte(f, flags);
-
-		if (flags & XMIT_USER_OBJ)
-			write_varint(f, racl->user_obj);
-		if (flags & XMIT_GROUP_OBJ)
-			write_varint(f, racl->group_obj);
-		if (flags & XMIT_MASK_OBJ)
-			write_varint(f, racl->mask_obj);
-		if (flags & XMIT_OTHER_OBJ)
-			write_varint(f, racl->other_obj);
-		if (flags & XMIT_NAME_LIST)
-			send_ida_entries(f, &racl->names);
-
+		if (acl_type == SMB_ACL_TYPE) {
+			if (racl->user_obj != NO_ENTRY)
+				flags |= XMIT_USER_OBJ;
+			if (racl->group_obj != NO_ENTRY)
+				flags |= XMIT_GROUP_OBJ;
+			if (racl->mask_obj != NO_ENTRY)
+				flags |= XMIT_MASK_OBJ;
+			if (racl->other_obj != NO_ENTRY)
+				flags |= XMIT_OTHER_OBJ;
+			if (racl->names.count)
+				flags |= XMIT_NAME_LIST;
+
+			write_byte(f, flags);
+
+			if (flags & XMIT_USER_OBJ)
+				write_varint(f, racl->user_obj);
+			if (flags & XMIT_GROUP_OBJ)
+				write_varint(f, racl->group_obj);
+			if (flags & XMIT_MASK_OBJ)
+				write_varint(f, racl->mask_obj);
+			if (flags & XMIT_OTHER_OBJ)
+				write_varint(f, racl->other_obj);
+			if (flags & XMIT_NAME_LIST)
+				send_ida_entries(f, &racl->names);
+#ifdef SUPPORT_NFS4_ACLS
+		} else {
+			write_varint(f, racl->nfs4_acl_len);
+			write_buf(f, racl->nfs4_acl_data, racl->nfs4_acl_len);
+#endif
+		}
 		/* Give the allocated data to the new list object. */
 		*new_racl = *racl;
 		*racl = empty_rsync_acl;
 <at>  <at>  -658,17 +764,30  <at>  <at> 
 	if (!sxp->acc_acl) {
 		sxp->acc_acl = create_racl();
 		rsync_acl_fake_perms(sxp->acc_acl, sxp->st.st_mode);
+#ifdef SUPPORT_NFS4_ACLS
+	} else if (sxp->acc_acl->nfs4_acl_data) {
+		if (protocol_version >= 31) {
+			write_byte(f, NFS4_ACL_TYPE);
+			send_rsync_acl(f, NFS4_ACL_TYPE, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);
+		} else {
+			rprintf(FERROR, "send_acl: nfs4 acls require protocol XX or higher (negotiated %d).\n", protocol_version);
+			exit_cleanup(RERR_PROTOCOL);
+		}
+		return;
+#endif
 	}
 	/* Avoid sending values that can be inferred from other data. */
 	rsync_acl_strip_perms(sxp);

-	send_rsync_acl(f, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);
+	if (protocol_version >= 31)
+		write_byte(f, SMB_ACL_TYPE);
+	send_rsync_acl(f, SMB_ACL_TYPE, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);

 	if (S_ISDIR(sxp->st.st_mode)) {
 		if (!sxp->def_acl)
 			sxp->def_acl = create_racl();

-		send_rsync_acl(f, sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list);
+		send_rsync_acl(f, SMB_ACL_TYPE, sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list);
 	}
 }

 <at>  <at>  -738,7 +857,7  <at>  <at> 
 	return computed_mask_bits & ~NO_ENTRY;
 }

-static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode_t mode)
+static int recv_rsync_acl(int f, int acl_type, item_list *racl_list, SMB_ACL_TYPE_T type, mode_t mode)
 {
 	uchar computed_mask_bits = 0;
 	acl_duo *duo_item;
 <at>  <at>  -758,33 +877,42  <at>  <at> 
 	duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
 	duo_item->racl = empty_rsync_acl;

-	flags = read_byte(f);
+	if (acl_type == SMB_ACL_TYPE) {
+		flags = read_byte(f);

-	if (flags & XMIT_USER_OBJ)
-		duo_item->racl.user_obj = recv_acl_access(f, NULL);
-	if (flags & XMIT_GROUP_OBJ)
-		duo_item->racl.group_obj = recv_acl_access(f, NULL);
-	if (flags & XMIT_MASK_OBJ)
-		duo_item->racl.mask_obj = recv_acl_access(f, NULL);
-	if (flags & XMIT_OTHER_OBJ)
-		duo_item->racl.other_obj = recv_acl_access(f, NULL);
-	if (flags & XMIT_NAME_LIST)
-		computed_mask_bits |= recv_ida_entries(f, &duo_item->racl.names);
+		if (flags & XMIT_USER_OBJ)
+			duo_item->racl.user_obj = recv_acl_access(f, NULL);
+		if (flags & XMIT_GROUP_OBJ)
+			duo_item->racl.group_obj = recv_acl_access(f, NULL);
+		if (flags & XMIT_MASK_OBJ)
+			duo_item->racl.mask_obj = recv_acl_access(f, NULL);
+		if (flags & XMIT_OTHER_OBJ)
+			duo_item->racl.other_obj = recv_acl_access(f, NULL);
+		if (flags & XMIT_NAME_LIST)
+			computed_mask_bits |= recv_ida_entries(f, &duo_item->racl.names);

 #ifdef HAVE_OSX_ACLS
-	/* If we received a superfluous mask, throw it away. */
-	duo_item->racl.mask_obj = NO_ENTRY;
+		/* If we received a superfluous mask, throw it away. */
+		duo_item->racl.mask_obj = NO_ENTRY;
 #else
-	if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
-		/* Mask must be non-empty with lists. */
-		if (type == SMB_ACL_TYPE_ACCESS)
-			computed_mask_bits = (mode >> 3) & 7;
-		else
-			computed_mask_bits |= duo_item->racl.group_obj & ~NO_ENTRY;
-		duo_item->racl.mask_obj = computed_mask_bits;
-	}
+		if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
+			/* Mask must be non-empty with lists. */
+			if (type == SMB_ACL_TYPE_ACCESS)
+				computed_mask_bits = (mode >> 3) & 7;
+			else
+				computed_mask_bits |= duo_item->racl.group_obj & ~NO_ENTRY;
+			duo_item->racl.mask_obj = computed_mask_bits;
+		}
 #endif
-
+#ifdef SUPPORT_NFS4_ACLS
+	} else if (acl_type == NFS4_ACL_TYPE) {
+		duo_item->racl.nfs4_acl_len = read_varint(f);
+		duo_item->racl.nfs4_acl_data = new_array(char, duo_item->racl.nfs4_acl_len);
+		if (!duo_item->racl.nfs4_acl_data)
+			out_of_memory("recv_rsync_acl");
+		read_buf(f, duo_item->racl.nfs4_acl_data, duo_item->racl.nfs4_acl_len);
+#endif
+	}
 	duo_item->sacl = NULL;

 	return ndx;
 <at>  <at>  -793,10 +921,20  <at>  <at> 
 /* Receive the ACL info the sender has included for this file-list entry. */
 void receive_acl(int f, struct file_struct *file)
 {
-	F_ACL(file) = recv_rsync_acl(f, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode);
+	int acl_type = protocol_version >= 31 ? read_byte(f) : SMB_ACL_TYPE;

-	if (S_ISDIR(file->mode))
-		F_DIR_DEFACL(file) = recv_rsync_acl(f, &default_acl_list, SMB_ACL_TYPE_DEFAULT, 0);
+	if (acl_type == SMB_ACL_TYPE) {
+		F_ACL(file) = recv_rsync_acl(f, acl_type, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode);
+		if (S_ISDIR(file->mode))
+			F_DIR_DEFACL(file) = recv_rsync_acl(f, acl_type, &default_acl_list, SMB_ACL_TYPE_DEFAULT, 0);
+#ifdef SUPPORT_NFS4_ACLS
+	} else if (acl_type == NFS4_ACL_TYPE) {
+		F_ACL(file) = recv_rsync_acl(f, acl_type, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode);
+#endif
+	} else {
+		rprintf(FERROR, "receive_acl: unsupported ACL type (%u)\n", acl_type);
+		exit_cleanup(RERR_UNSUPPORTED);
+	}
 }

 static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type, item_list *racl_list)
 <at>  <at>  -943,18 +1081,59  <at>  <at> 
 }
 #endif

+#ifdef SUPPORT_NFS4_ACLS
+#define NFS4_ACL_NOTREADY(racl) (!(racl) || !(racl)->nfs4_acl_data)
+#else
+#define NFS4_ACL_NOTREADY(racl) (1)
+#endif
+
 static int set_rsync_acl(const char *fname, acl_duo *duo_item,
 			 SMB_ACL_TYPE_T type, stat_x *sxp, mode_t mode)
 {
+#ifdef SUPPORT_NFS4_ACLS
+	if (duo_item->racl.nfs4_acl_data) {
+#ifdef SUPPORT_XATTRS
+		if (am_root < 0)
+			/* --fake-super support: store ACL in an xattr. */
+			return set_xattr_nfs4acl(fname, duo_item->racl.nfs4_acl_data,
+						 duo_item->racl.nfs4_acl_len);
+#endif
+#ifdef HAVE_CHMOD
+		/* prefer chmod to equalize the ACLs - don't create redundant security descriptors
+		   - unfortunately ACLs can still compare unequal on Linux simply due to padding
+		 */
+		if (!BITS_EQUAL(sxp->st.st_mode, mode, CHMOD_BITS)) {
+			static rsync_acl temp_racl;
+
+			if (!do_chmod(fname, mode)) {
+				sxp->st.st_mode = mode;
+				if (!sys_nfs4acl_get_file(fname, &temp_racl)) {
+					if (rsync_acl_equal(&temp_racl, &duo_item->racl)) {
+						free(temp_racl.nfs4_acl_data);
+						return 0;
+					}
+					free(temp_racl.nfs4_acl_data);
+				}
+			}
+		}
+#endif
+		if (sys_nfs4acl_set_file(fname, &duo_item->racl)) {
+			rsyserr(FERROR_XFER, errno, "set_acl: sys_nfs4acl_set_file(%s)", fname);
+			return -1;
+		}
+		return 0;
+	}
+#endif
 	if (type == SMB_ACL_TYPE_DEFAULT
 	 && duo_item->racl.user_obj == NO_ENTRY) {
-		int rc;
+		int rc = 0;
 #ifdef SUPPORT_XATTRS
 		/* --fake-super support: delete default ACL from xattrs. */
 		if (am_root < 0)
 			rc = del_def_xattr_acl(fname);
 		else
 #endif
+		if (NFS4_ACL_NOTREADY(sxp->acc_acl))
 			rc = sys_acl_delete_def_file(fname);
 		if (rc < 0) {
 			rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_delete_def_file(%s)",
 <at>  <at>  -986,7 +1165,7  <at>  <at> 
 		free(buf);
 		return rc;
 #endif
-	} else {
+	} else if (NFS4_ACL_NOTREADY(sxp->acc_acl)) {
 		mode_t cur_mode = sxp->st.st_mode;
 		if (!duo_item->sacl
 		 && !pack_smb_acl(&duo_item->sacl, &duo_item->racl))
diff -uNr rsync-3.0.9.orig/rsync.h rsync-3.0.9/rsync.h
--- rsync-3.0.9.orig/rsync.h	2011-02-22 04:32:51.000000000 +0900
+++ rsync-3.0.9/rsync.h	2012-05-14 13:50:38.000000000 +0900
 <at>  <at>  -96,7 +96,8  <at>  <at> 
 			     == ((unsigned)(b2) & (unsigned)(mask)))

 /* update this if you make incompatible changes */
-#define PROTOCOL_VERSION 30
+#define PROTOCOL_VERSION 31
+#define SUPPORT_NFS4_ACLS 1

 /* This is used when working on a new protocol version in CVS, and should
  * be a new non-zero value for each CVS change that affects the protocol.
diff -uNr rsync-3.0.9.orig/xattrs.c rsync-3.0.9/xattrs.c
--- rsync-3.0.9.orig/xattrs.c	2011-09-23 01:02:21.000000000 +0900
+++ rsync-3.0.9/xattrs.c	2012-05-14 13:50:38.000000000 +0900
 <at>  <at>  -71,6 +71,10  <at>  <at> 
 #define XACC_ACL_ATTR RSYNC_PREFIX "%" XACC_ACL_SUFFIX
 #define XDEF_ACL_SUFFIX "dacl"
 #define XDEF_ACL_ATTR RSYNC_PREFIX "%" XDEF_ACL_SUFFIX
+#ifdef SUPPORT_NFS4_ACLS
+#define XNFS4ACL_SUFFIX "nfs4acl"
+#define XNFS4ACL_ATTR RSYNC_PREFIX "%" XNFS4ACL_SUFFIX
+#endif

 typedef struct {
 	char *datum, *name;
 <at>  <at>  -166,8 +170,8  <at>  <at> 
 /* On entry, the *len_ptr parameter contains the size of the extra space we
  * should allocate when we create a buffer for the data.  On exit, it contains
  * the length of the datum. */
-static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr,
-			    int no_missing_error)
+char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr,
+		     int no_missing_error)
 {
 	size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
 	size_t extra_len = *len_ptr;
 <at>  <at>  -938,6 +942,14  <at>  <at> 
 	return get_xattr_data(fname, name, len_p, 1);
 }

+#ifdef SUPPORT_NFS4_ACLS
+char *get_xattr_nfs4acl(const char *fname, size_t *len_p)
+{
+	*len_p = 0; /* no extra data alloc needed from get_xattr_data() */
+	return get_xattr_data(fname, XNFS4ACL_ATTR, len_p, 1);
+}
+#endif
+
 int set_xattr_acl(const char *fname, int is_access_acl, const char *buf, size_t buf_len)
 {
 	const char *name = is_access_acl ? XACC_ACL_ATTR : XDEF_ACL_ATTR;
 <at>  <at>  -950,6 +962,19  <at>  <at> 
 	return 0;
 }

+#ifdef SUPPORT_NFS4_ACLS
+int set_xattr_nfs4acl(const char *fname, const char *buf, size_t buf_len)
+{
+	if (sys_lsetxattr(fname, XNFS4ACL_ATTR, buf, buf_len) < 0) {
+		rsyserr(FERROR_XFER, errno,
+			"set_xattr_nfs4acl: lsetxattr(\"%s\",\"%s\") failed",
+			full_fname(fname), XNFS4ACL_ATTR);
+		return -1;
+	}
+	return 0;
+}
+#endif
+
 int del_def_xattr_acl(const char *fname)
 {
 	return sys_lremovexattr(fname, XDEF_ACL_ATTR);
--

-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html
Wayne Davison | 21 Jul 2012 21:30
Picon
Favicon

Re: [patch] NFSv4/ZFS ACLs

On Sat, Jun 9, 2012 at 2:19 PM, Richard P <richardp345 <at> gmail.com> wrote:
This is a PoC patch for NFSv4/ZFS ACLs.

Much appreciated.  Your patch looks like it has some nice ideas that I'd like to get integrated into rsync.  Sorry for the huge delay in getting back to you.

Linux and Solaris represent NFSv4 ACLs differently and provide no API to serialize them in a platform-independent format so this patch treats the ACLs as platform-dependent opaque data.

There are two potential methods for sending  non-posix ACL info -- change the ACL-sending code (as you have done) or put the info into xattr data.  In retrospect, I think it would have been better to have unified the ACL and xattr code in rsync into a single aux-data-sending protocol from the start, rather than keeping things separate.  So, one possibility is that we should just be faking system xattr data for solaris NFSv4 data, and sending Linux NFSv4 xattr data via xattrs.  That would require the code to have a mode where it could send just the "system" ACL xattrs without affecting user xattrs (with the ability to send system xattrs being something new).

So, I'm debating whether I like your method or not.  It looks like it does a nice job of implementing a revised ACL protocol, with just a little more todos and rearranging (as you noted).  What do you (and anyone else) think about an xattr solution?  One consideration is the coalescing of common values -- this is currently done separately for ACLs and xattrs, which is probably more efficient (since the matching of ACLs is more likely to be a small set), and the xattr-matching code is not very efficient.  So, I'm not sure which I like better just yet.

 * wasn't sure how to SUBPROTOCOL_VERSION worked so I've revved PROTOCOL_VERSION to 31

I'm already using protocol 31 in the dev version, so your version will fail to sync with a future released 3.1.0 rsync (which is when the subprotocol version goes to 0).  For a non-standard protocol, you should increase the PROTOCOL_VERSION and set the SUBPROTOCOL_VERSION to a unique value (never one near an official value -- pick any fairly big, unique number).

..wayne..
--

-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html
f-rsync | 21 Jul 2012 22:42
Picon
Favicon

[patch] NFSv4/ZFS ACLs

If you're thinking about changing the way attrs work, here's a
question for you.  I just recently started trying to use them for the
first time, in backing up a Windows host via a wrapper script that
runs the remote rsync under Cygwin on the Windows side, by mounting
the source disk as a VSS snapshot as long as the Windows-side rsync is
running.  This enables dirvish, running on a Linux host, to actually
be able to read all the files in the Windows filesytem without tripping
over Windows file locks.  The wrapper must call rsync directly, rather
than setting things up and then letting a separate network connection
call rsync, because apparently the VSS mapping is only valid for the
duration of the invoking program.  (Search for tb-rsync-vss.)

But that wrapper script does not (yet) have a way to pass any args
to the rsync that it calls internally, yet to actually preserve file
metadata from the Windows side, I need to invoke --fake-super on the
Windows side via --rsync-path.  So while the wrapper script wants me
to do "--rsync-path=where-rsync-is-on-the-windows-side" it can't cope
with "--rsync-path=where-rsync-is-on-the-windows-side --fake-super";
its argument parsing doesn't expect additional args to accompany its
version of rsync-path, and it has no way to specify remote-side-only
extra args to rsync.  [In fact, it parses various args "by hand" out
of a single string that's passed to the script by abusing the final
"path" argument from the invoking rsync, and --rsync-path is one.]

I've asked the maintainer of that script to see if he can add that
functionality, which shouldn't be hard once he gets to it.  But it's
peculiar that rsync does it this way in the first place.  I understand
that one wants individual control of which end of the connection is
(or is not) doing --fake-super, but wouldn't it be somewhat more
logical to take this out of --rsync-path and instead make something
like --local-fake-super and --remote-fake-super instead, so the args
could be passed around in a consistent fashion?  Also, by explicitly
mentioning "local" in "fake-super", you'd be warning people that
they're only looking at half the story; I see various cries for help
on the net where people couldn't get --fake-super to work because they
didn't read that section of the manpage carefully enough to realize
that they needed to specify --fake-super inside --rsync-path instead
of or in addition to its use in the normal set of args to the local
rsync.

In fact, --fake-super seems to be a very peculiar exception to the way
rsync args are handled---so peculiar that I hadn't noticed it worked
that way before I had to use it, and the script author had no idea
that the remote rsync could be getting args that way, either, until I
pointed him at that particular manpage section.

Of course, making such a change would still have to honor the existing
args for backward compatibility in existing scripts.  *sigh*...

P.S.  I'm using --fake-super on the sending side in the first place
because, in part, there are many instances of unknown users and groups
which Cygwin is mapping into -1, which can't be handled on the Linux
side of the connection and cause rsync to complain.  (And I value
those complaints!  They warned me I was doing something wrong.)  But
in general, since I'm trying to actually back up the Windows side, I'd
really like to hang onto even non-POSIX metadata, so it seems like I
want --fake-super on the sending side and --fake-super --xattrs --acls
on the receiving side to run the backup.  Correct?  [My normal backups
of other Linux hosts don't bother with --fake-super on either end.]
--

-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html

Wayne Davison | 22 Jul 2012 19:23
Picon
Favicon

Re: [patch] NFSv4/ZFS ACLs

On Sat, Jul 21, 2012 at 1:42 PM, <f-rsync <at> media.mit.edu> wrote:
it has no way to specify remote-side-only extra args to rsync.

If you can rebuild the rsync, you could add the remote-option.diff patch (adding an option that will come standard in 3.1.0).  That lets you specify --remote-option=--fake-super.  Or perhaps look into having the remote be an rsync daemon, where you can specify "fake super" in the config file.

make something like --local-fake-super and --remote-fake-super instead
 
Yeah, I probably should have done something like that early on, e.g. --fake-super={local,remote,sender,receiver,both} (I like the arg rather than the separate options). I suppose I could still do that if I come up with a slightly different option name.  I like --faux-super=WHERE.

..wayne..
--

-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html

Gmane