--- /usr/src/bin/setfacl/setfacl.c	2011-02-03 12:11:02.303496318 -0700
+++ /tank/jails/dev/usr/src/bin/setfacl/setfacl.c	2011-02-08 10:47:18.190936489 -0700
@@ -23,7 +23,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
-
+#define _ACL_PRIVATE
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
@@ -32,6 +32,7 @@
 #include <sys/stat.h>
 #include <sys/acl.h>
 #include <sys/queue.h>
+#include <dirent.h>
 
 #include <err.h>
 #include <errno.h>
@@ -44,6 +45,8 @@
 
 static void	add_filename(const char *filename);
 static void	usage(void);
+static void	recurse_directory(const char *dirname, int r_flag);
+static acl_t	remove_invalid_inherit(struct stat *sb, acl_t acl);
 
 static void
 add_filename(const char *filename)
@@ -63,19 +66,83 @@
 usage(void)
 {
 
-	fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] "
+	fprintf(stderr, "usage: setfacl [-bdhknR] [-a position entries] "
 	    "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n");
 	exit(1);
 }
 
+static void
+recurse_directory(const char *dirname, int r_flag)
+{
+	DIR *dirp;
+	struct dirent *ent;
+	struct stat sb;
+	char newpath[PATH_MAX+1];
+	
+	if (stat(dirname, &sb) == -1) {
+		warn("%s: stat() failed", dirname);
+		return;
+	}
+	
+	add_filename(dirname);
+	
+	if (r_flag == 0 || S_ISDIR(sb.st_mode) == 0)
+		return;
+	
+	dirp = opendir(dirname);
+	while ((ent = readdir(dirp)) != NULL) {
+		if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
+			continue;
+		
+		snprintf(newpath, PATH_MAX, "%s/%s", dirname, ent->d_name); 
+		
+		if (stat(newpath, &sb) == -1) {
+			warn("%s: stat() failed", newpath);
+			continue;
+		}
+		
+		if (S_ISDIR(sb.st_mode))
+			recurse_directory(newpath, r_flag);
+		
+		add_filename(strdup(newpath));
+	}
+	closedir(dirp);
+}
+
+static acl_t
+remove_invalid_inherit(struct stat *sb, acl_t acl)
+{
+	acl_t acl_new;
+	int acl_brand;
+	acl_entry_t entry;
+	int entry_id;
+	
+	acl_get_brand_np(acl, &acl_brand);
+	if (acl_brand != ACL_BRAND_NFS4)
+		return acl;
+	
+	if (S_ISDIR(sb->st_mode) != 0)
+		return acl;
+	
+	acl_new = acl_dup(acl);
+	
+	entry_id = ACL_FIRST_ENTRY;
+	while (acl_get_entry(acl_new, entry_id, &entry) == 1) {
+		entry_id = ACL_NEXT_ENTRY;
+		entry->ae_flags = 0;
+	}
+	
+	return acl_new;
+}
+
 int
 main(int argc, char *argv[])
 {
-	acl_t acl;
+	acl_t acl, acl_backup;
 	acl_type_t acl_type;
 	char filename[PATH_MAX];
 	int local_error, carried_error, ch, i, entry_number, ret;
-	int h_flag;
+	int h_flag, r_flag;
 	struct sf_file *file;
 	struct sf_entry *entry;
 	const char *fn_dup;
@@ -84,12 +151,12 @@
 
 	acl_type = ACL_TYPE_ACCESS;
 	carried_error = local_error = 0;
-	h_flag = have_mask = have_stdin = n_flag = need_mask = 0;
+	h_flag = have_mask = have_stdin = n_flag = need_mask = r_flag = 0;
 
 	TAILQ_INIT(&entrylist);
 	TAILQ_INIT(&filelist);
 
-	while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1)
+	while ((ch = getopt(argc, argv, "RM:X:a:bdhkm:nx:")) != -1)
 		switch(ch) {
 		case 'M':
 			entry = zmalloc(sizeof(struct sf_entry));
@@ -167,6 +234,9 @@
 			}
 			TAILQ_INSERT_TAIL(&entrylist, entry, next);
 			break;
+		case 'R':
+			r_flag = 1;
+			break;
 		default:
 			usage();
 			break;
@@ -193,7 +263,7 @@
 		}
 	} else
 		for (i = 0; i < argc; i++)
-			add_filename(argv[i]);
+			recurse_directory(argv[i], r_flag);
 
 	/* cycle through each file */
 	TAILQ_FOREACH(file, &filelist, next) {
@@ -250,12 +320,24 @@
 
 			switch(entry->op) {
 			case OP_ADD_ACL:
+				acl_backup = entry->acl;
+				entry->acl = remove_invalid_inherit(&sb, entry->acl);
 				local_error += add_acl(entry->acl,
 				    entry->entry_number, &acl, file->filename);
+				if (entry->acl != acl_backup) {
+					acl_free(entry->acl);
+					entry->acl = acl_backup;
+				}
 				break;
 			case OP_MERGE_ACL:
+				acl_backup = entry->acl;
+				entry->acl = remove_invalid_inherit(&sb, entry->acl);
 				local_error += merge_acl(entry->acl, &acl,
 				    file->filename);
+				if (entry->acl != acl_backup) {
+					acl_free(entry->acl);
+					entry->acl = acl_backup;
+				}
 				need_mask = 1;
 				break;
 			case OP_REMOVE_EXT:
