The following patches add support for printing ACL's via 'ls -e/-E'.
This is supported on platforms that have some sort of acl_to_text
function, which is most of the platforms that the acl module supports.

I tested this on Linux, OpenSolaris, and FreeBSD.

-- David
From 2b81cf5a5eb32c5e500c892fd1178aef624bded5 Mon Sep 17 00:00:00 2001
From: David Bartley <dtbar...@csclub.uwaterloo.ca>
Date: Sat, 9 May 2009 03:29:22 -0400
Subject: [PATCH] Add ability to print acl's from ls

* src/ls.c: Add ability to print acl's from ls.
---
 src/ls.c |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/src/ls.c b/src/ls.c
index 795d1ed..8ee797e 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -193,6 +193,10 @@ struct fileinfo
     /* For long listings, true if the file has an access control list,
        or an SELinux security context.  */
     enum acl_type acl_type;
+
+    /* For long listings, the text of the access control lists.  */
+    char *acl;
+    char *defacl;
   };
 
 #define LEN_STR_PAIR(s) sizeof (s) - 1, s
@@ -468,6 +472,11 @@ static uintmax_t file_output_block_size = 1;
    strange characters in file names.  */
 static bool dired;
 
+/* Print acl's; optionally print short acl's.  */
+static bool print_acl;
+static bool print_short_acl;
+static bool print_acl_indents;
+
 /* `none' means don't mention the type of files.
    `slash' means mention directories only, with a '/'.
    `file_type' means mention file types.
@@ -775,6 +784,8 @@ enum
 
 static struct option const long_options[] =
 {
+  {"acl", no_argument, NULL, 'e'},
+  {"short-acl", no_argument, NULL, 'E'},
   {"all", no_argument, NULL, 'a'},
   {"escape", no_argument, NULL, 'b'},
   {"directory", no_argument, NULL, 'd'},
@@ -1600,7 +1611,7 @@ decode_switches (int argc, char **argv)
     {
       int oi = -1;
       int c = getopt_long (argc, argv,
-			   "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1",
+			   "abcdefghiklmnopqrstuvw:xABCDEFGHI:LNQRST:UXZ1",
 			   long_options, &oi);
       if (c == -1)
 	break;
@@ -1623,6 +1634,10 @@ decode_switches (int argc, char **argv)
 	  immediate_dirs = true;
 	  break;
 
+	case 'e':
+	  print_acl = true;
+	  break;
+
 	case 'f':
 	  /* Same as enabling -a -U and disabling -l -s.  */
 	  ignore_mode = IGNORE_MINIMAL;
@@ -1739,6 +1754,11 @@ decode_switches (int argc, char **argv)
 	  dired = true;
 	  break;
 
+	case 'E':
+	  print_acl = true;
+	  print_short_acl = true;
+	  break;
+
 	case 'F':
 	  indicator_style = classify;
 	  break;
@@ -1915,6 +1935,12 @@ decode_switches (int argc, char **argv)
 	}
     }
 
+  if (print_acl)
+    {
+      print_acl_indents = isatty (STDOUT_FILENO);
+      format = long_format;
+    }
+
   max_idx = MAX (1, line_length / MIN_COLUMN_WIDTH);
 
   filename_quoting_options = clone_quoting_options (NULL);
@@ -2629,6 +2655,10 @@ clear_files (void)
       free (f->linkname);
       if (f->scontext != UNKNOWN_SECURITY_CONTEXT)
 	freecon (f->scontext);
+      if (f->acl)
+	free_acl_text (f->acl);
+      if (f->defacl)
+	free_acl_text (f->defacl);
     }
 
   cwd_n_used = 0;
@@ -2805,7 +2835,22 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
 
 	  if (err == 0 && format == long_format)
 	    {
-	      int n = file_has_acl (absolute_name, &f->stat);
+	      int n;
+
+	      if (print_acl)
+		{
+		  int flags = 0;
+		  if (print_short_acl)
+		    flags |= GL_ACL_SHORT_FORMAT;
+		  if (numeric_ids)
+		    flags |= GL_ACL_NUMERIC_IDS;
+		  if (print_acl_indents)
+		    flags |= GL_ACL_PRINT_INDENTS;
+		  n = file_has_acl (absolute_name, &f->stat, flags,
+				    &f->acl, &f->defacl);
+		}
+	      else
+		n = file_has_acl (absolute_name, &f->stat, 0, NULL, NULL);
 	      err = (n < 0);
 	      have_acl = (0 < n);
 	    }
@@ -3532,6 +3577,27 @@ format_group_width (gid_t g)
   return format_user_or_group_width (numeric_ids ? NULL : getgroup (g), g);
 }
 
+/* Print an access control list.  */
+static int
+format_acl (char *text, const char *prefix)
+{
+  int flags = 0;
+
+  char *line;
+  line = strtok (text, "\n");
+  if (line)
+    {
+      do
+	{
+	  DIRED_PUTCHAR ('\n');
+	  DIRED_FPUTS_LITERAL ("    ", stdout);
+	  DIRED_FPUTS (prefix, stdout, strlen (prefix));
+	  DIRED_FPUTS (line, stdout, strlen (line));
+	}
+      while (line = strtok (NULL, "\n"));
+    }
+}
+
 
 /* Print information about F in long format.  */
 
@@ -3758,6 +3824,11 @@ print_long_format (const struct fileinfo *f)
     }
   else if (indicator_style != none)
     print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
+
+  if (f->acl)
+    format_acl (f->acl, "");
+  if (f->defacl)
+    format_acl (f->defacl, _("default:"));
 }
 
 /* Output to OUT a quoted representation of the file name NAME,
@@ -4537,6 +4608,12 @@ Mandatory arguments to long options are mandatory for short options too.\n\
   -D, --dired                generate output designed for Emacs' dired mode\n\
 "), stdout);
       fputs (_("\
+  -e, --acl                  print any access control lists of each file\n\
+                               (implies long listing format)\n\
+  -E, --short-acl            like -e, but print a shorter form of the access\n\
+                                control list if possible\n\
+"), stdout);
+      fputs (_("\
   -f                         do not sort, enable -aU, disable -ls --color\n\
   -F, --classify             append indicator (one of */=>@|) to entries\n\
       --file-type            likewise, except do not append `*'\n\
-- 
1.5.6.5

From ab7e231a8239c3d85e0ac0c97513a6100effabd2 Mon Sep 17 00:00:00 2001
From: David Bartley <dtbar...@csclub.uwaterloo.ca>
Date: Sat, 9 May 2009 03:23:19 -0400
Subject: [PATCH] Add support for returning access control list text.

---
 ChangeLog                 |   11 +++
 lib/acl.h                 |   10 ++-
 lib/file-has-acl.c        |  156 +++++++++++++++++++++++++++++++++++++++++----
 lib/free-acl-text.c       |   46 +++++++++++++
 m4/acl.m4                 |    2 +-
 modules/acl               |    3 +-
 tests/test-file-has-acl.c |    3 +-
 7 files changed, 213 insertions(+), 18 deletions(-)
 create mode 100644 lib/free-acl-text.c

diff --git a/ChangeLog b/ChangeLog
index 2f2055d..be88e39 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2009-05-09  David Bartley  <dtbar...@csclub.uwaterloo.ca>
+
+	Add support for returning access control list text
+	* lib/file-has-acl.c: Add additional parameters for returning access
+	and default access control lists.
+	* lib/free-acl-text.c: Add free_acl_text function.
+	* lib/acl.h: Add free_acl_text function and file_has_acl flags.
+	* m4/acl.m4: Check for acl_to_any_text.
+	* modules/acl: Add free-acl-text.c
+	* tests/test-file-has-acl.c: Update per changes to file_has_acl.
+
 2009-05-08  Bruno Haible  <br...@clisp.org>
 
 	* lib/sys_socket.in.h (_SS_PADSIZE): Use a conditional expression
diff --git a/lib/acl.h b/lib/acl.h
index fcb00f5..3fdb3bc 100644
--- a/lib/acl.h
+++ b/lib/acl.h
@@ -1,6 +1,6 @@
 /* acl.c - access control lists
 
-   Copyright (C) 2002, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,7 +20,13 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
-int file_has_acl (char const *, struct stat const *);
+/* Flags for file_has_acl.  */
+#define GL_ACL_SHORT_FORMAT	0x01
+#define GL_ACL_NUMERIC_IDS	0x02
+#define GL_ACL_PRINT_INDENTS	0x04
+
+int file_has_acl (char const *, struct stat const *, int, char **, char**);
+void free_acl_text (char *);
 int copy_acl (char const *, int, char const *, int, mode_t);
 int set_acl (char const *, int, mode_t);
 int qset_acl (char const *, int, mode_t);
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index c3b77c3..e3505ca 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -15,7 +15,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-   Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible.  */
+   Written by Paul Eggert, Andreas Grünbacher, Bruno Haible, and
+   David Bartley.  */
 
 #include <config.h>
 
@@ -120,6 +121,17 @@ acl_access_nontrivial (acl_t acl)
 
 #elif USE_ACL && HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
 
+static void
+replace_chr (char *str, char c1, char c2)
+{
+  while (*str)
+    {
+      if (*str == c1)
+	*str = c2;
+      str++;
+    }
+}
+
 # if !defined ACL_NO_TRIVIAL /* Solaris <= 10, Cygwin */
 
 /* Test an ACL retrieved with GETACL.
@@ -269,12 +281,22 @@ acl_nontrivial (struct acl *a)
 
 /* Return 1 if NAME has a nontrivial access control list, 0 if NAME
    only has no or a base access control list, and -1 (setting errno)
-   on error.  SB must be set to the stat buffer of FILE.  */
+   on error.  SB must be set to the stat buffer of FILE. If ATEXT and
+   DTEXT are non-null they will be filled with a text representation
+   of the access and default (if applicable) access control list if
+   it is nontrivial.  FLAGS contains formatting flags for ATEXT and
+   DTEXT. atext and dtext should be free'd by calling free_acl_text. */
 
 int
-file_has_acl (char const *name, struct stat const *sb)
+file_has_acl (char const *name, struct stat const *sb, int flags,
+	      char **atext, char **dtext)
 {
 #if USE_ACL
+  if (dtext)
+    *dtext = NULL;
+  if (atext)
+    *atext = NULL;
+
   if (! S_ISLNK (sb->st_mode))
     {
 # if HAVE_ACL_GET_FILE
@@ -283,14 +305,14 @@ file_has_acl (char const *name, struct stat const *sb)
       /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
       int ret;
 
-      if (HAVE_ACL_EXTENDED_FILE) /* Linux */
+      if (! atext && ! dtext && HAVE_ACL_EXTENDED_FILE) /* Linux */
 	{
 	  /* On Linux, acl_extended_file is an optimized function: It only
 	     makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
 	     ACL_TYPE_DEFAULT.  */
 	  ret = acl_extended_file (name);
 	}
-      else /* FreeBSD, MacOS X, IRIX, Tru64 */
+      else /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
 	{
 #  if HAVE_ACL_TYPE_EXTENDED /* MacOS X */
 	  /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
@@ -302,17 +324,47 @@ file_has_acl (char const *name, struct stat const *sb)
 	  if (acl)
 	    {
 	      ret = acl_extended_nontrivial (acl);
+	      if (ret && atext)
+		{
+		  *atext = acl_to_text (acl, &len);
+		  if (! *atext)
+		    ret = -1;
+		}
 	      acl_free (acl);
 	    }
 	  else
 	    ret = -1;
-#  else /* FreeBSD, IRIX, Tru64 */
+#  else /* Linux, FreeBSD, IRIX, Tru64 */
 	  acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
 	  if (acl)
 	    {
 	      int saved_errno;
+	      ssize_t len;
 
 	      ret = acl_access_nontrivial (acl);
+	      if (ret && atext)
+		{
+#   if HAVE_ACL_TO_SHORT_TEXT /* IRIX */
+		  if (flags & GL_ACL_SHORT_FORMAT)
+		    *atext = acl_to_short_text (acl, &len);
+		  else
+		    *atext = acl_to_text (acl, &len);
+#   elif HAVE_ACL_TO_ANY_TEXT /* Linux */
+                  int flags2 = TEXT_SOME_EFFECTIVE;
+                  if (flags & GL_ACL_SHORT_FORMAT)
+                    flags2 |= TEXT_ABBREVIATE;
+                  if (flags & GL_ACL_NUMERIC_IDS)
+                    flags2 |= TEXT_NUMERIC_IDS;
+                  if (flags & GL_ACL_PRINT_INDENTS)
+                    flags2 |= TEXT_SMART_INDENT;
+                  *atext = acl_to_any_text (acl, "", '\n', flags2);
+#   else /* FreeBSD, Tru64 */
+
+                  *atext = acl_to_text (acl, &len);
+#   endif
+		  if (! *atext)
+		    ret = -1;
+		}
 	      saved_errno = errno;
 	      acl_free (acl);
 	      errno = saved_errno;
@@ -320,19 +372,47 @@ file_has_acl (char const *name, struct stat const *sb)
 	      /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always
 		 returns NULL with errno not set.  There is no point in
 		 making this call.  */
-#   else /* FreeBSD, IRIX */
+#   else /* Linux, FreeBSD, IRIX */
 	      /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS)
 		 and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory
 		 either both succeed or both fail; it depends on the
 		 filesystem.  Therefore there is no point in making the second
-		 call if the first one already failed.  */
-	      if (ret == 0 && S_ISDIR (sb->st_mode))
+		 call if the first one already failed.  If the access acl is
+		 non-trivial and dtext is null we don't need to continue.  */
+	      if ((ret == 0 || (ret == 1 && dtext)) && S_ISDIR (sb->st_mode))
 		{
 		  acl = acl_get_file (name, ACL_TYPE_DEFAULT);
 		  if (acl)
 		    {
-		      ret = (0 < acl_entries (acl));
+		      if (acl_entries (acl) > 0)
+			{
+			  ret = 1;
+			  if (dtext)
+			    {
+#    if HAVE_ACL_TO_SHORT_TEXT /* IRIX */
+			      if (! (flags & GL_ACL_SHORT_FORMAT))
+				*dtext = acl_to_short_text (acl, &len);
+			      else
+				*dtext = acl_to_text (acl, &len);
+#    elif HAVE_ACL_TO_ANY_TEXT /* Linux */
+			      int flags2 = TEXT_SOME_EFFECTIVE;
+			      if (flags & GL_ACL_SHORT_FORMAT)
+				flags2 |= TEXT_ABBREVIATE;
+			      if (flags & GL_ACL_NUMERIC_IDS)
+				flags2 |= TEXT_NUMERIC_IDS;
+			      if (flags & GL_ACL_PRINT_INDENTS)
+				flags2 |= TEXT_SMART_INDENT;
+			      *dtext = acl_to_any_text (acl, "", '\n', flags2);
+#    else /* FreeBSD */
+			      *dtext = acl_to_text (acl, &len);
+#    endif
+			      if (! *dtext)
+				ret = -1;
+			    }
+			}
+		      saved_errno = errno;
 		      acl_free (acl);
+		      errno = saved_errno;
 		    }
 		  else
 		    ret = -1;
@@ -344,7 +424,22 @@ file_has_acl (char const *name, struct stat const *sb)
 #  endif
 	}
       if (ret < 0)
-	return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
+	{
+	  if (atext)
+	    {
+	      free_acl_text (*atext);
+	      *atext = NULL;
+	    }
+/* Linux, FreeBSD, IRIX */
+#  if HAVE_ACL_GET_FILE && ! HAVE_ACL_TYPE_EXTENDED && ! HAVE_ACL_FREE_TEXT
+	  if (dtext)
+	    {
+	      free_acl_text (*dtext);
+	      *dtext = NULL;
+	    }
+#  endif
+	  return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
+	}
       return ret;
 
 # elif HAVE_ACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
@@ -354,7 +449,32 @@ file_has_acl (char const *name, struct stat const *sb)
       /* Solaris 10 (newer version), which has additional API declared in
 	 <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
 	 acl_fromtext, ...).  */
-      return acl_trivial (name);
+      if (atext)
+	{
+	  int ret;
+	  acl_t *aclp;
+
+	  ret = acl_get (name, ACL_NO_TRIVIAL, &aclp);
+	  if (ret == 0)
+	    {
+	      if (aclp)
+		{
+		  int flags2 = 0;
+		  if (flags & GL_ACL_SHORT_FORMAT)
+		    flags2 |= ACL_COMPACT_FMT;
+		  *atext = acl_totext (aclp, flags2);
+		  if (*atext)
+		    replace_chr (*atext, ',', '\n');
+		  else
+		    ret = -1;
+		  acl_free (aclp);
+		}
+	      else
+		ret = 0;
+	    }
+	}
+      else
+	return acl_trivial (name);
 
 #  else /* Solaris, Cygwin, general case */
 
@@ -384,7 +504,7 @@ file_has_acl (char const *name, struct stat const *sb)
 	       returns only 3 entries for files with no ACL.  But this is safe:
 	       If there are more than 4 entries, there cannot be only the
 	       "user::", "group::", "other:", and "mask:" entries.  */
-	    if (count > 4)
+	    if (! atext && count > 4)
 	      return 1;
 
 	    entries = (aclent_t *) malloc (count * sizeof (aclent_t));
@@ -397,6 +517,16 @@ file_has_acl (char const *name, struct stat const *sb)
 	      {
 		if (acl_nontrivial (count, entries))
 		  {
+		    if (atext)
+		      {
+			*atext = acltotext (entries, count);
+			if (! *atext)
+			  {
+			    free (entries);
+			    return -1;
+			  }
+			replace_chr (*atext, ',', '\n');
+		      }
 		    free (entries);
 		    return 1;
 		  }
diff --git a/lib/free-acl-text.c b/lib/free-acl-text.c
new file mode 100644
index 0000000..230808d
--- /dev/null
+++ b/lib/free-acl-text.c
@@ -0,0 +1,46 @@
+/* Free text allocated by file_has_acl.
+
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by David Bartley.  */
+
+#include <config.h>
+
+#include <stddef.h>
+
+#include "acl.h"
+
+#include "acl-internal.h"
+
+
+/* Free text allocated by file_has_acl.  */
+
+void
+free_acl_text (char *text)
+{
+#if USE_ACL
+  if (text)
+    {
+# if HAVE_ACL_FREE_TEXT /* Tru64 */
+      acl_free_text (text);
+# elif HAVE_ACL_FREE /* Linux, FreeBSD, MacOS X, IRIX */
+      acl_free (text);
+# else /* Solaris */
+      free (text);
+# endif
+    }
+#endif
+}
diff --git a/m4/acl.m4 b/m4/acl.m4
index 5340e2e..ce7ff85 100644
--- a/m4/acl.m4
+++ b/m4/acl.m4
@@ -36,7 +36,7 @@ AC_DEFUN([gl_FUNC_ACL],
 	      acl_delete_def_file acl_extended_file \
 	      acl_delete_fd_np acl_delete_file_np \
 	      acl_copy_ext_native acl_create_entry_np \
-	      acl_to_short_text acl_free_text])
+	      acl_to_short_text acl_to_any_text acl_free_text])
 	   # If the acl_get_file bug is detected, don't enable the ACL support.
 	   gl_ACL_GET_FILE([use_acl=1], [])
 	   if test $use_acl = 1; then
diff --git a/modules/acl b/modules/acl
index 2185242..2e57b0b 100644
--- a/modules/acl
+++ b/modules/acl
@@ -8,6 +8,7 @@ lib/acl_entries.c
 lib/set-mode-acl.c
 lib/copy-acl.c
 lib/file-has-acl.c
+lib/free-acl-text.c
 m4/acl.m4
 
 Depends-on:
@@ -20,7 +21,7 @@ configure.ac:
 gl_FUNC_ACL
 
 Makefile.am:
-lib_SOURCES += set-mode-acl.c copy-acl.c file-has-acl.c
+lib_SOURCES += set-mode-acl.c copy-acl.c file-has-acl.c free-acl-text.c
 
 Include:
 "acl.h"
diff --git a/tests/test-file-has-acl.c b/tests/test-file-has-acl.c
index 5685f41..e1ace65 100644
--- a/tests/test-file-has-acl.c
+++ b/tests/test-file-has-acl.c
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <stddef.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -65,7 +66,7 @@ main (int argc, char *argv[])
 
 #if USE_ACL
   {
-    int ret = file_has_acl (file, &statbuf);
+    int ret = file_has_acl (file, &statbuf, 0, NULL, NULL);
     if (ret < 0)
       {
 	fprintf (stderr, "could not access the ACL of file \"%s\"\n", file);
-- 
1.5.6.5

Reply via email to