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