diff options
author | Mike Frysinger <vapier@gentoo.org> | 2014-01-10 10:07:41 -0500 |
---|---|---|
committer | Andreas Gruenbacher <agruen@linbit.com> | 2014-01-13 13:18:39 +0100 |
commit | c8f23c92177c5a82ab3699b1b0d4acbee9afb770 (patch) | |
tree | 72b6c7cf7a006babd2177055d8c61d1f6bd15375 /tools | |
parent | 2d13383b7fbcc04fc16681caa4f43d9d9083cca1 (diff) | |
download | acl-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.am | 23 | ||||
-rw-r--r-- | tools/chacl.c | 354 | ||||
-rw-r--r-- | tools/do_set.c | 528 | ||||
-rw-r--r-- | tools/do_set.h | 36 | ||||
-rw-r--r-- | tools/getfacl.c | 747 | ||||
-rw-r--r-- | tools/parse.c | 594 | ||||
-rw-r--r-- | tools/parse.h | 85 | ||||
-rw-r--r-- | tools/sequence.c | 162 | ||||
-rw-r--r-- | tools/sequence.h | 115 | ||||
-rw-r--r-- | tools/setfacl.c | 669 | ||||
-rw-r--r-- | tools/user_group.c | 59 | ||||
-rw-r--r-- | tools/user_group.h | 31 |
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); + |