3 (Linux Access Control List Management)
5 Copyright (C) 1999-2002
6 Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or (at
11 your option) any later version.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this library; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 #include <acl/libacl.h>
39 #include "user_group.h"
40 #include "walk_tree.h"
43 #define POSIXLY_CORRECT_STR "POSIXLY_CORRECT"
46 # define CMD_LINE_OPTIONS "aceEsRLPtpndvh"
48 #define POSIXLY_CMD_LINE_OPTIONS "d"
50 struct option long_options[] = {
52 { "access", 0, 0, 'a' },
53 { "omit-header", 0, 0, 'c' },
54 { "all-effective", 0, 0, 'e' },
55 { "no-effective", 0, 0, 'E' },
56 { "skip-base", 0, 0, 's' },
57 { "recursive", 0, 0, 'R' },
58 { "logical", 0, 0, 'L' },
59 { "physical", 0, 0, 'P' },
60 { "tabular", 0, 0, 't' },
61 { "absolute-names", 0, 0, 'p' },
62 { "numeric", 0, 0, 'n' },
64 { "default", 0, 0, 'd' },
65 { "version", 0, 0, 'v' },
66 { "help", 0, 0, 'h' },
71 const char *cmd_line_options;
73 int walk_flags = WALK_TREE_DEREFERENCE_TOPLEVEL;
75 int opt_print_default_acl;
76 int opt_strip_leading_slash = 1;
77 int opt_comments = 1; /* include comments */
78 int opt_skip_base; /* skip files that only have the base entries */
79 int opt_tabular; /* tabular output format (alias `showacl') */
81 const int posixly_correct = 1; /* Posix compatible behavior! */
83 int posixly_correct; /* Posix compatible behavior? */
86 int absolute_warning; /* Absolute path warning was issued */
87 int print_options = TEXT_SOME_EFFECTIVE;
88 int opt_numeric; /* don't convert id's to symbolic names */
91 static const char *xquote(const char *str, const char *quote_chars)
93 const char *q = quote(str, quote_chars);
95 fprintf(stderr, "%s: %s\n", progname, strerror(errno));
102 struct name_list *next;
106 void free_list(struct name_list *names)
108 struct name_list *next;
117 struct name_list *get_list(const struct stat *st, acl_t acl)
119 struct name_list *first = NULL, *last = NULL;
124 ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &ent);
130 const char *name = "";
133 acl_get_tag_type(ent, &e_type);
136 name = user_name(st->st_uid, opt_numeric);
140 id_p = acl_get_qualifier(ent);
142 name = user_name(*id_p, opt_numeric);
146 name = group_name(st->st_gid, opt_numeric);
150 id_p = acl_get_qualifier(ent);
152 name = group_name(*id_p, opt_numeric);
155 name = xquote(name, "\t\n\r");
158 first = last = (struct name_list *)
159 malloc(sizeof(struct name_list) + len + 1);
161 last->next = (struct name_list *)
162 malloc(sizeof(struct name_list) + len + 1);
170 strcpy(last->name, name);
172 ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &ent);
177 int max_name_length(struct name_list *names)
180 while (names != NULL) {
181 struct name_list *next = names->next;
182 int len = strlen(names->name);
193 struct acl_perm_def {
198 struct acl_perm_def acl_perm_defs[] = {
201 { ACL_EXECUTE, 'x' },
205 #define ACL_PERMS (sizeof(acl_perm_defs) / sizeof(struct acl_perm_def) - 1)
207 void acl_perm_str(acl_entry_t entry, char *str)
209 acl_permset_t permset;
212 acl_get_permset(entry, &permset);
213 for (n = 0; n < (int) ACL_PERMS; n++) {
214 str[n] = (acl_get_perm(permset, acl_perm_defs[n].tag) ?
215 acl_perm_defs[n].c : '-');
220 void acl_mask_perm_str(acl_t acl, char *str)
225 if (acl_get_entry(acl, ACL_FIRST_ENTRY, &entry) != 1)
230 acl_get_tag_type(entry, &tag);
231 if (tag == ACL_MASK) {
232 acl_perm_str(entry, str);
235 if (acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) != 1)
240 void apply_mask(char *perm, const char *mask)
243 if (*mask == '-' && *perm >= 'a' && *perm <= 'z')
244 *perm = *perm - 'a' + 'A';
251 int show_line(FILE *stream, struct name_list **acl_names, acl_t acl,
252 acl_entry_t *acl_ent, const char *acl_mask,
253 struct name_list **dacl_names, acl_t dacl,
254 acl_entry_t *dacl_ent, const char *dacl_mask)
257 const char *tag, *name;
258 char acl_perm[ACL_PERMS+1], dacl_perm[ACL_PERMS+1];
261 acl_get_tag_type(*acl_ent, &tag_type);
262 name = (*acl_names)->name;
264 acl_get_tag_type(*dacl_ent, &tag_type);
265 name = (*dacl_names)->name;
291 memset(acl_perm, ' ', ACL_PERMS);
292 acl_perm[ACL_PERMS] = '\0';
294 acl_perm_str(*acl_ent, acl_perm);
295 if (tag_type != ACL_USER_OBJ && tag_type != ACL_OTHER &&
296 tag_type != ACL_MASK)
297 apply_mask(acl_perm, acl_mask);
299 memset(dacl_perm, ' ', ACL_PERMS);
300 dacl_perm[ACL_PERMS] = '\0';
302 acl_perm_str(*dacl_ent, dacl_perm);
303 if (tag_type != ACL_USER_OBJ && tag_type != ACL_OTHER &&
304 tag_type != ACL_MASK)
305 apply_mask(dacl_perm, dacl_mask);
308 fprintf(stream, "%-5s %*s %*s %*s\n",
309 tag, -names_width, name,
310 -(int)ACL_PERMS, acl_perm,
311 -(int)ACL_PERMS, dacl_perm);
314 acl_get_entry(acl, ACL_NEXT_ENTRY, acl_ent);
315 (*acl_names) = (*acl_names)->next;
318 acl_get_entry(dacl, ACL_NEXT_ENTRY, dacl_ent);
319 (*dacl_names) = (*dacl_names)->next;
324 int do_show(FILE *stream, const char *path_p, const struct stat *st,
325 acl_t acl, acl_t dacl)
327 struct name_list *acl_names = get_list(st, acl),
328 *first_acl_name = acl_names;
329 struct name_list *dacl_names = get_list(st, dacl),
330 *first_dacl_name = dacl_names;
332 int acl_names_width = max_name_length(acl_names);
333 int dacl_names_width = max_name_length(dacl_names);
335 acl_entry_t dacl_ent;
336 char acl_mask[ACL_PERMS+1], dacl_mask[ACL_PERMS+1];
340 if (acl_names_width > names_width)
341 names_width = acl_names_width;
342 if (dacl_names_width > names_width)
343 names_width = dacl_names_width;
347 acl_mask_perm_str(acl, acl_mask);
348 ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_ent);
356 acl_mask_perm_str(dacl, dacl_mask);
357 ret = acl_get_entry(dacl, ACL_FIRST_ENTRY, &dacl_ent);
363 fprintf(stream, "# file: %s\n", xquote(path_p, "\n\r"));
364 while (acl_names != NULL || dacl_names != NULL) {
365 acl_tag_t acl_tag, dacl_tag;
368 acl_get_tag_type(acl_ent, &acl_tag);
370 acl_get_tag_type(dacl_ent, &dacl_tag);
372 if (acl && (!dacl || acl_tag < dacl_tag)) {
373 show_line(stream, &acl_names, acl, &acl_ent, acl_mask,
374 NULL, NULL, NULL, NULL);
376 } else if (dacl && (!acl || dacl_tag < acl_tag)) {
377 show_line(stream, NULL, NULL, NULL, NULL,
378 &dacl_names, dacl, &dacl_ent, dacl_mask);
381 if (acl_tag == ACL_USER || acl_tag == ACL_GROUP) {
382 id_t *acl_id_p = NULL, *dacl_id_p = NULL;
384 acl_id_p = acl_get_qualifier(acl_ent);
386 dacl_id_p = acl_get_qualifier(dacl_ent);
388 if (acl && (!dacl || *acl_id_p < *dacl_id_p)) {
389 show_line(stream, &acl_names, acl,
391 NULL, NULL, NULL, NULL);
394 (!acl || *dacl_id_p < *acl_id_p)) {
395 show_line(stream, NULL, NULL, NULL,
396 NULL, &dacl_names, dacl,
397 &dacl_ent, dacl_mask);
401 show_line(stream, &acl_names, acl, &acl_ent, acl_mask,
402 &dacl_names, dacl, &dacl_ent, dacl_mask);
406 free_list(first_acl_name);
407 free_list(first_dacl_name);
413 * Create an ACL from the file permission bits
414 * of the file PATH_P.
417 acl_get_file_mode(const char *path_p)
421 if (stat(path_p, &st) != 0)
423 return acl_from_mode(st.st_mode);
431 str[0] = (mode & S_ISUID) ? 's' : '-';
432 str[1] = (mode & S_ISGID) ? 's' : '-';
433 str[2] = (mode & S_ISVTX) ? 't' : '-';
438 int do_print(const char *path_p, const struct stat *st, int walk_flags, void *unused)
440 const char *default_prefix = NULL;
441 acl_t acl = NULL, default_acl = NULL;
444 if (walk_flags & WALK_TREE_FAILED) {
445 fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"),
451 * Symlinks can never have ACLs, so when doing a physical walk, we
452 * skip symlinks altogether, and when doing a half-logical walk, we
453 * skip all non-toplevel symlinks.
455 if ((walk_flags & WALK_TREE_SYMLINK) &&
456 ((walk_flags & WALK_TREE_PHYSICAL) ||
457 !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL))))
461 acl = acl_get_file(path_p, ACL_TYPE_ACCESS);
462 if (acl == NULL && (errno == ENOSYS || errno == ENOTSUP))
463 acl = acl_get_file_mode(path_p);
468 if (opt_print_default_acl && S_ISDIR(st->st_mode)) {
469 default_acl = acl_get_file(path_p, ACL_TYPE_DEFAULT);
470 if (default_acl == NULL) {
471 if (errno != ENOSYS && errno != ENOTSUP)
473 } else if (acl_entries(default_acl) == 0) {
474 acl_free(default_acl);
480 (!acl || acl_equiv_mode(acl, NULL) == 0) && !default_acl)
483 if (opt_print_acl && opt_print_default_acl)
484 default_prefix = "default:";
486 if (opt_strip_leading_slash) {
487 if (*path_p == '/') {
488 if (!absolute_warning) {
489 fprintf(stderr, _("%s: Removing leading "
490 "'/' from absolute path names\n"),
492 absolute_warning = 1;
494 while (*path_p == '/')
496 } else if (*path_p == '.' && *(path_p+1) == '/')
497 while (*++path_p == '/')
504 if (do_show(stdout, path_p, st, acl, default_acl) != 0)
508 printf("# file: %s\n", xquote(path_p, "\n\r"));
509 printf("# owner: %s\n",
510 xquote(user_name(st->st_uid, opt_numeric), " \t\n\r"));
511 printf("# group: %s\n",
512 xquote(group_name(st->st_gid, opt_numeric), " \t\n\r"));
513 if ((st->st_mode & (S_ISVTX | S_ISUID | S_ISGID)) && !posixly_correct)
514 printf("# flags: %s\n", flagstr(st->st_mode));
517 char *acl_text = acl_to_any_text(acl, NULL, '\n',
521 if (puts(acl_text) < 0) {
527 if (default_acl != NULL) {
528 char *acl_text = acl_to_any_text(default_acl,
529 default_prefix, '\n',
533 if (puts(acl_text) < 0) {
540 if (acl || default_acl || opt_comments)
547 acl_free(default_acl);
551 fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"),
560 printf(_("%s %s -- get file access control lists\n"),
562 printf(_("Usage: %s [-%s] file ...\n"),
563 progname, cmd_line_options);
565 if (posixly_correct) {
568 " -d, --default display the default access control list\n"));
572 " -a, --access display the file access control list only\n"
573 " -d, --default display the default access control list only\n"
574 " -c, --omit-header do not display the comment header\n"
575 " -e, --all-effective print all effective rights\n"
576 " -E, --no-effective print no effective rights\n"
577 " -s, --skip-base skip files that only have the base entries\n"
578 " -R, --recursive recurse into subdirectories\n"
579 " -L, --logical logical walk, follow symbolic links\n"
580 " -P, --physical physical walk, do not follow symbolic links\n"
581 " -t, --tabular use tabular output format\n"
582 " -n, --numeric print numeric user/group identifiers\n"
583 " -p, --absolute-names don't strip leading '/' in pathnames\n"));
587 " -v, --version print version and exit\n"
588 " -h, --help this help text\n"));
591 int main(int argc, char *argv[])
596 progname = basename(argv[0]);
599 cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
601 if (getenv(POSIXLY_CORRECT_STR))
603 if (!posixly_correct)
604 cmd_line_options = CMD_LINE_OPTIONS;
606 cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
609 setlocale(LC_CTYPE, "");
610 setlocale(LC_MESSAGES, "");
611 bindtextdomain(PACKAGE, LOCALEDIR);
614 /* Align `#effective:' comments to column 40 for tty's */
615 if (!posixly_correct && isatty(fileno(stdout)))
616 print_options |= TEXT_SMART_INDENT;
618 while ((opt = getopt_long(argc, argv, cmd_line_options,
619 long_options, NULL)) != -1) {
621 case 'a': /* acl only */
627 case 'd': /* default acl only */
628 opt_print_default_acl = 1;
631 case 'c': /* no comments */
637 case 'e': /* all #effective comments */
640 print_options |= TEXT_ALL_EFFECTIVE;
643 case 'E': /* no #effective comments */
646 print_options &= ~(TEXT_SOME_EFFECTIVE |
650 case 'R': /* recursive */
653 walk_flags |= WALK_TREE_RECURSIVE;
656 case 'L': /* follow all symlinks */
659 walk_flags |= WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE;
660 walk_flags &= ~WALK_TREE_PHYSICAL;
663 case 'P': /* skip all symlinks */
666 walk_flags |= WALK_TREE_PHYSICAL;
667 walk_flags &= ~(WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE |
668 WALK_TREE_DEREFERENCE_TOPLEVEL);
671 case 's': /* skip files with only base entries */
680 opt_strip_leading_slash = 0;
689 case 'n': /* numeric */
691 print_options |= TEXT_NUMERIC_IDS;
694 case 'v': /* print version */
695 printf("%s " VERSION "\n", progname);
702 case ':': /* option missing */
703 case '?': /* unknown option */
709 if (!(opt_print_acl || opt_print_default_acl)) {
711 if (!posixly_correct)
712 opt_print_default_acl = 1;
715 if ((optind == argc) && !posixly_correct)
719 if (optind == argc ||
720 strcmp(argv[optind], "-") == 0) {
721 while ((line = next_line(stdin)) != NULL) {
725 had_errors += walk_tree(line, walk_flags, 0,
729 fprintf(stderr, _("%s: Standard input: %s\n"),
730 progname, strerror(errno));
734 had_errors += walk_tree(argv[optind], walk_flags, 0,
737 } while (optind < argc);
739 return had_errors ? 1 : 0;
742 fprintf(stderr, _("Usage: %s [-%s] file ...\n"),
743 progname, cmd_line_options);
744 fprintf(stderr, _("Try `%s --help' for more information.\n"),