summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2014-01-10 10:07:41 -0500
committerAndreas Gruenbacher <agruen@linbit.com>2014-01-13 13:18:39 +0100
commitc8f23c92177c5a82ab3699b1b0d4acbee9afb770 (patch)
tree72b6c7cf7a006babd2177055d8c61d1f6bd15375 /tools
parent2d13383b7fbcc04fc16681caa4f43d9d9083cca1 (diff)
downloadacl-c8f23c92177c5a82ab3699b1b0d4acbee9afb770.tar.gz
modernize build system
This deletes the hand rolled build system and replaces it entirely with autotools. The overall diffstat shows that this is a clear win, and it makes the package build/install like every other autotool package out there which makes the lives of distro maintainers a lot easier. This should also be faster by virtue of using a non-recursive build. Things to note: - to generate autotools: ./autogen.sh - to see full compile output: make V=1 - to build specific targets: make attr getfattr ... - to run tests: make check - to create a release: make distcheck -j Other non-developer things: - man pages are no longer compressed as this is uncommon in the autotools world and distros don't need it anyways (they already handle it automatically for most pakages) - the minor # of the shared library is now based on the package version so it'll be much bigger ... this isn't a problem, and is actually a bugfix (older releases didn't change when they should have) (Again, this is all using the standard autotool targets.)
Diffstat (limited to 'tools')
-rw-r--r--tools/Makemodule.am23
-rw-r--r--tools/chacl.c354
-rw-r--r--tools/do_set.c528
-rw-r--r--tools/do_set.h36
-rw-r--r--tools/getfacl.c747
-rw-r--r--tools/parse.c594
-rw-r--r--tools/parse.h85
-rw-r--r--tools/sequence.c162
-rw-r--r--tools/sequence.h115
-rw-r--r--tools/setfacl.c669
-rw-r--r--tools/user_group.c59
-rw-r--r--tools/user_group.h31
12 files changed, 3403 insertions, 0 deletions
diff --git a/tools/Makemodule.am b/tools/Makemodule.am
new file mode 100644
index 0000000..60a9a2d
--- /dev/null
+++ b/tools/Makemodule.am
@@ -0,0 +1,23 @@
+tools_ldadd = $(LDADD) libacl.la libmisc.la
+
+bin_PROGRAMS += chacl
+chacl_SOURCES = tools/chacl.c
+chacl_LDADD = $(tools_ldadd)
+
+bin_PROGRAMS += getfacl
+getfacl_SOURCES = \
+ tools/getfacl.c \
+ tools/user_group.c \
+ tools/user_group.h
+getfacl_LDADD = $(tools_ldadd)
+
+bin_PROGRAMS += setfacl
+setfacl_SOURCES = \
+ tools/do_set.c \
+ tools/do_set.h \
+ tools/parse.c \
+ tools/parse.h \
+ tools/sequence.c \
+ tools/sequence.h \
+ tools/setfacl.c
+setfacl_LDADD = $(tools_ldadd)
diff --git a/tools/chacl.c b/tools/chacl.c
new file mode 100644
index 0000000..525a7ff
--- /dev/null
+++ b/tools/chacl.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2001-2002 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would 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, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#include "misc.h"
+
+static int acl_delete_file (const char * path, acl_type_t type);
+static int list_acl(char *file);
+static int set_acl(acl_t acl, acl_t dacl, const char *fname);
+static int walk_dir(acl_t acl, acl_t dacl, const char *fname);
+
+static char *program;
+static int rflag;
+
+static void
+usage(void)
+{
+ fprintf(stderr, _("Usage:\n"));
+ fprintf(stderr, _("\t%s acl pathname...\n"), program);
+ fprintf(stderr, _("\t%s -b acl dacl pathname...\n"), program);
+ fprintf(stderr, _("\t%s -d dacl pathname...\n"), program);
+ fprintf(stderr, _("\t%s -R pathname...\n"), program);
+ fprintf(stderr, _("\t%s -D pathname...\n"), program);
+ fprintf(stderr, _("\t%s -B pathname...\n"), program);
+ fprintf(stderr, _("\t%s -l pathname...\t[not IRIX compatible]\n"),
+ program);
+ fprintf(stderr, _("\t%s -r pathname...\t[not IRIX compatible]\n"),
+ program);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *file;
+ int switch_flag = 0; /* ensure only one switch is used */
+ int args_required = 2;
+ int failed = 0; /* exit status */
+ int c; /* For use by getopt(3) */
+ int dflag = 0; /* a Default ACL is desired */
+ int bflag = 0; /* a both ACLs are desired */
+ int Rflag = 0; /* set to true to remove an acl */
+ int Dflag = 0; /* set to true to remove default acls */
+ int Bflag = 0; /* set to true to remove both acls */
+ int lflag = 0; /* set to true to list acls */
+ acl_t acl = NULL; /* File ACL */
+ acl_t dacl = NULL; /* Directory Default ACL */
+
+ program = basename(argv[0]);
+
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /* parse arguments */
+ while ((c = getopt(argc, argv, "bdlRDBr")) != -1) {
+ if (switch_flag)
+ usage();
+ switch_flag = 1;
+
+ switch (c) {
+ case 'b':
+ bflag = 1;
+ args_required = 3;
+ break;
+ case 'd':
+ dflag = 1;
+ args_required = 2;
+ break;
+ case 'R':
+ Rflag = 1;
+ args_required = 1;
+ break;
+ case 'D':
+ Dflag = 1;
+ args_required = 1;
+ break;
+ case 'B':
+ Bflag = 1;
+ args_required = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ args_required = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ args_required = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ /* if not enough arguments quit */
+ if ((argc - optind) < args_required)
+ usage();
+
+ /* list the acls */
+ if (lflag) {
+ for (; optind < argc; optind++) {
+ file = argv[optind];
+ if (!list_acl(file))
+ failed++;
+ }
+ return(failed);
+ }
+
+ /* remove the acls */
+ if (Rflag || Dflag || Bflag) {
+ for (; optind < argc; optind++) {
+ file = argv[optind];
+ if (!Dflag &&
+ (acl_delete_file(file, ACL_TYPE_ACCESS) == -1)) {
+ fprintf(stderr, _(
+ "%s: error removing access acl on \"%s\": %s\n"),
+ program, file, strerror(errno));
+ failed++;
+ }
+ if (!Rflag &&
+ (acl_delete_file(file, ACL_TYPE_DEFAULT) == -1)) {
+ fprintf(stderr, _(
+ "%s: error removing default acl on \"%s\": %s\n"),
+ program, file, strerror(errno));
+ failed++;
+ }
+ }
+ return(failed);
+ }
+
+ /* file access acl */
+ if (! dflag) {
+ acl = acl_from_text(argv[optind]);
+ failed = acl_check(acl, &c);
+ if (failed < 0) {
+ fprintf(stderr, "%s: %s - %s\n",
+ program, argv[optind], strerror(errno));
+ return 1;
+ }
+ else if (failed > 0) {
+ fprintf(stderr, _(
+ "%s: access ACL '%s': %s at entry %d\n"),
+ program, argv[optind], acl_error(failed), c);
+ return 1;
+ }
+ optind++;
+ }
+
+
+ /* directory default acl */
+ if (bflag || dflag) {
+ dacl = acl_from_text(argv[optind]);
+ failed = acl_check(dacl, &c);
+ if (failed < 0) {
+ fprintf(stderr, "%s: %s - %s\n",
+ program, argv[optind], strerror(errno));
+ return 1;
+ }
+ else if (failed > 0) {
+ fprintf(stderr, _(
+ "%s: access ACL '%s': %s at entry %d\n"),
+ program, argv[optind], acl_error(failed), c);
+ return 1;
+ }
+ optind++;
+ }
+
+ /* place acls on files */
+ for (; optind < argc; optind++)
+ failed += set_acl(acl, dacl, argv[optind]);
+
+ if (acl)
+ acl_free(acl);
+ if (dacl)
+ acl_free(dacl);
+
+ return(failed);
+}
+
+/*
+ * deletes an access acl or directory default acl if one exists
+ */
+static int
+acl_delete_file(const char *path, acl_type_t type)
+{
+ int error = 0;
+
+ /* converts access ACL to a minimal ACL */
+ if (type == ACL_TYPE_ACCESS) {
+ acl_t acl;
+ acl_entry_t entry;
+ acl_tag_t tag;
+
+ acl = acl_get_file(path, ACL_TYPE_ACCESS);
+ if (!acl)
+ return -1;
+ error = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
+ while (error == 1) {
+ acl_get_tag_type(entry, &tag);
+ switch(tag) {
+ case ACL_USER:
+ case ACL_GROUP:
+ case ACL_MASK:
+ acl_delete_entry(acl, entry);
+ break;
+ }
+ error = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
+ }
+ if (!error)
+ error = acl_set_file(path, ACL_TYPE_ACCESS, acl);
+ } else
+ error = acl_delete_def_file(path);
+ return(error);
+}
+
+/*
+ * lists the acl for a file/dir in short text form
+ * return 0 on failure
+ * return 1 on success
+ */
+static int
+list_acl(char *file)
+{
+ acl_t acl = NULL;
+ acl_t dacl = NULL;
+ char *acl_text, *dacl_text = NULL;
+
+ if ((acl = acl_get_file(file, ACL_TYPE_ACCESS)) == NULL) {
+ fprintf(stderr, _("%s: cannot get access ACL on '%s': %s\n"),
+ program, file, strerror(errno));
+ return 0;
+ }
+ if ((dacl = acl_get_file(file, ACL_TYPE_DEFAULT)) == NULL &&
+ (errno != EACCES)) { /* EACCES given if not a directory */
+ fprintf(stderr, _("%s: cannot get default ACL on '%s': %s\n"),
+ program, file, strerror(errno));
+ return 0;
+ }
+ acl_text = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE);
+ if (acl_text == NULL) {
+ fprintf(stderr, _("%s: cannot get access ACL text on "
+ "'%s': %s\n"), program, file, strerror(errno));
+ return 0;
+ }
+ if (acl_entries(dacl) > 0) {
+ dacl_text = acl_to_any_text(dacl, NULL, ',', TEXT_ABBREVIATE);
+ if (dacl_text == NULL) {
+ fprintf(stderr, _("%s: cannot get default ACL text on "
+ "'%s': %s\n"), program, file, strerror(errno));
+ return 0;
+ }
+ }
+ if (dacl_text) {
+ printf("%s [%s/%s]\n", file, acl_text, dacl_text);
+ acl_free(dacl_text);
+ } else
+ printf("%s [%s]\n", file, acl_text);
+ acl_free(acl_text);
+ acl_free(acl);
+ acl_free(dacl);
+ return 1;
+}
+
+static int
+set_acl(acl_t acl, acl_t dacl, const char *fname)
+{
+ int failed = 0;
+
+ if (rflag)
+ failed += walk_dir(acl, dacl, fname);
+
+ /* set regular acl */
+ if (acl && acl_set_file(fname, ACL_TYPE_ACCESS, acl) == -1) {
+ fprintf(stderr, _("%s: cannot set access acl on \"%s\": %s\n"),
+ program, fname, strerror(errno));
+ failed++;
+ }
+ /* set default acl */
+ if (dacl && acl_set_file(fname, ACL_TYPE_DEFAULT, dacl) == -1) {
+ fprintf(stderr, _("%s: cannot set default acl on \"%s\": %s\n"),
+ program, fname, strerror(errno));
+ failed++;
+ }
+
+ return(failed);
+}
+
+static int
+walk_dir(acl_t acl, acl_t dacl, const char *fname)
+{
+ int failed = 0;
+ DIR *dir;
+ struct dirent64 *d;
+ char *name;
+
+ if ((dir = opendir(fname)) == NULL) {
+ if (errno != ENOTDIR) {
+ fprintf(stderr, _("%s: opendir failed: %s\n"),
+ program, strerror(errno));
+ return(1);
+ }
+ return(0); /* got a file, not an error */
+ }
+
+ while ((d = readdir64(dir)) != NULL) {
+ /* skip "." and ".." entries */
+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
+ continue;
+
+ name = malloc(strlen(fname) + strlen(d->d_name) + 2);
+ if (name == NULL) {
+ fprintf(stderr, _("%s: malloc failed: %s\n"),
+ program, strerror(errno));
+ exit(1);
+ }
+ sprintf(name, "%s/%s", fname, d->d_name);
+
+ failed += set_acl(acl, dacl, name);
+ free(name);
+ }
+ closedir(dir);
+
+ return(failed);
+}
diff --git a/tools/do_set.c b/tools/do_set.c
new file mode 100644
index 0000000..60da837
--- /dev/null
+++ b/tools/do_set.c
@@ -0,0 +1,528 @@
+/*
+ File: do_set.c
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999, 2000
+ Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/acl.h>
+#include <acl/libacl.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <ftw.h>
+#include "misc.h"
+#include "sequence.h"
+#include "do_set.h"
+#include "parse.h"
+#include "walk_tree.h"
+
+
+extern const char *progname;
+extern int opt_recalculate;
+extern int opt_test;
+extern int print_options;
+
+acl_entry_t
+find_entry(
+ acl_t acl,
+ acl_tag_t type,
+ id_t id)
+{
+ acl_entry_t ent;
+ acl_tag_t e_type;
+ id_t *e_id_p;
+
+ if (acl_get_entry(acl, ACL_FIRST_ENTRY, &ent) != 1)
+ return NULL;
+
+ for(;;) {
+ acl_get_tag_type(ent, &e_type);
+ if (type == e_type) {
+ if (id != ACL_UNDEFINED_ID) {
+ e_id_p = acl_get_qualifier(ent);
+ if (e_id_p == NULL)
+ return NULL;
+ if (*e_id_p == id) {
+ acl_free(e_id_p);
+ return ent;
+ }
+ acl_free(e_id_p);
+ } else {
+ return ent;
+ }
+ }
+ if (acl_get_entry(acl, ACL_NEXT_ENTRY, &ent) != 1)
+ return NULL;
+ }
+}
+
+int
+has_execute_perms(
+ acl_t acl)
+{
+ acl_entry_t ent;
+
+ if (acl_get_entry(acl, ACL_FIRST_ENTRY, &ent) != 1)
+ return 0;
+
+ for(;;) {
+ acl_permset_t permset;
+
+ acl_get_permset(ent, &permset);
+ if (acl_get_perm(permset, ACL_EXECUTE) != 0)
+ return 1;
+
+ if (acl_get_entry(acl, ACL_NEXT_ENTRY, &ent) != 1)
+ return 0;
+ }
+}
+
+int
+clone_entry(
+ acl_t from_acl,
+ acl_tag_t from_type,
+ acl_t *to_acl,
+ acl_tag_t to_type)
+{
+ acl_entry_t from_entry, to_entry;
+ from_entry = find_entry(from_acl, from_type, ACL_UNDEFINED_ID);
+ if (from_entry) {
+ if (acl_create_entry(to_acl, &to_entry) != 0)
+ return -1;
+ acl_copy_entry(to_entry, from_entry);
+ acl_set_tag_type(to_entry, to_type);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+void
+print_test(
+ FILE *file,
+ const char *path_p,
+ const struct stat *st,
+ const acl_t acl,
+ const acl_t default_acl)
+{
+ char *acl_text, *default_acl_text;
+
+ acl_text = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE);
+ default_acl_text =
+ acl_to_any_text(default_acl, "d:", ',', TEXT_ABBREVIATE);
+ fprintf(file, "%s: %s,%s\n", path_p,
+ acl_text ? acl_text : "*",
+ default_acl_text ? default_acl_text : "*");
+ acl_free(acl_text);
+ acl_free(default_acl_text);
+}
+
+
+static void
+set_perm(
+ acl_entry_t ent,
+ mode_t perm)
+{
+ acl_permset_t set;
+
+ acl_get_permset(ent, &set);
+ if (perm & CMD_PERM_READ)
+ acl_add_perm(set, ACL_READ);
+ else
+ acl_delete_perm(set, ACL_READ);
+ if (perm & CMD_PERM_WRITE)
+ acl_add_perm(set, ACL_WRITE);
+ else
+ acl_delete_perm(set, ACL_WRITE);
+ if (perm & CMD_PERM_EXECUTE)
+ acl_add_perm(set, ACL_EXECUTE);
+ else
+ acl_delete_perm(set, ACL_EXECUTE);
+}
+
+
+static int
+retrieve_acl(
+ const char *path_p,
+ acl_type_t type,
+ const struct stat *st,
+ acl_t *old_acl,
+ acl_t *acl)
+{
+ if (*acl)
+ return 0;
+ *acl = NULL;
+ if (type == ACL_TYPE_ACCESS || S_ISDIR(st->st_mode)) {
+ *old_acl = acl_get_file(path_p, type);
+ if (*old_acl == NULL && (errno == ENOSYS || errno == ENOTSUP)) {
+ if (type == ACL_TYPE_DEFAULT)
+ *old_acl = acl_init(0);
+ else
+ *old_acl = acl_from_mode(st->st_mode);
+ }
+ } else
+ *old_acl = acl_init(0);
+ if (*old_acl == NULL)
+ return -1;
+ *acl = acl_dup(*old_acl);
+ if (*acl == NULL)
+ return -1;
+ return 0;
+}
+
+
+static int
+remove_extended_entries(
+ acl_t acl)
+{
+ acl_entry_t ent, group_obj;
+ acl_permset_t mask_permset, group_obj_permset;
+ acl_tag_t tag;
+ int error;
+
+ /*
+ * Removing the ACL_MASK entry from the ACL results in
+ * increased permissions for the owning group if the
+ * ACL_GROUP_OBJ entry contains permissions not contained
+ * in the ACL_MASK entry. We remove these permissions from
+ * the ACL_GROUP_OBJ entry to avoid that.
+ *
+ * After removing the ACL, the file owner and the owning group
+ * therefore have the same permissions as before.
+ */
+
+ ent = find_entry(acl, ACL_MASK, ACL_UNDEFINED_ID);
+ group_obj = find_entry(acl, ACL_GROUP_OBJ, ACL_UNDEFINED_ID);
+ if (ent && group_obj) {
+ if (!acl_get_permset(ent, &mask_permset) &&
+ !acl_get_permset(group_obj, &group_obj_permset)) {
+ if (!acl_get_perm(mask_permset, ACL_READ))
+ acl_delete_perm(group_obj_permset, ACL_READ);
+ if (!acl_get_perm(mask_permset, ACL_WRITE))
+ acl_delete_perm(group_obj_permset, ACL_WRITE);
+ if (!acl_get_perm(mask_permset, ACL_EXECUTE))
+ acl_delete_perm(group_obj_permset, ACL_EXECUTE);
+ }
+ }
+
+ error = acl_get_entry(acl, ACL_FIRST_ENTRY, &ent);
+ while (error == 1) {
+ acl_get_tag_type(ent, &tag);
+ switch(tag) {
+ case ACL_USER:
+ case ACL_GROUP:
+ case ACL_MASK:
+ acl_delete_entry(acl, ent);
+ break;
+ default:
+ break;
+ }
+
+ error = acl_get_entry(acl, ACL_NEXT_ENTRY, &ent);
+ }
+ if (error < 0)
+ return -1;
+ return 0;
+}
+
+
+#define RETRIEVE_ACL(type) do { \
+ error = retrieve_acl(path_p, type, st, old_xacl, xacl); \
+ if (error) \
+ goto fail; \
+ } while(0)
+
+int
+do_set(
+ const char *path_p,
+ const struct stat *st,
+ int walk_flags,
+ void *arg)
+{
+ struct do_set_args *args = arg;
+ acl_t old_acl = NULL, old_default_acl = NULL;
+ acl_t acl = NULL, default_acl = NULL;
+ acl_t *xacl, *old_xacl;
+ acl_entry_t ent;
+ cmd_t cmd;
+ int which_entry;
+ int errors = 0, error;
+ char *acl_text;
+ int acl_modified = 0, default_acl_modified = 0;
+ int acl_mask_provided = 0, default_acl_mask_provided = 0;
+
+ if (walk_flags & WALK_TREE_FAILED) {
+ fprintf(stderr, "%s: %s: %s\n", progname, path_p, strerror(errno));
+ return 1;
+ }
+
+ /*
+ * Symlinks can never have ACLs, so when doing a physical walk, we
+ * skip symlinks altogether, and when doing a half-logical walk, we
+ * skip all non-toplevel symlinks.
+ */
+ if ((walk_flags & WALK_TREE_SYMLINK) &&
+ ((walk_flags & WALK_TREE_PHYSICAL) ||
+ !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL))))
+ return 0;
+
+ /* Execute the commands in seq (read ACLs on demand) */
+ error = seq_get_cmd(args->seq, SEQ_FIRST_CMD, &cmd);
+ if (error == 0)
+ return 0;
+ while (error == 1) {
+ mode_t perm = cmd->c_perm;
+
+ if (cmd->c_type == ACL_TYPE_ACCESS) {
+ xacl = &acl;
+ old_xacl = &old_acl;
+ acl_modified = 1;
+ if (cmd->c_tag == ACL_MASK)
+ acl_mask_provided = 1;
+ } else {
+ xacl = &default_acl;
+ old_xacl = &old_default_acl;
+ default_acl_modified = 1;
+ if (cmd->c_tag == ACL_MASK)
+ default_acl_mask_provided = 1;
+ }
+
+ RETRIEVE_ACL(cmd->c_type);
+
+ /* Check for `X', and replace with `x' as appropriate. */
+ if (perm & CMD_PERM_COND_EXECUTE) {
+ perm &= ~CMD_PERM_COND_EXECUTE;
+ if (S_ISDIR(st->st_mode) || has_execute_perms(*xacl))
+ perm |= CMD_PERM_EXECUTE;
+ }
+
+ switch(cmd->c_cmd) {
+ case CMD_ENTRY_REPLACE:
+ ent = find_entry(*xacl, cmd->c_tag, cmd->c_id);
+ if (!ent) {
+ if (acl_create_entry(xacl, &ent) != 0)
+ goto fail;
+ acl_set_tag_type(ent, cmd->c_tag);
+ if (cmd->c_id != ACL_UNDEFINED_ID)
+ acl_set_qualifier(ent,
+ &cmd->c_id);
+ }
+ set_perm(ent, perm);
+ break;
+
+ case CMD_REMOVE_ENTRY:
+ ent = find_entry(*xacl, cmd->c_tag, cmd->c_id);
+ if (ent)
+ acl_delete_entry(*xacl, ent);
+ else
+ /* ignore */;
+ break;
+
+ case CMD_REMOVE_EXTENDED_ACL:
+ remove_extended_entries(acl);
+ break;
+
+ case CMD_REMOVE_ACL:
+ acl_free(*xacl);
+ *xacl = acl_init(5);
+ if (!*xacl)
+ goto fail;
+ break;
+
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+
+ error = seq_get_cmd(args->seq, SEQ_NEXT_CMD, &cmd);
+ }
+
+ if (error < 0)
+ goto fail;
+
+ /* Try to fill in missing entries */
+ if (default_acl && acl_entries(default_acl) != 0) {
+ xacl = &acl;
+ old_xacl = &old_acl;
+
+ if (!find_entry(default_acl, ACL_USER_OBJ, ACL_UNDEFINED_ID)) {
+ if (!acl)
+ RETRIEVE_ACL(ACL_TYPE_ACCESS);
+ clone_entry(acl, ACL_USER_OBJ,
+ &default_acl, ACL_USER_OBJ);
+ }
+ if (!find_entry(default_acl, ACL_GROUP_OBJ, ACL_UNDEFINED_ID)) {
+ if (!acl)
+ RETRIEVE_ACL(ACL_TYPE_ACCESS);
+ clone_entry(acl, ACL_GROUP_OBJ,
+ &default_acl, ACL_GROUP_OBJ);
+ }
+ if (!find_entry(default_acl, ACL_OTHER, ACL_UNDEFINED_ID)) {
+ if (!acl)
+ RETRIEVE_ACL(ACL_TYPE_ACCESS);
+ clone_entry(acl, ACL_OTHER,
+ &default_acl, ACL_OTHER);
+ }
+ }
+
+ /* update mask entries and check if ACLs are valid */
+ if (acl && acl_modified) {
+ if (acl_equiv_mode(acl, NULL) != 0) {
+ if (!acl_mask_provided &&
+ !find_entry(acl, ACL_MASK, ACL_UNDEFINED_ID))
+ clone_entry(acl, ACL_GROUP_OBJ,
+ &acl, ACL_MASK);
+ if (opt_recalculate != -1 &&
+ (!acl_mask_provided || opt_recalculate == 1))
+ acl_calc_mask(&acl);
+ }
+
+ error = acl_check(acl, &which_entry);
+ if (error < 0)
+ goto fail;
+ if (error > 0) {
+ acl_text = acl_to_any_text(acl, NULL, ',', 0);
+ fprintf(stderr, _("%s: %s: Malformed access ACL "
+ "`%s': %s at entry %d\n"), progname, path_p,
+ acl_text, acl_error(error), which_entry+1);
+ acl_free(acl_text);
+ errors++;
+ goto cleanup;
+ }
+ }
+
+ if (default_acl && acl_entries(default_acl) != 0 &&
+ default_acl_modified) {
+ if (acl_equiv_mode(default_acl, NULL) != 0) {
+ if (!default_acl_mask_provided &&
+ !find_entry(default_acl,ACL_MASK,ACL_UNDEFINED_ID))
+ clone_entry(default_acl, ACL_GROUP_OBJ,
+ &default_acl, ACL_MASK);
+ if (opt_recalculate != -1 &&
+ (!default_acl_mask_provided ||
+ opt_recalculate == 1))
+ acl_calc_mask(&default_acl);
+ }
+
+ error = acl_check(default_acl, &which_entry);
+ if (error < 0)
+ goto fail;
+ if (error > 0) {
+ acl_text = acl_to_any_text(default_acl, NULL, ',', 0);
+ fprintf(stderr, _("%s: %s: Malformed default ACL "
+ "`%s': %s at entry %d\n"),
+ progname, path_p, acl_text,
+ acl_error(error), which_entry+1);
+ acl_free(acl_text);
+ errors++;
+ goto cleanup;
+ }
+ }
+
+ /* Only directores can have default ACLs */
+ if (default_acl && !S_ISDIR(st->st_mode) && (walk_flags & WALK_TREE_RECURSIVE)) {
+ /* In recursive mode, ignore default ACLs for files */
+ acl_free(default_acl);
+ default_acl = NULL;
+ }
+
+ /* check which ACLs have changed */
+ if (acl && old_acl && acl_cmp(old_acl, acl) == 0) {
+ acl_free(acl);
+ acl = NULL;
+ }
+ if ((default_acl && old_default_acl &&
+ acl_cmp(old_default_acl, default_acl) == 0)) {
+ acl_free(default_acl);
+ default_acl = NULL;
+ }
+
+ /* update the file system */
+ if (opt_test) {
+ print_test(stdout, path_p, st,
+ acl, default_acl);
+ goto cleanup;
+ }
+ if (acl) {
+ int equiv_mode;
+ mode_t mode = 0;
+
+ equiv_mode = acl_equiv_mode(acl, &mode);
+
+ if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) != 0) {
+ if (errno == ENOSYS || errno == ENOTSUP) {
+ if (equiv_mode != 0)
+ goto fail;
+ else if (chmod(path_p, mode) != 0)
+ goto fail;
+ } else
+ goto fail;
+ }
+ args->mode = mode;
+ }
+ if (default_acl) {
+ if (S_ISDIR(st->st_mode)) {
+ if (acl_entries(default_acl) == 0) {
+ if (acl_delete_def_file(path_p) != 0 &&
+ errno != ENOSYS && errno != ENOTSUP)
+ goto fail;
+ } else {
+ if (acl_set_file(path_p, ACL_TYPE_DEFAULT,
+ default_acl) != 0)
+ goto fail;
+ }
+ } else {
+ if (acl_entries(default_acl) != 0) {
+ fprintf(stderr, _("%s: %s: Only directories "
+ "can have default ACLs\n"),
+ progname, path_p);
+ errors++;
+ goto cleanup;
+ }
+ }
+ }
+
+ error = 0;
+
+cleanup:
+ if (acl)
+ acl_free(acl);
+ if (old_acl)
+ acl_free(old_acl);
+ if (default_acl)
+ acl_free(default_acl);
+ if (old_default_acl)
+ acl_free(old_default_acl);
+ return errors;
+
+fail:
+ fprintf(stderr, "%s: %s: %s\n", progname, path_p, strerror(errno));
+ errors++;
+ goto cleanup;
+}
+
diff --git a/tools/do_set.h b/tools/do_set.h
new file mode 100644
index 0000000..2ea25a8
--- /dev/null
+++ b/tools/do_set.h
@@ -0,0 +1,36 @@
+/*
+ File: do_set.h
+ (Linux Access Control List Management)
+
+ Copyright (C) 2009 by Andreas Gruenbacher
+ <a.gruenbacher@computer.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __DO_SET_H
+#define __DO_SET_H
+
+#include "sequence.h"
+
+struct do_set_args {
+ seq_t seq;
+ mode_t mode;
+};
+
+extern int do_set(const char *path_p, const struct stat *stat_p, int flags,
+ void *arg);
+
+#endif /* __DO_SET_H */
diff --git a/tools/getfacl.c b/tools/getfacl.c
new file mode 100644
index 0000000..22cc1c7
--- /dev/null
+++ b/tools/getfacl.c
@@ -0,0 +1,747 @@
+/*
+ File: getfacl.c
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999-2002
+ Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
+
+ 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 2 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 library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/acl.h>
+#include <acl/libacl.h>
+
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <getopt.h>
+#include "misc.h"
+#include "user_group.h"
+#include "walk_tree.h"
+
+#define POSIXLY_CORRECT_STR "POSIXLY_CORRECT"
+
+#if !POSIXLY_CORRECT
+# define CMD_LINE_OPTIONS "aceEsRLPtpndvh"
+#endif
+#define POSIXLY_CMD_LINE_OPTIONS "d"
+
+struct option long_options[] = {
+#if !POSIXLY_CORRECT
+ { "access", 0, 0, 'a' },
+ { "omit-header", 0, 0, 'c' },
+ { "all-effective", 0, 0, 'e' },
+ { "no-effective", 0, 0, 'E' },
+ { "skip-base", 0, 0, 's' },
+ { "recursive", 0, 0, 'R' },
+ { "logical", 0, 0, 'L' },
+ { "physical", 0, 0, 'P' },
+ { "tabular", 0, 0, 't' },
+ { "absolute-names", 0, 0, 'p' },
+ { "numeric", 0, 0, 'n' },
+#endif
+ { "default", 0, 0, 'd' },
+ { "version", 0, 0, 'v' },
+ { "help", 0, 0, 'h' },
+ { NULL, 0, 0, 0 }
+};
+
+const char *progname;
+const char *cmd_line_options;
+
+int walk_flags = WALK_TREE_DEREFERENCE_TOPLEVEL;
+int opt_print_acl;
+int opt_print_default_acl;
+int opt_strip_leading_slash = 1;
+int opt_comments = 1; /* include comments */
+int opt_skip_base; /* skip files that only have the base entries */
+int opt_tabular; /* tabular output format (alias `showacl') */
+#if POSIXLY_CORRECT
+const int posixly_correct = 1; /* Posix compatible behavior! */
+#else
+int posixly_correct; /* Posix compatible behavior? */
+#endif
+int had_errors;
+int absolute_warning; /* Absolute path warning was issued */
+int print_options = TEXT_SOME_EFFECTIVE;
+int opt_numeric; /* don't convert id's to symbolic names */
+
+
+static const char *xquote(const char *str, const char *quote_chars)
+{
+ const char *q = quote(str, quote_chars);
+ if (q == NULL) {
+ fprintf(stderr, "%s: %s\n", progname, strerror(errno));
+ exit(1);
+ }
+ return q;
+}
+
+struct name_list {
+ struct name_list *next;
+ char name[0];
+};
+
+void free_list(struct name_list *names)
+{
+ struct name_list *next;
+
+ while (names) {
+ next = names->next;
+ free(names);
+ names = next;
+ }
+}
+
+struct name_list *get_list(const struct stat *st, acl_t acl)
+{
+ struct name_list *first = NULL, *last = NULL;
+ acl_entry_t ent;
+ int ret = 0;
+
+ if (acl != NULL)
+ ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &ent);
+ if (ret != 1)
+ return NULL;
+ while (ret > 0) {
+ acl_tag_t e_type;
+ const id_t *id_p;
+ const char *name = "";
+ int len;
+
+ acl_get_tag_type(ent, &e_type);
+ switch(e_type) {
+ case ACL_USER_OBJ:
+ name = user_name(st->st_uid, opt_numeric);
+ break;
+
+ case ACL_USER:
+ id_p = acl_get_qualifier(ent);
+ if (id_p != NULL)
+ name = user_name(*id_p, opt_numeric);
+ break;
+
+ case ACL_GROUP_OBJ:
+ name = group_name(st->st_gid, opt_numeric);
+ break;
+
+ case ACL_GROUP:
+ id_p = acl_get_qualifier(ent);
+ if (id_p != NULL)
+ name = group_name(*id_p, opt_numeric);
+ break;
+ }
+ name = xquote(name, "\t\n\r");
+ len = strlen(name);
+ if (last == NULL) {
+ first = last = (struct name_list *)
+ malloc(sizeof(struct name_list) + len + 1);
+ } else {
+ last->next = (struct name_list *)
+ malloc(sizeof(struct name_list) + len + 1);
+ last = last->next;
+ }
+ if (last == NULL) {
+ free_list(first);
+ return NULL;
+ }
+ last->next = NULL;
+ strcpy(last->name, name);
+
+ ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &ent);
+ }
+ return first;
+}
+
+int max_name_length(struct name_list *names)
+{
+ int max_len = 0;
+ while (names != NULL) {
+ struct name_list *next = names->next;
+ int len = strlen(names->name);
+
+ if (len > max_len)
+ max_len = len;
+ names = next;
+ }
+ return max_len;
+}
+
+int names_width;
+
+struct acl_perm_def {
+ acl_tag_t tag;
+ char c;
+};
+
+struct acl_perm_def acl_perm_defs[] = {
+ { ACL_READ, 'r' },
+ { ACL_WRITE, 'w' },
+ { ACL_EXECUTE, 'x' },
+ { 0, 0 }
+};
+
+#define ACL_PERMS (sizeof(acl_perm_defs) / sizeof(struct acl_perm_def) - 1)
+
+void acl_perm_str(acl_entry_t entry, char *str)
+{
+ acl_permset_t permset;
+ int n;
+
+ acl_get_permset(entry, &permset);
+ for (n = 0; n < (int) ACL_PERMS; n++) {
+ str[n] = (acl_get_perm(permset, acl_perm_defs[n].tag) ?
+ acl_perm_defs[n].c : '-');
+ }
+ str[n] = '\0';
+}
+
+void acl_mask_perm_str(acl_t acl, char *str)
+{
+ acl_entry_t entry;
+
+ str[0] = '\0';
+ if (acl_get_entry(acl, ACL_FIRST_ENTRY, &entry) != 1)
+ return;
+ for(;;) {
+ acl_tag_t tag;
+
+ acl_get_tag_type(entry, &tag);
+ if (tag == ACL_MASK) {
+ acl_perm_str(entry, str);
+ return;
+ }
+ if (acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) != 1)
+ return;
+ }
+}
+
+void apply_mask(char *perm, const char *mask)
+{
+ while (*perm) {
+ if (*mask == '-' && *perm >= 'a' && *perm <= 'z')
+ *perm = *perm - 'a' + 'A';
+ perm++;
+ if (*mask)
+ mask++;
+ }
+}
+
+int show_line(FILE *stream, struct name_list **acl_names, acl_t acl,
+ acl_entry_t *acl_ent, const char *acl_mask,
+ struct name_list **dacl_names, acl_t dacl,
+ acl_entry_t *dacl_ent, const char *dacl_mask)
+{
+ acl_tag_t tag_type;
+ const char *tag, *name;
+ char acl_perm[ACL_PERMS+1], dacl_perm[ACL_PERMS+1];
+
+ if (acl) {
+ acl_get_tag_type(*acl_ent, &tag_type);
+ name = (*acl_names)->name;
+ } else {
+ acl_get_tag_type(*dacl_ent, &tag_type);
+ name = (*dacl_names)->name;
+ }
+
+ switch(tag_type) {
+ case ACL_USER_OBJ:
+ tag = "USER";
+ break;
+ case ACL_USER:
+ tag = "user";
+ break;
+ case ACL_GROUP_OBJ:
+ tag = "GROUP";
+ break;
+ case ACL_GROUP:
+ tag = "group";
+ break;
+ case ACL_MASK:
+ tag = "mask";
+ break;
+ case ACL_OTHER:
+ tag = "other";
+ break;
+ default:
+ return -1;
+ }
+
+ memset(acl_perm, ' ', ACL_PERMS);
+ acl_perm[ACL_PERMS] = '\0';
+ if (acl_ent) {
+ acl_perm_str(*acl_ent, acl_perm);
+ if (tag_type != ACL_USER_OBJ && tag_type != ACL_OTHER &&
+ tag_type != ACL_MASK)
+ apply_mask(acl_perm, acl_mask);
+ }
+ memset(dacl_perm, ' ', ACL_PERMS);
+ dacl_perm[ACL_PERMS] = '\0';
+ if (dacl_ent) {
+ acl_perm_str(*dacl_ent, dacl_perm);
+ if (tag_type != ACL_USER_OBJ && tag_type != ACL_OTHER &&
+ tag_type != ACL_MASK)
+ apply_mask(dacl_perm, dacl_mask);
+ }
+
+ fprintf(stream, "%-5s %*s %*s %*s\n",
+ tag, -names_width, name,
+ -(int)ACL_PERMS, acl_perm,
+ -(int)ACL_PERMS, dacl_perm);
+
+ if (acl_names) {
+ acl_get_entry(acl, ACL_NEXT_ENTRY, acl_ent);
+ (*acl_names) = (*acl_names)->next;
+ }
+ if (dacl_names) {
+ acl_get_entry(dacl, ACL_NEXT_ENTRY, dacl_ent);
+ (*dacl_names) = (*dacl_names)->next;
+ }
+ return 0;
+}
+
+int do_show(FILE *stream, const char *path_p, const struct stat *st,
+ acl_t acl, acl_t dacl)
+{
+ struct name_list *acl_names = get_list(st, acl),
+ *first_acl_name = acl_names;
+ struct name_list *dacl_names = get_list(st, dacl),
+ *first_dacl_name = dacl_names;
+
+ int acl_names_width = max_name_length(acl_names);
+ int dacl_names_width = max_name_length(dacl_names);
+ acl_entry_t acl_ent;
+ acl_entry_t dacl_ent;
+ char acl_mask[ACL_PERMS+1], dacl_mask[ACL_PERMS+1];
+ int ret;
+
+ names_width = 8;
+ if (acl_names_width > names_width)
+ names_width = acl_names_width;
+ if (dacl_names_width > names_width)
+ names_width = dacl_names_width;
+
+ acl_mask[0] = '\0';
+ if (acl) {
+ acl_mask_perm_str(acl, acl_mask);
+ ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_ent);
+ if (ret == 0)
+ acl = NULL;
+ if (ret < 0)
+ return ret;
+ }
+ dacl_mask[0] = '\0';
+ if (dacl) {
+ acl_mask_perm_str(dacl, dacl_mask);
+ ret = acl_get_entry(dacl, ACL_FIRST_ENTRY, &dacl_ent);
+ if (ret == 0)
+ dacl = NULL;
+ if (ret < 0)
+ return ret;
+ }
+ fprintf(stream, "# file: %s\n", xquote(path_p, "\n\r"));
+ while (acl_names != NULL || dacl_names != NULL) {
+ acl_tag_t acl_tag, dacl_tag;
+
+ if (acl)
+ acl_get_tag_type(acl_ent, &acl_tag);
+ if (dacl)
+ acl_get_tag_type(dacl_ent, &dacl_tag);
+
+ if (acl && (!dacl || acl_tag < dacl_tag)) {
+ show_line(stream, &acl_names, acl, &acl_ent, acl_mask,
+ NULL, NULL, NULL, NULL);
+ continue;
+ } else if (dacl && (!acl || dacl_tag < acl_tag)) {
+ show_line(stream, NULL, NULL, NULL, NULL,
+ &dacl_names, dacl, &dacl_ent, dacl_mask);
+ continue;
+ } else {
+ if (acl_tag == ACL_USER || acl_tag == ACL_GROUP) {
+ id_t *acl_id_p = NULL, *dacl_id_p = NULL;
+ if (acl_ent)
+ acl_id_p = acl_get_qualifier(acl_ent);
+ if (dacl_ent)
+ dacl_id_p = acl_get_qualifier(dacl_ent);
+
+ if (acl && (!dacl || *acl_id_p < *dacl_id_p)) {
+ show_line(stream, &acl_names, acl,
+ &acl_ent, acl_mask,
+ NULL, NULL, NULL, NULL);
+ continue;
+ } else if (dacl &&
+ (!acl || *dacl_id_p < *acl_id_p)) {
+ show_line(stream, NULL, NULL, NULL,
+ NULL, &dacl_names, dacl,
+ &dacl_ent, dacl_mask);
+ continue;
+ }
+ }
+ show_line(stream, &acl_names, acl, &acl_ent, acl_mask,
+ &dacl_names, dacl, &dacl_ent, dacl_mask);
+ }
+ }
+
+ free_list(first_acl_name);
+ free_list(first_dacl_name);
+
+ return 0;
+}
+
+/*
+ * Create an ACL from the file permission bits
+ * of the file PATH_P.
+ */
+static acl_t
+acl_get_file_mode(const char *path_p)
+{
+ struct stat st;
+
+ if (stat(path_p, &st) != 0)
+ return NULL;
+ return acl_from_mode(st.st_mode);
+}
+
+static const char *
+flagstr(mode_t mode)
+{
+ static char str[4];
+
+ str[0] = (mode & S_ISUID) ? 's' : '-';
+ str[1] = (mode & S_ISGID) ? 's' : '-';
+ str[2] = (mode & S_ISVTX) ? 't' : '-';
+ str[3] = '\0';
+ return str;
+}
+
+int do_print(const char *path_p, const struct stat *st, int walk_flags, void *unused)
+{
+ const char *default_prefix = NULL;
+ acl_t acl = NULL, default_acl = NULL;
+ int error = 0;
+
+ if (walk_flags & WALK_TREE_FAILED) {
+ fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"),
+ strerror(errno));
+ return 1;
+ }
+
+ /*
+ * Symlinks can never have ACLs, so when doing a physical walk, we
+ * skip symlinks altogether, and when doing a half-logical walk, we
+ * skip all non-toplevel symlinks.
+ */
+ if ((walk_flags & WALK_TREE_SYMLINK) &&
+ ((walk_flags & WALK_TREE_PHYSICAL) ||
+ !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL))))
+ return 0;
+
+ if (opt_print_acl) {
+ acl = acl_get_file(path_p, ACL_TYPE_ACCESS);
+ if (acl == NULL && (errno == ENOSYS || errno == ENOTSUP))
+ acl = acl_get_file_mode(path_p);
+ if (acl == NULL)
+ goto fail;
+ }
+
+ if (opt_print_default_acl && S_ISDIR(st->st_mode)) {
+ default_acl = acl_get_file(path_p, ACL_TYPE_DEFAULT);
+ if (default_acl == NULL) {
+ if (errno != ENOSYS && errno != ENOTSUP)
+ goto fail;
+ } else if (acl_entries(default_acl) == 0) {
+ acl_free(default_acl);
+ default_acl = NULL;
+ }
+ }
+
+ if (opt_skip_base &&
+ (!acl || acl_equiv_mode(acl, NULL) == 0) && !default_acl)
+ return 0;
+
+ if (opt_print_acl && opt_print_default_acl)
+ default_prefix = "default:";
+
+ if (opt_strip_leading_slash) {
+ if (*path_p == '/') {
+ if (!absolute_warning) {
+ fprintf(stderr, _("%s: Removing leading "
+ "'/' from absolute path names\n"),
+ progname);
+ absolute_warning = 1;
+ }
+ while (*path_p == '/')
+ path_p++;
+ } else if (*path_p == '.' && *(path_p+1) == '/')
+ while (*++path_p == '/')
+ /* nothing */ ;
+ if (*path_p == '\0')
+ path_p = ".";
+ }
+
+ if (opt_tabular) {
+ if (do_show(stdout, path_p, st, acl, default_acl) != 0)
+ goto fail;
+ } else {
+ if (opt_comments) {
+ printf("# file: %s\n", xquote(path_p, "\n\r"));
+ printf("# owner: %s\n",
+ xquote(user_name(st->st_uid, opt_numeric), " \t\n\r"));
+ printf("# group: %s\n",
+ xquote(group_name(st->st_gid, opt_numeric), " \t\n\r"));
+ if ((st->st_mode & (S_ISVTX | S_ISUID | S_ISGID)) && !posixly_correct)
+ printf("# flags: %s\n", flagstr(st->st_mode));
+ }
+ if (acl != NULL) {
+ char *acl_text = acl_to_any_text(acl, NULL, '\n',
+ print_options);
+ if (!acl_text)
+ goto fail;
+ if (puts(acl_text) < 0) {
+ acl_free(acl_text);
+ goto fail;
+ }
+ acl_free(acl_text);
+ }
+ if (default_acl != NULL) {
+ char *acl_text = acl_to_any_text(default_acl,
+ default_prefix, '\n',
+ print_options);
+ if (!acl_text)
+ goto fail;
+ if (puts(acl_text) < 0) {
+ acl_free(acl_text);
+ goto fail;
+ }
+ acl_free(acl_text);
+ }
+ }
+ if (acl || default_acl || opt_comments)
+ printf("\n");
+
+cleanup:
+ if (acl)
+ acl_free(acl);
+ if (default_acl)
+ acl_free(default_acl);
+ return error;
+
+fail:
+ fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"),
+ strerror(errno));
+ error = -1;
+ goto cleanup;
+}
+
+
+void help(void)
+{
+ printf(_("%s %s -- get file access control lists\n"),
+ progname, VERSION);
+ printf(_("Usage: %s [-%s] file ...\n"),
+ progname, cmd_line_options);
+#if !POSIXLY_CORRECT
+ if (posixly_correct) {
+#endif
+ printf(_(
+" -d, --default display the default access control list\n"));
+#if !POSIXLY_CORRECT
+ } else {
+ printf(_(
+" -a, --access display the file access control list only\n"
+" -d, --default display the default access control list only\n"
+" -c, --omit-header do not display the comment header\n"
+" -e, --all-effective print all effective rights\n"
+" -E, --no-effective print no effective rights\n"
+" -s, --skip-base skip files that only have the base entries\n"
+" -R, --recursive recurse into subdirectories\n"
+" -L, --logical logical walk, follow symbolic links\n"
+" -P, --physical physical walk, do not follow symbolic links\n"
+" -t, --tabular use tabular output format\n"
+" -n, --numeric print numeric user/group identifiers\n"
+" -p, --absolute-names don't strip leading '/' in pathnames\n"));
+ }
+#endif
+ printf(_(
+" -v, --version print version and exit\n"
+" -h, --help this help text\n"));
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ char *line;
+
+ progname = basename(argv[0]);
+
+#if POSIXLY_CORRECT
+ cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
+#else
+ if (getenv(POSIXLY_CORRECT_STR))
+ posixly_correct = 1;
+ if (!posixly_correct)
+ cmd_line_options = CMD_LINE_OPTIONS;
+ else
+ cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
+#endif
+
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /* Align `#effective:' comments to column 40 for tty's */
+ if (!posixly_correct && isatty(fileno(stdout)))
+ print_options |= TEXT_SMART_INDENT;
+
+ while ((opt = getopt_long(argc, argv, cmd_line_options,
+ long_options, NULL)) != -1) {
+ switch (opt) {
+ case 'a': /* acl only */
+ if (posixly_correct)
+ goto synopsis;
+ opt_print_acl = 1;
+ break;
+
+ case 'd': /* default acl only */
+ opt_print_default_acl = 1;
+ break;
+
+ case 'c': /* no comments */
+ if (posixly_correct)
+ goto synopsis;
+ opt_comments = 0;
+ break;
+
+ case 'e': /* all #effective comments */
+ if (posixly_correct)
+ goto synopsis;
+ print_options |= TEXT_ALL_EFFECTIVE;
+ break;
+
+ case 'E': /* no #effective comments */
+ if (posixly_correct)
+ goto synopsis;
+ print_options &= ~(TEXT_SOME_EFFECTIVE |
+ TEXT_ALL_EFFECTIVE);
+ break;
+
+ case 'R': /* recursive */
+ if (posixly_correct)
+ goto synopsis;
+ walk_flags |= WALK_TREE_RECURSIVE;
+ break;
+
+ case 'L': /* follow all symlinks */
+ if (posixly_correct)
+ goto synopsis;
+ walk_flags |= WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE;
+ walk_flags &= ~WALK_TREE_PHYSICAL;
+ break;
+
+ case 'P': /* skip all symlinks */
+ if (posixly_correct)
+ goto synopsis;
+ walk_flags |= WALK_TREE_PHYSICAL;
+ walk_flags &= ~(WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE |
+ WALK_TREE_DEREFERENCE_TOPLEVEL);
+ break;
+
+ case 's': /* skip files with only base entries */
+ if (posixly_correct)
+ goto synopsis;
+ opt_skip_base = 1;
+ break;
+
+ case 'p':
+ if (posixly_correct)
+ goto synopsis;
+ opt_strip_leading_slash = 0;
+ break;
+
+ case 't':
+ if (posixly_correct)
+ goto synopsis;
+ opt_tabular = 1;
+ break;
+
+ case 'n': /* numeric */
+ opt_numeric = 1;
+ print_options |= TEXT_NUMERIC_IDS;
+ break;
+
+ case 'v': /* print version */
+ printf("%s " VERSION "\n", progname);
+ return 0;
+
+ case 'h': /* help */
+ help();
+ return 0;
+
+ case ':': /* option missing */
+ case '?': /* unknown option */
+ default:
+ goto synopsis;
+ }
+ }
+
+ if (!(opt_print_acl || opt_print_default_acl)) {
+ opt_print_acl = 1;
+ if (!posixly_correct)
+ opt_print_default_acl = 1;
+ }
+
+ if ((optind == argc) && !posixly_correct)
+ goto synopsis;
+
+ do {
+ if (optind == argc ||
+ strcmp(argv[optind], "-") == 0) {
+ while ((line = next_line(stdin)) != NULL) {
+ if (*line == '\0')
+ continue;
+
+ had_errors += walk_tree(line, walk_flags, 0,
+ do_print, NULL);
+ }
+ if (!feof(stdin)) {
+ fprintf(stderr, _("%s: Standard input: %s\n"),
+ progname, strerror(errno));
+ had_errors++;
+ }
+ } else
+ had_errors += walk_tree(argv[optind], walk_flags, 0,
+ do_print, NULL);
+ optind++;
+ } while (optind < argc);
+
+ return had_errors ? 1 : 0;
+
+synopsis:
+ fprintf(stderr, _("Usage: %s [-%s] file ...\n"),
+ progname, cmd_line_options);
+ fprintf(stderr, _("Try `%s --help' for more information.\n"),
+ progname);
+ return 2;
+}
+
diff --git a/tools/parse.c b/tools/parse.c
new file mode 100644
index 0000000..e7e6add
--- /dev/null
+++ b/tools/parse.c
@@ -0,0 +1,594 @@
+/*
+ File: parse.c
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999, 2000
+ Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include "sys/acl.h"
+
+#include "sequence.h"
+#include "parse.h"
+#include "misc.h"
+
+#define SKIP_WS(x) ({ \
+ while (*(x)==' ' || *(x)=='\t' || *(x)=='\n' || *(x)=='\r') \
+ (x)++; \
+ })
+
+
+static int
+skip_tag_name(
+ const char **text_p,
+ const char *token)
+{
+ size_t len = strlen(token);
+ const char *text = *text_p;
+
+ SKIP_WS(text);
+ if (strncmp(text, token, len) == 0) {
+ text += len;
+ goto delimiter;
+ }
+ if (*text == *token) {
+ text++;
+ goto delimiter;
+ }
+ return 0;
+
+delimiter:
+ SKIP_WS(text);
+ if (*text == ':') {
+ *text_p = text+1;
+ return 1;
+ }
+ if (*text == ',' || *text == '\0') {
+ *text_p = text;
+ return 1;
+ }
+ return 0;
+}
+
+
+static char *
+get_token(
+ const char **text_p)
+{
+ char *token = NULL, *t;
+ const char *bp, *ep;
+
+ bp = *text_p;
+ SKIP_WS(bp);
+ ep = bp;
+
+ while (*ep!='\0' && *ep!='\r' && *ep!='\n' && *ep!=':' && *ep!=',')
+ ep++;
+ if (ep == bp)
+ goto after_token;
+ token = (char*)malloc(ep - bp + 1);
+ if (token == NULL)
+ goto after_token;
+ memcpy(token, bp, ep - bp);
+
+ /* Trim trailing whitespace */
+ t = token + (ep - bp - 1);
+ while (t >= token &&
+ (*t==' ' || *t=='\t' || *t=='\n' || *t=='\r'))
+ t--;
+ *(t+1) = '\0';
+
+after_token:
+ if (*ep == ':')
+ ep++;
+ *text_p = ep;
+ return token;
+}
+
+
+static int
+get_id(
+ const char *token,
+ id_t *id_p)
+{
+ char *ep;
+ long l;
+ l = strtol(token, &ep, 0);
+ if (*ep != '\0')
+ return -1;
+ if (l < 0) {
+ /*
+ Negative values are interpreted as 16-bit numbers,
+ so that id -2 maps to 65534 (nobody/nogroup), etc.
+ */
+ l &= 0xFFFF;
+ }
+ *id_p = l;
+ return 0;
+}
+
+
+static int
+get_uid(
+ const char *token,
+ uid_t *uid_p)
+{
+ struct passwd *passwd;
+
+ if (get_id(token, (id_t *)uid_p) == 0)
+ goto accept;
+ passwd = getpwnam(token);
+ if (passwd) {
+ *uid_p = passwd->pw_uid;
+ goto accept;
+ }
+ return -1;
+
+accept:
+ return 0;
+}
+
+
+static int
+get_gid(
+ const char *token,
+ gid_t *gid_p)
+{
+ struct group *group;
+
+ if (get_id(token, (id_t *)gid_p) == 0)
+ goto accept;
+ group = getgrnam(token);
+ if (group) {
+ *gid_p = group->gr_gid;
+ goto accept;
+ }
+ return -1;
+
+accept:
+ return 0;
+}
+
+
+/*
+ Parses the next acl entry in text_p.
+
+ Returns:
+ -1 on error, 0 on success.
+*/
+
+cmd_t
+parse_acl_cmd(
+ const char **text_p,
+ int seq_cmd,
+ int parse_mode)
+{
+ cmd_t cmd = cmd_init();
+ char *str;
+ const char *backup;
+ int error, perm_chars;
+ if (!cmd)
+ return NULL;
+
+ cmd->c_cmd = seq_cmd;
+ if (parse_mode & SEQ_PROMOTE_ACL)
+ cmd->c_type = ACL_TYPE_DEFAULT;
+ else
+ cmd->c_type = ACL_TYPE_ACCESS;
+ cmd->c_id = ACL_UNDEFINED_ID;
+ cmd->c_perm = 0;
+
+ if (parse_mode & SEQ_PARSE_DEFAULT) {
+ /* check for default acl entry */
+ backup = *text_p;
+ if (skip_tag_name(text_p, "default")) {
+ if (parse_mode & SEQ_PROMOTE_ACL) {
+ /* if promoting from acl to default acl and
+ a default acl entry is found, fail. */
+ *text_p = backup;
+ goto fail;
+ }
+ cmd->c_type = ACL_TYPE_DEFAULT;
+ }
+ }
+
+ /* parse acl entry type */
+ switch (**text_p) {
+ case 'u': /* user */
+ skip_tag_name(text_p, "user");
+
+user_entry:
+ backup = *text_p;
+ str = get_token(text_p);
+ if (str) {
+ cmd->c_tag = ACL_USER;
+ error = get_uid(unquote(str), &cmd->c_id);
+ free(str);
+ if (error) {
+ *text_p = backup;
+ goto fail;
+ }
+ } else {
+ cmd->c_tag = ACL_USER_OBJ;
+ }
+ break;
+
+ case 'g': /* group */
+ if (!skip_tag_name(text_p, "group"))
+ goto user_entry;
+
+ backup = *text_p;
+ str = get_token(text_p);
+ if (str) {
+ cmd->c_tag = ACL_GROUP;
+ error = get_gid(unquote(str), &cmd->c_id);
+ free(str);
+ if (error) {
+ *text_p = backup;
+ goto fail;
+ }
+ } else {
+ cmd->c_tag = ACL_GROUP_OBJ;
+ }
+ break;
+
+ case 'o': /* other */
+ if (!skip_tag_name(text_p, "other"))
+ goto user_entry;
+ /* skip empty entry qualifier field (this field may
+ be missing for compatibility with Solaris.) */
+ SKIP_WS(*text_p);
+ if (**text_p == ':')
+ (*text_p)++;
+ cmd->c_tag = ACL_OTHER;
+ break;
+
+ case 'm': /* mask */
+ if (!skip_tag_name(text_p, "mask"))
+ goto user_entry;
+ /* skip empty entry qualifier field (this field may
+ be missing for compatibility with Solaris.) */
+ SKIP_WS(*text_p);
+ if (**text_p == ':')
+ (*text_p)++;
+ cmd->c_tag = ACL_MASK;
+ break;
+
+ default: /* assume "user:" */
+ goto user_entry;
+ }
+
+ SKIP_WS(*text_p);
+ if (**text_p == ',' || **text_p == '\0') {
+ if (parse_mode & SEQ_PARSE_NO_PERM)
+ return cmd;
+ else
+ goto fail;
+ }
+ if (!(parse_mode & SEQ_PARSE_WITH_PERM))
+ return cmd;
+
+ /* parse permissions */
+ SKIP_WS(*text_p);
+ if (**text_p >= '0' && **text_p <= '7') {
+ cmd->c_perm = 0;
+ while (**text_p == '0')
+ (*text_p)++;
+ if (**text_p >= '1' && **text_p <= '7') {
+ cmd->c_perm = (*(*text_p)++ - '0');
+ }
+
+ return cmd;
+ }
+
+ for (perm_chars=0; perm_chars<3; perm_chars++, (*text_p)++) {
+ switch(**text_p) {
+ case 'r': /* read */
+ if (cmd->c_perm & CMD_PERM_READ)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_READ;
+ break;
+
+ case 'w': /* write */
+ if (cmd->c_perm & CMD_PERM_WRITE)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_WRITE;
+ break;
+
+ case 'x': /* execute */
+ if (cmd->c_perm & CMD_PERM_EXECUTE)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_EXECUTE;
+ break;
+
+ case 'X': /* execute only if directory or some
+ entries already have execute permissions
+ set */
+ if (cmd->c_perm & CMD_PERM_COND_EXECUTE)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_COND_EXECUTE;
+ break;
+
+ case '-':
+ /* ignore */
+ break;
+
+ default:
+ if (perm_chars == 0)
+ goto fail;
+ return cmd;
+ }
+ }
+ if (perm_chars != 3)
+ goto fail;
+ return cmd;
+
+fail:
+ cmd_free(cmd);
+ return NULL;
+}
+
+
+/*
+ Parse a comma-separated list of acl entries.
+
+ which is set to the index of the first character that was not parsed,
+ or -1 in case of success.
+*/
+int
+parse_acl_seq(
+ seq_t seq,
+ const char *text_p,
+ int *which,
+ int seq_cmd,
+ int parse_mode)
+{
+ const char *initial_text_p = text_p;
+ cmd_t cmd;
+
+ if (which)
+ *which = -1;
+
+ while (*text_p != '\0') {
+ cmd = parse_acl_cmd(&text_p, seq_cmd, parse_mode);
+ if (cmd == NULL) {
+ errno = EINVAL;
+ goto fail;
+ }
+ if (seq_append(seq, cmd) != 0) {
+ cmd_free(cmd);
+ goto fail;
+ }
+ SKIP_WS(text_p);
+ if (*text_p != ',')
+ break;
+ text_p++;
+ }
+
+ if (*text_p != '\0') {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (which)
+ *which = (text_p - initial_text_p);
+ return -1;
+}
+
+
+
+int
+read_acl_comments(
+ FILE *file,
+ int *line,
+ char **path_p,
+ uid_t *uid_p,
+ gid_t *gid_p,
+ mode_t *flags)
+{
+ int c;
+ /*
+ Max PATH_MAX bytes even for UTF-8 path names and additional 9
+ bytes for "# file: ". Not a good solution but for now it is the
+ best I can do without too much impact on the code. [tw]
+ */
+ char linebuf[(4*PATH_MAX)+9];
+ char *cp;
+ char *p;
+ int comments_read = 0;
+
+ if (path_p)
+ *path_p = NULL;
+ if (uid_p)
+ *uid_p = ACL_UNDEFINED_ID;
+ if (gid_p)
+ *gid_p = ACL_UNDEFINED_ID;
+ if (flags)
+ *flags = 0;
+
+ for(;;) {
+ c = fgetc(file);
+ if (c == EOF)
+ break;
+ if (c==' ' || c=='\t' || c=='\r' || c=='\n') {
+ if (c=='\n')
+ (*line)++;
+ continue;
+ }
+ if (c != '#') {
+ ungetc(c, file);
+ break;
+ }
+ if (line)
+ (*line)++;
+
+ if (fgets(linebuf, sizeof(linebuf), file) == NULL)
+ break;
+
+ comments_read = 1;
+
+ p = strrchr(linebuf, '\0');
+ while (p > linebuf &&
+ (*(p-1)=='\r' || *(p-1)=='\n')) {
+ p--;
+ *p = '\0';
+ }
+
+ cp = linebuf;
+ SKIP_WS(cp);
+ if (strncmp(cp, "file:", 5) == 0) {
+ cp += 5;
+ SKIP_WS(cp);
+ cp = unquote(cp);
+
+ if (path_p) {
+ if (*path_p)
+ goto fail;
+ *path_p = (char*)malloc(strlen(cp)+1);
+ if (!*path_p)
+ return -1;
+ strcpy(*path_p, cp);
+ }
+ } else if (strncmp(cp, "owner:", 6) == 0) {
+ cp += 6;
+ SKIP_WS(cp);
+
+ if (uid_p) {
+ if (*uid_p != ACL_UNDEFINED_ID)
+ goto fail;
+ if (get_uid(unquote(cp), uid_p) != 0)
+ continue;
+ }
+ } else if (strncmp(cp, "group:", 6) == 0) {
+ cp += 6;
+ SKIP_WS(cp);
+
+ if (gid_p) {
+ if (*gid_p != ACL_UNDEFINED_ID)
+ goto fail;
+ if (get_gid(unquote(cp), gid_p) != 0)
+ continue;
+ }
+ } else if (strncmp(cp, "flags:", 6) == 0) {
+ mode_t f = 0;
+
+ cp += 6;
+ SKIP_WS(cp);
+
+ if (cp[0] == 's')
+ f |= S_ISUID;
+ else if (cp[0] != '-')
+ goto fail;
+ if (cp[1] == 's')
+ f |= S_ISGID;
+ else if (cp[1] != '-')
+ goto fail;
+ if (cp[2] == 't')
+ f |= S_ISVTX;
+ else if (cp[2] != '-')
+ goto fail;
+ if (cp[3] != '\0')
+ goto fail;
+
+ if (flags)
+ *flags = f;
+ }
+ }
+ if (ferror(file))
+ return -1;
+ return comments_read;
+fail:
+ if (path_p && *path_p) {
+ free(*path_p);
+ *path_p = NULL;
+ }
+ return -EINVAL;
+}
+
+
+int
+read_acl_seq(
+ FILE *file,
+ seq_t seq,
+ int seq_cmd,
+ int parse_mode,
+ int *line,
+ int *which)
+{
+ char linebuf[1024];
+ const char *cp;
+ cmd_t cmd;
+
+ if (which)
+ *which = -1;
+
+ for(;;) {
+ if (fgets(linebuf, sizeof(linebuf), file) == NULL)
+ break;
+ if (line)
+ (*line)++;
+
+ cp = linebuf;
+ SKIP_WS(cp);
+ if (*cp == '\0') {
+ if (!(parse_mode & SEQ_PARSE_MULTI))
+ continue;
+ break;
+ } else if (*cp == '#') {
+ continue;
+ }
+
+ cmd = parse_acl_cmd(&cp, seq_cmd, parse_mode);
+ if (cmd == NULL) {
+ errno = EINVAL;
+ goto fail;
+ }
+ if (seq_append(seq, cmd) != 0) {
+ cmd_free(cmd);
+ goto fail;
+ }
+
+ SKIP_WS(cp);
+ if (*cp != '\0' && *cp != '#') {
+ errno = EINVAL;
+ goto fail;
+ }
+ }
+
+ if (ferror(file))
+ goto fail;
+ return 0;
+
+fail:
+ if (which)
+ *which = (cp - linebuf);
+ return -1;
+}
+
diff --git a/tools/parse.h b/tools/parse.h
new file mode 100644
index 0000000..b2e68b4
--- /dev/null
+++ b/tools/parse.h
@@ -0,0 +1,85 @@
+/*
+ File: parse.h
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999 by Andreas Gruenbacher
+ <a.gruenbacher@computer.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __PARSE_H
+#define __PARSE_H
+
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include "sequence.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* parse options */
+
+#define SEQ_PARSE_WITH_PERM (0x0001)
+#define SEQ_PARSE_NO_PERM (0x0002)
+#define SEQ_PARSE_ANY_PERM (0x0001|0x0002)
+
+#define SEQ_PARSE_MULTI (0x0010)
+#define SEQ_PARSE_DEFAULT (0x0020) /* "default:" = default acl */
+
+#define SEQ_PROMOTE_ACL (0x0040) /* promote from acl
+ to default acl */
+
+cmd_t
+parse_acl_cmd(
+ const char **text_p,
+ int seq_cmd,
+ int parse_mode);
+int
+parse_acl_seq(
+ seq_t seq,
+ const char *text_p,
+ int *which,
+ int seq_cmd,
+ int parse_mode);
+int
+read_acl_comments(
+ FILE *file,
+ int *line,
+ char **path_p,
+ uid_t *uid_p,
+ gid_t *gid_p,
+ mode_t *flags);
+int
+read_acl_seq(
+ FILE *file,
+ seq_t seq,
+ int seq_cmd,
+ int parse_mode,
+ int *line,
+ int *which);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __PARSE_H */
+
diff --git a/tools/sequence.c b/tools/sequence.c
new file mode 100644
index 0000000..a418b23
--- /dev/null
+++ b/tools/sequence.c
@@ -0,0 +1,162 @@
+/*
+ File: sequence.c
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999, 2000
+ Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include <stdlib.h>
+#include "sequence.h"
+
+
+cmd_t
+cmd_init(
+ void)
+{
+ return (cmd_t)malloc(sizeof(struct cmd_obj));
+}
+
+
+void
+cmd_free(
+ cmd_t cmd)
+{
+ free(cmd);
+}
+
+
+seq_t
+seq_init(
+ void)
+{
+ seq_t seq = (seq_t)malloc(sizeof(struct seq_obj));
+ if (seq == NULL)
+ return NULL;
+ seq->s_first = seq->s_last = NULL;
+ return seq;
+}
+
+
+int
+seq_free(
+ seq_t seq)
+{
+ cmd_t cmd = seq->s_first;
+ while (cmd) {
+ seq->s_first = seq->s_first->c_next;
+ cmd_free(cmd);
+ cmd = seq->s_first;
+ }
+ free(seq);
+ return 0;
+}
+
+
+int
+seq_empty(
+ seq_t seq)
+{
+ return (seq->s_first == NULL);
+}
+
+
+int
+seq_append(
+ seq_t seq,
+ cmd_t cmd)
+{
+ cmd->c_next = NULL;
+ if (seq->s_first == NULL) {
+ seq->s_first = seq->s_last = cmd;
+ } else {
+ seq->s_last->c_next = cmd;
+ seq->s_last = cmd;
+ }
+ return 0;
+}
+
+
+int
+seq_append_cmd(
+ seq_t seq,
+ cmd_tag_t cmd,
+ acl_type_t type)
+{
+ cmd_t cmd_d = cmd_init();
+ if (cmd_d == NULL)
+ return -1;
+ cmd_d->c_cmd = cmd;
+ cmd_d->c_type = type;
+ if (seq_append(seq, cmd_d) != 0) {
+ cmd_free(cmd_d);
+ return -1;
+ }
+ return 0;
+}
+
+
+int
+seq_get_cmd(
+ seq_t seq,
+ int which,
+ cmd_t *cmd)
+{
+ if (which == SEQ_FIRST_CMD) {
+ if (seq->s_first == NULL)
+ return 0;
+ if (cmd)
+ *cmd = seq->s_first;
+ return 1;
+ } else if (which == SEQ_NEXT_CMD) {
+ if (cmd == NULL)
+ return -1;
+ if (*cmd) {
+ *cmd = (*cmd)->c_next;
+ return (*cmd == NULL) ? 0 : 1;
+ }
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+
+int
+seq_delete_cmd(
+ seq_t seq,
+ cmd_t cmd)
+{
+ cmd_t prev = seq->s_first;
+
+ if (cmd == seq->s_first) {
+ seq->s_first = seq->s_first->c_next;
+ cmd_free(cmd);
+ return 0;
+ }
+ while (prev != NULL && prev->c_next != cmd)
+ prev = prev->c_next;
+ if (prev == NULL)
+ return -1;
+ if (cmd == seq->s_last)
+ seq->s_last = prev;
+ prev->c_next = cmd->c_next;
+ cmd_free(cmd);
+ return 0;
+}
+
diff --git a/tools/sequence.h b/tools/sequence.h
new file mode 100644
index 0000000..c5d7403
--- /dev/null
+++ b/tools/sequence.h
@@ -0,0 +1,115 @@
+/*
+ File: sequence.h
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999 by Andreas Gruenbacher
+ <a.gruenbacher@computer.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __SEQUENCE_H
+#define __SEQUENCE_H
+
+
+#include <sys/acl.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef unsigned int cmd_tag_t;
+
+struct cmd_obj {
+ cmd_tag_t c_cmd;
+ acl_type_t c_type;
+ acl_tag_t c_tag;
+ uid_t c_id;
+ mode_t c_perm;
+ struct cmd_obj *c_next;
+};
+
+typedef struct cmd_obj *cmd_t;
+
+struct seq_obj {
+ cmd_t s_first;
+ cmd_t s_last;
+};
+
+typedef struct seq_obj *seq_t;
+
+/* command types */
+#define CMD_ENTRY_REPLACE (0)
+#define CMD_REMOVE_ENTRY (3)
+#define CMD_REMOVE_EXTENDED_ACL (4)
+#define CMD_REMOVE_ACL (5)
+
+/* constants for permission specifiers */
+#define CMD_PERM_READ (4)
+#define CMD_PERM_WRITE (2)
+#define CMD_PERM_EXECUTE (1)
+#define CMD_PERM_COND_EXECUTE (8)
+
+/* iteration over command sequence */
+#define SEQ_FIRST_CMD (0)
+#define SEQ_NEXT_CMD (1)
+
+/* command sequence manipulation */
+
+cmd_t
+cmd_init(
+ void);
+void
+cmd_free(
+ cmd_t cmd);
+seq_t
+seq_init(
+ void);
+int
+seq_free(
+ seq_t seq);
+int
+seq_empty(
+ seq_t seq);
+int
+seq_append(
+ seq_t seq,
+ cmd_t cmd);
+int
+seq_append_cmd(
+ seq_t seq,
+ cmd_tag_t cmd,
+ acl_type_t type);
+int
+seq_get_cmd(
+ seq_t seq,
+ int which,
+ cmd_t *cmd);
+int
+seq_delete_cmd(
+ seq_t seq,
+ cmd_t cmd);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __SEQUENCE_H */
+
diff --git a/tools/setfacl.c b/tools/setfacl.c
new file mode 100644
index 0000000..f6135b7
--- /dev/null
+++ b/tools/setfacl.c
@@ -0,0 +1,669 @@
+/*
+ File: setfacl.c
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999-2002
+ Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <getopt.h>
+#include "misc.h"
+#include "sequence.h"
+#include "parse.h"
+#include "do_set.h"
+#include "walk_tree.h"
+
+#define POSIXLY_CORRECT_STR "POSIXLY_CORRECT"
+
+/* '-' stands for `process non-option arguments in loop' */
+#if !POSIXLY_CORRECT
+# define CMD_LINE_OPTIONS "-:bkndvhm:M:x:X:RLP"
+# define CMD_LINE_SPEC "[-bkndRLP] { -m|-M|-x|-X ... } file ..."
+#endif
+#define POSIXLY_CMD_LINE_OPTIONS "-:bkndvhm:M:x:X:"
+#define POSIXLY_CMD_LINE_SPEC "[-bknd] {-m|-M|-x|-X ... } file ..."
+
+struct option long_options[] = {
+#if !POSIXLY_CORRECT
+ { "set", 1, 0, 's' },
+ { "set-file", 1, 0, 'S' },
+
+ { "mask", 0, 0, 'r' },
+ { "recursive", 0, 0, 'R' },
+ { "logical", 0, 0, 'L' },
+ { "physical", 0, 0, 'P' },
+ { "restore", 1, 0, 'B' },
+ { "test", 0, 0, 't' },
+#endif
+ { "modify", 1, 0, 'm' },
+ { "modify-file", 1, 0, 'M' },
+ { "remove", 1, 0, 'x' },
+ { "remove-file", 1, 0, 'X' },
+
+ { "default", 0, 0, 'd' },
+ { "no-mask", 0, 0, 'n' },
+ { "remove-all", 0, 0, 'b' },
+ { "remove-default", 0, 0, 'k' },
+ { "version", 0, 0, 'v' },
+ { "help", 0, 0, 'h' },
+ { NULL, 0, 0, 0 },
+};
+
+const char *progname;
+const char *cmd_line_options, *cmd_line_spec;
+
+int walk_flags = WALK_TREE_DEREFERENCE_TOPLEVEL;
+int opt_recalculate; /* recalculate mask entry (0=default, 1=yes, -1=no) */
+int opt_promote; /* promote access ACL to default ACL */
+int opt_test; /* do not write to the file system.
+ Print what would happen instead. */
+#if POSIXLY_CORRECT
+const int posixly_correct = 1; /* Posix compatible behavior! */
+#else
+int posixly_correct; /* Posix compatible behavior? */
+#endif
+int chown_error;
+int promote_warning;
+
+
+static const char *xquote(const char *str, const char *quote_chars)
+{
+ const char *q = quote(str, quote_chars);
+ if (q == NULL) {
+ fprintf(stderr, "%s: %s\n", progname, strerror(errno));
+ exit(1);
+ }
+ return q;
+}
+
+int
+has_any_of_type(
+ cmd_t cmd,
+ acl_type_t acl_type)
+{
+ while (cmd) {
+ if (cmd->c_type == acl_type)
+ return 1;
+ cmd = cmd->c_next;
+ }
+ return 0;
+}
+
+
+#if !POSIXLY_CORRECT
+int
+restore(
+ FILE *file,
+ const char *filename)
+{
+ char *path_p;
+ struct stat st;
+ uid_t uid;
+ gid_t gid;
+ mode_t mask, flags;
+ struct do_set_args args = { };
+ int line = 0, backup_line;
+ int error, status = 0;
+ int chmod_required = 0;
+
+ memset(&st, 0, sizeof(st));
+
+ for(;;) {
+ backup_line = line;
+ error = read_acl_comments(file, &line, &path_p, &uid, &gid,
+ &flags);
+ if (error < 0) {
+ error = -error;
+ goto fail;
+ }
+ if (error == 0)
+ return status;
+
+ if (path_p == NULL) {
+ if (filename) {
+ fprintf(stderr, _("%s: %s: No filename found "
+ "in line %d, aborting\n"),
+ progname, xquote(filename, "\n\r"),
+ backup_line);
+ } else {
+ fprintf(stderr, _("%s: No filename found in "
+ "line %d of standard input, "
+ "aborting\n"),
+ progname, backup_line);
+ }
+ status = 1;
+ goto getout;
+ }
+
+ if (!(args.seq = seq_init()))
+ goto fail_errno;
+ if (seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_ACCESS) ||
+ seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_DEFAULT))
+ goto fail_errno;
+
+ error = read_acl_seq(file, args.seq, CMD_ENTRY_REPLACE,
+ SEQ_PARSE_WITH_PERM |
+ SEQ_PARSE_DEFAULT |
+ SEQ_PARSE_MULTI,
+ &line, NULL);
+ if (error != 0) {
+ fprintf(stderr, _("%s: %s: %s in line %d\n"),
+ progname, xquote(filename, "\n\r"), strerror(errno),
+ line);
+ status = 1;
+ goto getout;
+ }
+
+ error = stat(path_p, &st);
+ if (opt_test && error != 0) {
+ fprintf(stderr, "%s: %s: %s\n", progname,
+ xquote(path_p, "\n\r"), strerror(errno));
+ status = 1;
+ }
+
+ args.mode = 0;
+ error = do_set(path_p, &st, 0, &args);
+ if (error != 0) {
+ status = 1;
+ goto resume;
+ }
+
+ if (uid != ACL_UNDEFINED_ID && uid != st.st_uid)
+ st.st_uid = uid;
+ else
+ st.st_uid = -1;
+ if (gid != ACL_UNDEFINED_ID && gid != st.st_gid)
+ st.st_gid = gid;
+ else
+ st.st_gid = -1;
+ if (!opt_test &&
+ (st.st_uid != -1 || st.st_gid != -1)) {
+ if (chown(path_p, st.st_uid, st.st_gid) != 0) {
+ fprintf(stderr, _("%s: %s: Cannot change "
+ "owner/group: %s\n"),
+ progname, xquote(path_p, "\n\r"),
+ strerror(errno));
+ status = 1;
+ }
+
+ /* chown() clears setuid/setgid so force a chmod if
+ * S_ISUID/S_ISGID was expected */
+ if ((st.st_mode & flags) & (S_ISUID | S_ISGID))
+ chmod_required = 1;
+ }
+
+ mask = S_ISUID | S_ISGID | S_ISVTX;
+ if (chmod_required || ((st.st_mode & mask) != (flags & mask))) {
+ if (!args.mode)
+ args.mode = st.st_mode;
+ args.mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+ if (chmod(path_p, flags | args.mode) != 0) {
+ fprintf(stderr, _("%s: %s: Cannot change "
+ "mode: %s\n"),
+ progname, xquote(path_p, "\n\r"),
+ strerror(errno));
+ status = 1;
+ }
+ }
+resume:
+ if (path_p) {
+ free(path_p);
+ path_p = NULL;
+ }
+ if (args.seq) {
+ seq_free(args.seq);
+ args.seq = NULL;
+ }
+ }
+
+getout:
+ if (path_p) {
+ free(path_p);
+ path_p = NULL;
+ }
+ if (args.seq) {
+ seq_free(args.seq);
+ args.seq = NULL;
+ }
+ return status;
+
+fail_errno:
+ error = errno;
+fail:
+ fprintf(stderr, "%s: %s: %s\n", progname, xquote(filename, "\n\r"),
+ strerror(error));
+ status = 1;
+ goto getout;
+}
+#endif
+
+
+void help(void)
+{
+ printf(_("%s %s -- set file access control lists\n"),
+ progname, VERSION);
+ printf(_("Usage: %s %s\n"),
+ progname, cmd_line_spec);
+ printf(_(
+" -m, --modify=acl modify the current ACL(s) of file(s)\n"
+" -M, --modify-file=file read ACL entries to modify from file\n"
+" -x, --remove=acl remove entries from the ACL(s) of file(s)\n"
+" -X, --remove-file=file read ACL entries to remove from file\n"
+" -b, --remove-all remove all extended ACL entries\n"
+" -k, --remove-default remove the default ACL\n"));
+#if !POSIXLY_CORRECT
+ if (!posixly_correct) {
+ printf(_(
+" --set=acl set the ACL of file(s), replacing the current ACL\n"
+" --set-file=file read ACL entries to set from file\n"
+" --mask do recalculate the effective rights mask\n"));
+ }
+#endif
+ printf(_(
+" -n, --no-mask don't recalculate the effective rights mask\n"
+" -d, --default operations apply to the default ACL\n"));
+#if !POSIXLY_CORRECT
+ if (!posixly_correct) {
+ printf(_(
+" -R, --recursive recurse into subdirectories\n"
+" -L, --logical logical walk, follow symbolic links\n"
+" -P, --physical physical walk, do not follow symbolic links\n"
+" --restore=file restore ACLs (inverse of `getfacl -R')\n"
+" --test test mode (ACLs are not modified)\n"));
+ }
+#endif
+ printf(_(
+" -v, --version print version and exit\n"
+" -h, --help this help text\n"));
+}
+
+
+int next_file(const char *arg, seq_t seq)
+{
+ char *line;
+ int errors = 0;
+ struct do_set_args args;
+
+ args.seq = seq;
+
+ if (strcmp(arg, "-") == 0) {
+ while ((line = next_line(stdin)))
+ errors = walk_tree(line, walk_flags, 0, do_set, &args);
+ if (!feof(stdin)) {
+ fprintf(stderr, _("%s: Standard input: %s\n"),
+ progname, strerror(errno));
+ errors = 1;
+ }
+ } else {
+ errors = walk_tree(arg, walk_flags, 0, do_set, &args);
+ }
+ return errors ? 1 : 0;
+}
+
+
+#define ERRNO_ERROR(s) \
+ ({status = (s); goto errno_error; })
+
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ int saw_files = 0;
+ int status = 0;
+ FILE *file;
+ int which;
+ int lineno;
+ int error;
+ seq_t seq;
+ int seq_cmd, parse_mode;
+
+ progname = basename(argv[0]);
+
+#if POSIXLY_CORRECT
+ cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
+ cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
+#else
+ if (getenv(POSIXLY_CORRECT_STR))
+ posixly_correct = 1;
+ if (!posixly_correct) {
+ cmd_line_options = CMD_LINE_OPTIONS;
+ cmd_line_spec = _(CMD_LINE_SPEC);
+ } else {
+ cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
+ cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
+ }
+#endif
+
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ seq = seq_init();
+ if (!seq)
+ ERRNO_ERROR(1);
+
+ while ((opt = getopt_long(argc, argv, cmd_line_options,
+ long_options, NULL)) != -1) {
+ /* we remember the two REMOVE_ACL commands of the set
+ operations because we may later need to delete them. */
+ cmd_t seq_remove_default_acl_cmd = NULL;
+ cmd_t seq_remove_acl_cmd = NULL;
+
+ if (opt != '\1' && saw_files) {
+ seq_free(seq);
+ seq = seq_init();
+ if (!seq)
+ ERRNO_ERROR(1);
+ saw_files = 0;
+ }
+
+ switch (opt) {
+ case 'b': /* remove all extended entries */
+ if (seq_append_cmd(seq, CMD_REMOVE_EXTENDED_ACL,
+ ACL_TYPE_ACCESS) ||
+ seq_append_cmd(seq, CMD_REMOVE_ACL,
+ ACL_TYPE_DEFAULT))
+ ERRNO_ERROR(1);
+ break;
+
+ case 'k': /* remove default ACL */
+ if (seq_append_cmd(seq, CMD_REMOVE_ACL,
+ ACL_TYPE_DEFAULT))
+ ERRNO_ERROR(1);
+ break;
+
+ case 'n': /* do not recalculate mask */
+ opt_recalculate = -1;
+ break;
+
+ case 'r': /* force recalculate mask */
+ opt_recalculate = 1;
+ break;
+
+ case 'd': /* operations apply to default ACL */
+ opt_promote = 1;
+ break;
+
+ case 's': /* set */
+ if (seq_append_cmd(seq, CMD_REMOVE_ACL,
+ ACL_TYPE_ACCESS))
+ ERRNO_ERROR(1);
+ seq_remove_acl_cmd = seq->s_last;
+ if (seq_append_cmd(seq, CMD_REMOVE_ACL,
+ ACL_TYPE_DEFAULT))
+ ERRNO_ERROR(1);
+ seq_remove_default_acl_cmd = seq->s_last;
+
+ seq_cmd = CMD_ENTRY_REPLACE;
+ parse_mode = SEQ_PARSE_WITH_PERM;
+ goto set_modify_delete;
+
+ case 'm': /* modify */
+ seq_cmd = CMD_ENTRY_REPLACE;
+ parse_mode = SEQ_PARSE_WITH_PERM;
+ goto set_modify_delete;
+
+ case 'x': /* delete */
+ seq_cmd = CMD_REMOVE_ENTRY;
+#if POSIXLY_CORRECT
+ parse_mode = SEQ_PARSE_ANY_PERM;
+#else
+ if (posixly_correct)
+ parse_mode = SEQ_PARSE_ANY_PERM;
+ else
+ parse_mode = SEQ_PARSE_NO_PERM;
+#endif
+ goto set_modify_delete;
+
+ set_modify_delete:
+ if (!posixly_correct)
+ parse_mode |= SEQ_PARSE_DEFAULT;
+ if (opt_promote)
+ parse_mode |= SEQ_PROMOTE_ACL;
+ if (parse_acl_seq(seq, optarg, &which,
+ seq_cmd, parse_mode) != 0) {
+ if (which < 0 ||
+ (size_t) which >= strlen(optarg)) {
+ fprintf(stderr, _(
+ "%s: Option "
+ "-%c incomplete\n"),
+ progname, opt);
+ } else {
+ fprintf(stderr, _(
+ "%s: Option "
+ "-%c: %s near "
+ "character %d\n"),
+ progname, opt,
+ strerror(errno),
+ which+1);
+ }
+ status = 2;
+ goto cleanup;
+ }
+ break;
+
+ case 'S': /* set from file */
+ if (seq_append_cmd(seq, CMD_REMOVE_ACL,
+ ACL_TYPE_ACCESS))
+ ERRNO_ERROR(1);
+ seq_remove_acl_cmd = seq->s_last;
+ if (seq_append_cmd(seq, CMD_REMOVE_ACL,
+ ACL_TYPE_DEFAULT))
+ ERRNO_ERROR(1);
+ seq_remove_default_acl_cmd = seq->s_last;
+
+ seq_cmd = CMD_ENTRY_REPLACE;
+ parse_mode = SEQ_PARSE_WITH_PERM;
+ goto set_modify_delete_from_file;
+
+ case 'M': /* modify from file */
+ seq_cmd = CMD_ENTRY_REPLACE;
+ parse_mode = SEQ_PARSE_WITH_PERM;
+ goto set_modify_delete_from_file;
+
+ case 'X': /* delete from file */
+ seq_cmd = CMD_REMOVE_ENTRY;
+#if POSIXLY_CORRECT
+ parse_mode = SEQ_PARSE_ANY_PERM;
+#else
+ if (posixly_correct)
+ parse_mode = SEQ_PARSE_ANY_PERM;
+ else
+ parse_mode = SEQ_PARSE_NO_PERM;
+#endif
+ goto set_modify_delete_from_file;
+
+ set_modify_delete_from_file:
+ if (!posixly_correct)
+ parse_mode |= SEQ_PARSE_DEFAULT;
+ if (opt_promote)
+ parse_mode |= SEQ_PROMOTE_ACL;
+ if (strcmp(optarg, "-") == 0) {
+ file = stdin;
+ } else {
+ file = fopen(optarg, "r");
+ if (file == NULL) {
+ fprintf(stderr, "%s: %s: %s\n",
+ progname,
+ xquote(optarg, "\n\r"),
+ strerror(errno));
+ status = 2;
+ goto cleanup;
+ }
+ }
+
+ lineno = 0;
+ error = read_acl_seq(file, seq, seq_cmd,
+ parse_mode, &lineno, NULL);
+
+ if (file != stdin) {
+ fclose(file);
+ }
+
+ if (error) {
+ if (!errno)
+ errno = EINVAL;
+
+ if (file != stdin) {
+ fprintf(stderr, _(
+ "%s: %s in line "
+ "%d of file %s\n"),
+ progname,
+ strerror(errno),
+ lineno,
+ xquote(optarg, "\n\r"));
+ } else {
+ fprintf(stderr, _(
+ "%s: %s in line "
+ "%d of standard "
+ "input\n"), progname,
+ strerror(errno),
+ lineno);
+ }
+ status = 2;
+ goto cleanup;
+ }
+ break;
+
+
+ case '\1': /* file argument */
+ if (seq_empty(seq))
+ goto synopsis;
+ saw_files = 1;
+
+ status = next_file(optarg, seq);
+ break;
+
+ case 'B': /* restore ACL backup */
+ saw_files = 1;
+
+ if (strcmp(optarg, "-") == 0)
+ file = stdin;
+ else {
+ file = fopen(optarg, "r");
+ if (file == NULL) {
+ fprintf(stderr, "%s: %s: %s\n",
+ progname,
+ xquote(optarg, "\n\r"),
+ strerror(errno));
+ status = 2;
+ goto cleanup;
+ }
+ }
+
+ status = restore(file,
+ (file == stdin) ? NULL : optarg);
+
+ if (file != stdin)
+ fclose(file);
+ if (status != 0)
+ goto cleanup;
+ break;
+
+ case 'R': /* recursive */
+ walk_flags |= WALK_TREE_RECURSIVE;
+ break;
+
+ case 'L': /* follow symlinks */
+ walk_flags |= WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE;
+ walk_flags &= ~WALK_TREE_PHYSICAL;
+ break;
+
+ case 'P': /* do not follow symlinks */
+ walk_flags |= WALK_TREE_PHYSICAL;
+ walk_flags &= ~(WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE |
+ WALK_TREE_DEREFERENCE_TOPLEVEL);
+ break;
+
+ case 't': /* test mode */
+ opt_test = 1;
+ break;
+
+ case 'v': /* print version and exit */
+ printf("%s " VERSION "\n", progname);
+ status = 0;
+ goto cleanup;
+
+ case 'h': /* help! */
+ help();
+ status = 0;
+ goto cleanup;
+
+ case ':': /* option missing */
+ case '?': /* unknown option */
+ default:
+ goto synopsis;
+ }
+ if (seq_remove_acl_cmd) {
+ /* This was a set operation. Check if there are
+ actually entries of ACL_TYPE_ACCESS; if there
+ are none, we need to remove this command! */
+ if (!has_any_of_type(seq_remove_acl_cmd->c_next,
+ ACL_TYPE_ACCESS))
+ seq_delete_cmd(seq, seq_remove_acl_cmd);
+ }
+ if (seq_remove_default_acl_cmd) {
+ /* This was a set operation. Check if there are
+ actually entries of ACL_TYPE_DEFAULT; if there
+ are none, we need to remove this command! */
+ if (!has_any_of_type(seq_remove_default_acl_cmd->c_next,
+ ACL_TYPE_DEFAULT))
+ seq_delete_cmd(seq, seq_remove_default_acl_cmd);
+ }
+ }
+ while (optind < argc) {
+ if(!seq)
+ goto synopsis;
+ if (seq_empty(seq))
+ goto synopsis;
+ saw_files = 1;
+
+ status = next_file(argv[optind++], seq);
+ }
+ if (!saw_files)
+ goto synopsis;
+
+ goto cleanup;
+
+synopsis:
+ fprintf(stderr, _("Usage: %s %s\n"),
+ progname, cmd_line_spec);
+ fprintf(stderr, _("Try `%s --help' for more information.\n"),
+ progname);
+ status = 2;
+ goto cleanup;
+
+errno_error:
+ fprintf(stderr, "%s: %s\n", progname, strerror(errno));
+ goto cleanup;
+
+cleanup:
+ if (seq)
+ seq_free(seq);
+ return status;
+}
+
diff --git a/tools/user_group.c b/tools/user_group.c
new file mode 100644
index 0000000..61fa0c3
--- /dev/null
+++ b/tools/user_group.c
@@ -0,0 +1,59 @@
+/*
+ File: user_group.c
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999, 2000
+ Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
+
+ 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 2 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 library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "user_group.h"
+
+
+const char *
+user_name(uid_t uid, int numeric)
+{
+ struct passwd *passwd = numeric ? NULL : getpwuid(uid);
+ static char uid_str[22];
+ int ret;
+
+ if (passwd != NULL)
+ return passwd->pw_name;
+ ret = snprintf(uid_str, sizeof(uid_str), "%ld", (long)uid);
+ if (ret < 1 || (size_t)ret >= sizeof(uid_str))
+ return "?";
+ return uid_str;
+}
+
+
+const char *
+group_name(gid_t gid, int numeric)
+{
+ struct group *group = numeric ? NULL : getgrgid(gid);
+ static char gid_str[22];
+ int ret;
+
+ if (group != NULL)
+ return group->gr_name;
+ ret = snprintf(gid_str, sizeof(gid_str), "%ld", (long)gid);
+ if (ret < 1 || (size_t)ret >= sizeof(gid_str))
+ return "?";
+ return gid_str;
+}
+
diff --git a/tools/user_group.h b/tools/user_group.h
new file mode 100644
index 0000000..aad5846
--- /dev/null
+++ b/tools/user_group.h
@@ -0,0 +1,31 @@
+/*
+ File: user_group.h
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999 by Andreas Gruenbacher
+ <a.gruenbacher@computer.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+const char *
+user_name(uid_t uid, int numeric);
+const char *
+group_name(gid_t uid, int numeric);
+