Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 9 Feb 2011 14:10:42 -0700
From:      Shawn Webb <lattera@gmail.com>
To:        Tim Kientzle <tim@kientzle.com>
Cc:        FreeBSD-current <freebsd-current@freebsd.org>
Subject:   Re: setfacl Recursive Functionality
Message-ID:  <AANLkTim9anqAqSLH=5OMi_dTyAsqY2xCL37KM8MmrZdR@mail.gmail.com>
In-Reply-To: <AANLkTin2XR9Ra4LK-rgh94XaAUB6jY_sg0n51GdbgnQ2@mail.gmail.com>
References:  <AANLkTi=%2BWtmRz07m=Cg7hbXJGw7eWRHC1ASGeufTSLBB@mail.gmail.com> <80373F51-25C7-48A0-8920-3444A98D857F@kientzle.com> <AANLkTin2XR9Ra4LK-rgh94XaAUB6jY_sg0n51GdbgnQ2@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Included in the attached patch is the refactor using fts(3) and with the -L
and -H options. I'm still looking for feedback and suggestions on how to
improve the patch. I'll also port these changes over to my getfacl patch.

If anyone's interested in following up-to-date development of the patch, the
link to it on github is
https://github.com/lattera/patches/blob/master/freebsd/setfacl_recursive.patch

I'd like to take the time to address why I created the
remove_invalid_inherit function since I got a private email asking why it
existed. Other than symbolic links, non-directory entries cannot have
inheritance set. That function prevents attempting to set inheritance flags
on non-directory entries when doing a recursive call. That way, you can run
`setfacl -R -m user:<user>:read_data:file_inherit/dir_inherit:allow
<directory>` and not run into errors.

Thanks,

Shawn

On Tue, Feb 8, 2011 at 7:51 PM, Shawn Webb <lattera@gmail.com> wrote:

> On Tue, Feb 8, 2011 at 7:35 PM, Tim Kientzle <tim@kientzle.com> wrote:
>
>> On Feb 8, 2011, at 9:58 AM, Shawn Webb wrote:
>> > I've just finished a patch to add recursive functionality to setfacl.
>> Before
>> > I officially submit it, I'd like a few suggestions on how to improve the
>> > patch.
>> >
>> > The part I'm worried about involves the #define directive at top. I'm
>> not
>> > sure what ramifications using that define might have. I needed it for my
>> > remove_invalid_inherit() function to work.
>>
>> You should certainly not need
>>   #define _ACL_PRIVATE
>> for any user-space utilities.  What exactly is the
>> problem without that?
>>
>> Your approach to directory walking here
>> is a little simplistic.  In particular, you're storing
>> every filename for the entire tree in memory,
>> which is a problem for large filesystems.
>>
>> It would be much better to refactor the code so that
>> the actual ACL update was in a function and then
>> recurse_directory should call that function for
>> each filename as it visited it.  That will reduce
>> the memory requirements significantly.
>>
>> You should also take a look at fts(3).  In particular,
>> you'll want to implement the BSD-standard
>> -L/-P/-H options, and fts(3) makes that much easier.
>> (-L always follows symlinks, -P never follows symlinks,
>> -H follows symlinks on the command line).
>>
>> Tim
>>
>>
> Great suggestions. I'll definitely look at implementing that functionality.
>
> As a side note, it looks like my setfacl patch segfaults on freebsd-current
> r218075 with the zpool v28 patchset applied. I wrote it on freebsd 8.2-RC3
> with zpool v15.
>
> Thanks,
>
> Shawn
>

[-- Attachment #2 --]
--- /usr/src/bin/setfacl/setfacl.c	2011-02-03 12:11:02.303496318 -0700
+++ bin/setfacl/setfacl.c	2011-02-09 13:59:06.961538056 -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,8 @@
 #include <sys/stat.h>
 #include <sys/acl.h>
 #include <sys/queue.h>
+#include <fts.h>
+#include <dirent.h>
 
 #include <err.h>
 #include <errno.h>
@@ -44,6 +46,8 @@
 
 static void	add_filename(const char *filename);
 static void	usage(void);
+static void	recurse_directory(char *const *paths, int r_flag, int l_flag, int big_h_flag);
+static acl_t	remove_invalid_inherit(const char *path, acl_t acl, int l_flag);
 
 static void
 add_filename(const char *filename)
@@ -62,34 +66,106 @@
 static void
 usage(void)
 {
-
-	fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] "
+	fprintf(stderr, "usage: setfacl [-bdhkLnR] [-a position entries] "
 	    "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n");
 	exit(1);
 }
 
+static void
+recurse_directory(char *const *paths, int r_flag, int l_flag, int big_h_flag)
+{
+	FTS *ftsp;
+	FTSENT *p, *chp;
+	int fts_options = FTS_NOCHDIR;
+	unsigned int i;
+	
+	fts_options |= (l_flag == 1) ? FTS_LOGICAL : FTS_PHYSICAL;
+	if (big_h_flag)
+		fts_options |= FTS_COMFOLLOW;
+	
+	if (r_flag)
+	{
+		ftsp = fts_open(paths, fts_options, NULL);
+		if (ftsp == NULL)
+			return;
+		
+		chp = fts_children(ftsp, 0);
+		if (chp == NULL)
+			return;
+		
+		while ((p = fts_read(ftsp)) != NULL) {
+			if (l_flag == 0 && p->fts_info & FTS_D)
+				continue;
+			else if (l_flag == 1 && p->fts_info & FTS_DP)
+				continue;
+			
+			add_filename(strdup(p->fts_path));
+		}
+		
+		fts_close(ftsp);
+	} else
+		for (i = 0; paths[i] != NULL; i++)
+			add_filename(paths[i]);
+}
+
+static acl_t
+remove_invalid_inherit(const char *path, acl_t acl, int l_flag)
+{
+	acl_t acl_new;
+	int acl_brand;
+	acl_entry_t entry;
+	int entry_id;
+	struct stat sb;
+	
+	acl_get_brand_np(acl, &acl_brand);
+	if (acl_brand != ACL_BRAND_NFS4)
+		return acl;
+	
+	if (l_flag == 1) {
+		if (stat(path, &sb) == -1)
+			return acl;
+	} else
+		if (lstat(path, &sb) == -1)
+			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 local_error, carried_error, ch, entry_number, ret;
+	int h_flag, r_flag, l_flag, big_h_flag;
 	struct sf_file *file;
 	struct sf_entry *entry;
-	const char *fn_dup;
+	char *fn_dup;
 	char *end;
+	char **files=NULL;
+	unsigned int numfiles=0;
 	struct stat sb;
 
 	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 = l_flag = big_h_flag = 0;
 
 	TAILQ_INIT(&entrylist);
 	TAILQ_INIT(&filelist);
 
-	while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1)
+	while ((ch = getopt(argc, argv, "HLRM:X:a:bdhkm:nx:")) != -1)
 		switch(ch) {
 		case 'M':
 			entry = zmalloc(sizeof(struct sf_entry));
@@ -167,6 +243,15 @@
 			}
 			TAILQ_INSERT_TAIL(&entrylist, entry, next);
 			break;
+		case 'R':
+			r_flag = 1;
+			break;
+		case 'L':
+			l_flag = 1;
+			break;
+		case 'H':
+			big_h_flag = 1;
+			break;
 		default:
 			usage();
 			break;
@@ -189,11 +274,18 @@
 			fn_dup = strdup(filename);
 			if (fn_dup == NULL)
 				err(1, "strdup() failed");
-			add_filename(fn_dup);
+			files = realloc(files, ++numfiles * sizeof(char **));
+			if (files == NULL)
+				err(1, "realloc() failed");
+			files[numfiles-1] = (char *)fn_dup;
 		}
+		
+		files = realloc(files, ++numfiles * sizeof(char **));
+		files[numfiles-1] = NULL;
 	} else
-		for (i = 0; i < argc; i++)
-			add_filename(argv[i]);
+		files = argv;
+	
+	recurse_directory(files, r_flag, l_flag, big_h_flag);
 
 	/* cycle through each file */
 	TAILQ_FOREACH(file, &filelist, next) {
@@ -250,12 +342,24 @@
 
 			switch(entry->op) {
 			case OP_ADD_ACL:
+				acl_backup = entry->acl;
+				entry->acl = remove_invalid_inherit(file->filename, entry->acl, l_flag);
 				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(file->filename, entry->acl, l_flag);
 				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:

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?AANLkTim9anqAqSLH=5OMi_dTyAsqY2xCL37KM8MmrZdR>