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
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #include "walk_tree.h"
40 #define POSIXLY_CORRECT_STR "POSIXLY_CORRECT"
42 /* '-' stands for `process non-option arguments in loop' */
44 # define CMD_LINE_OPTIONS "-:bkndvhm:M:x:X:RLP"
45 # define CMD_LINE_SPEC "[-bkndRLP] { -m|-M|-x|-X ... } file ..."
47 #define POSIXLY_CMD_LINE_OPTIONS "-:bkndvhm:M:x:X:"
48 #define POSIXLY_CMD_LINE_SPEC "[-bknd] {-m|-M|-x|-X ... } file ..."
50 struct option long_options[] = {
53 { "set-file", 1, 0, 'S' },
55 { "mask", 0, 0, 'r' },
56 { "recursive", 0, 0, 'R' },
57 { "logical", 0, 0, 'L' },
58 { "physical", 0, 0, 'P' },
59 { "restore", 1, 0, 'B' },
60 { "test", 0, 0, 't' },
62 { "modify", 1, 0, 'm' },
63 { "modify-file", 1, 0, 'M' },
64 { "remove", 1, 0, 'x' },
65 { "remove-file", 1, 0, 'X' },
67 { "default", 0, 0, 'd' },
68 { "no-mask", 0, 0, 'n' },
69 { "remove-all", 0, 0, 'b' },
70 { "remove-default", 0, 0, 'k' },
71 { "version", 0, 0, 'v' },
72 { "help", 0, 0, 'h' },
77 const char *cmd_line_options, *cmd_line_spec;
79 int walk_flags = WALK_TREE_DEREFERENCE;
80 int opt_recalculate; /* recalculate mask entry (0=default, 1=yes, -1=no) */
81 int opt_promote; /* promote access ACL to default ACL */
82 int opt_test; /* do not write to the file system.
83 Print what would happen instead. */
85 const int posixly_correct = 1; /* Posix compatible behavior! */
87 int posixly_correct; /* Posix compatible behavior? */
93 static const char *xquote(const char *str, const char *quote_chars)
95 const char *q = quote(str, quote_chars);
97 fprintf(stderr, "%s: %s\n", progname, strerror(errno));
109 if (cmd->c_type == acl_type)
121 const char *filename)
128 struct do_set_args args;
129 int line = 0, backup_line;
130 int error, status = 0;
132 memset(&st, 0, sizeof(st));
136 error = read_acl_comments(file, &line, &path_p, &uid, &gid,
143 if (path_p == NULL) {
145 fprintf(stderr, _("%s: %s: No filename found "
146 "in line %d, aborting\n"),
147 progname, xquote(filename, "\n\r"),
150 fprintf(stderr, _("%s: No filename found in "
151 "line %d of standard input, "
153 progname, backup_line);
159 if (!(args.seq = seq_init()))
161 if (seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_ACCESS) ||
162 seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_DEFAULT))
165 error = read_acl_seq(file, args.seq, CMD_ENTRY_REPLACE,
166 SEQ_PARSE_WITH_PERM |
171 fprintf(stderr, _("%s: %s: %s in line %d\n"),
172 progname, xquote(filename, "\n\r"), strerror(errno),
178 error = stat(path_p, &st);
179 if (opt_test && error != 0) {
180 fprintf(stderr, "%s: %s: %s\n", progname,
181 xquote(path_p, "\n\r"), strerror(errno));
186 error = do_set(path_p, &st, 0, &args);
192 if (uid != ACL_UNDEFINED_ID && uid != st.st_uid)
196 if (gid != ACL_UNDEFINED_ID && gid != st.st_gid)
201 (st.st_uid != -1 || st.st_gid != -1)) {
202 if (chown(path_p, st.st_uid, st.st_gid) != 0) {
203 fprintf(stderr, _("%s: %s: Cannot change "
204 "owner/group: %s\n"),
205 progname, xquote(path_p, "\n\r"),
211 mask = S_ISUID | S_ISGID | S_ISVTX;
212 if ((st.st_mode & mask) != (flags & mask)) {
214 args.mode = st.st_mode;
215 args.mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
216 if (chmod(path_p, flags | args.mode) != 0) {
217 fprintf(stderr, _("%s: %s: Cannot change "
219 progname, xquote(path_p, "\n\r"),
247 fprintf(stderr, "%s: %s: %s\n", progname, xquote(filename, "\n\r"),
257 printf(_("%s %s -- set file access control lists\n"),
259 printf(_("Usage: %s %s\n"),
260 progname, cmd_line_spec);
262 " -m, --modify=acl modify the current ACL(s) of file(s)\n"
263 " -M, --modify-file=file read ACL entries to modify from file\n"
264 " -x, --remove=acl remove entries from the ACL(s) of file(s)\n"
265 " -X, --remove-file=file read ACL entries to remove from file\n"
266 " -b, --remove-all remove all extended ACL entries\n"
267 " -k, --remove-default remove the default ACL\n"));
269 if (!posixly_correct) {
271 " --set=acl set the ACL of file(s), replacing the current ACL\n"
272 " --set-file=file read ACL entries to set from file\n"
273 " --mask do recalculate the effective rights mask\n"));
277 " -n, --no-mask don't recalculate the effective rights mask\n"
278 " -d, --default operations apply to the default ACL\n"));
280 if (!posixly_correct) {
282 " -R, --recursive recurse into subdirectories\n"
283 " -L, --logical logical walk, follow symbolic links\n"
284 " -P, --physical physical walk, do not follow symbolic links\n"
285 " --restore=file restore ACLs (inverse of `getfacl -R')\n"
286 " --test test mode (ACLs are not modified)\n"));
290 " -v, --version print version and exit\n"
291 " -h, --help this help text\n"));
295 int next_file(const char *arg, seq_t seq)
299 struct do_set_args args;
303 if (strcmp(arg, "-") == 0) {
304 while ((line = next_line(stdin)))
305 errors = walk_tree(line, walk_flags, 0, do_set, &args);
307 fprintf(stderr, _("%s: Standard input: %s\n"),
308 progname, strerror(errno));
312 errors = walk_tree(arg, walk_flags, 0, do_set, &args);
314 return errors ? 1 : 0;
318 #define ERRNO_ERROR(s) \
319 ({status = (s); goto errno_error; })
322 int main(int argc, char *argv[])
332 int seq_cmd, parse_mode;
334 progname = basename(argv[0]);
337 cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
338 cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
340 if (getenv(POSIXLY_CORRECT_STR))
342 if (!posixly_correct) {
343 cmd_line_options = CMD_LINE_OPTIONS;
344 cmd_line_spec = _(CMD_LINE_SPEC);
346 cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
347 cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
351 setlocale(LC_CTYPE, "");
352 setlocale(LC_MESSAGES, "");
353 bindtextdomain(PACKAGE, LOCALEDIR);
360 while ((opt = getopt_long(argc, argv, cmd_line_options,
361 long_options, NULL)) != -1) {
362 /* we remember the two REMOVE_ACL commands of the set
363 operations because we may later need to delete them. */
364 cmd_t seq_remove_default_acl_cmd = NULL;
365 cmd_t seq_remove_acl_cmd = NULL;
367 if (opt != '\1' && saw_files) {
376 case 'b': /* remove all extended entries */
377 if (seq_append_cmd(seq, CMD_REMOVE_EXTENDED_ACL,
379 seq_append_cmd(seq, CMD_REMOVE_ACL,
384 case 'k': /* remove default ACL */
385 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
390 case 'n': /* do not recalculate mask */
391 opt_recalculate = -1;
394 case 'r': /* force recalculate mask */
398 case 'd': /* operations apply to default ACL */
403 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
406 seq_remove_acl_cmd = seq->s_last;
407 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
410 seq_remove_default_acl_cmd = seq->s_last;
412 seq_cmd = CMD_ENTRY_REPLACE;
413 parse_mode = SEQ_PARSE_WITH_PERM;
414 goto set_modify_delete;
416 case 'm': /* modify */
417 seq_cmd = CMD_ENTRY_REPLACE;
418 parse_mode = SEQ_PARSE_WITH_PERM;
419 goto set_modify_delete;
421 case 'x': /* delete */
422 seq_cmd = CMD_REMOVE_ENTRY;
424 parse_mode = SEQ_PARSE_ANY_PERM;
427 parse_mode = SEQ_PARSE_ANY_PERM;
429 parse_mode = SEQ_PARSE_NO_PERM;
431 goto set_modify_delete;
434 if (!posixly_correct)
435 parse_mode |= SEQ_PARSE_DEFAULT;
437 parse_mode |= SEQ_PROMOTE_ACL;
438 if (parse_acl_seq(seq, optarg, &which,
439 seq_cmd, parse_mode) != 0) {
441 (size_t) which >= strlen(optarg)) {
460 case 'S': /* set from file */
461 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
464 seq_remove_acl_cmd = seq->s_last;
465 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
468 seq_remove_default_acl_cmd = seq->s_last;
470 seq_cmd = CMD_ENTRY_REPLACE;
471 parse_mode = SEQ_PARSE_WITH_PERM;
472 goto set_modify_delete_from_file;
474 case 'M': /* modify from file */
475 seq_cmd = CMD_ENTRY_REPLACE;
476 parse_mode = SEQ_PARSE_WITH_PERM;
477 goto set_modify_delete_from_file;
479 case 'X': /* delete from file */
480 seq_cmd = CMD_REMOVE_ENTRY;
482 parse_mode = SEQ_PARSE_ANY_PERM;
485 parse_mode = SEQ_PARSE_ANY_PERM;
487 parse_mode = SEQ_PARSE_NO_PERM;
489 goto set_modify_delete_from_file;
491 set_modify_delete_from_file:
492 if (!posixly_correct)
493 parse_mode |= SEQ_PARSE_DEFAULT;
495 parse_mode |= SEQ_PROMOTE_ACL;
496 if (strcmp(optarg, "-") == 0) {
499 file = fopen(optarg, "r");
501 fprintf(stderr, "%s: %s: %s\n",
503 xquote(optarg, "\n\r"),
511 error = read_acl_seq(file, seq, seq_cmd,
512 parse_mode, &lineno, NULL);
529 xquote(optarg, "\n\r"));
534 "input\n"), progname,
544 case '\1': /* file argument */
549 status = next_file(optarg, seq);
552 case 'B': /* restore ACL backup */
555 if (strcmp(optarg, "-") == 0)
558 file = fopen(optarg, "r");
560 fprintf(stderr, "%s: %s: %s\n",
562 xquote(optarg, "\n\r"),
569 status = restore(file,
570 (file == stdin) ? NULL : optarg);
578 case 'R': /* recursive */
579 walk_flags |= WALK_TREE_RECURSIVE;
582 case 'L': /* follow symlinks */
583 walk_flags |= WALK_TREE_LOGICAL;
584 walk_flags &= ~WALK_TREE_PHYSICAL;
587 case 'P': /* do not follow symlinks */
588 walk_flags |= WALK_TREE_PHYSICAL;
589 walk_flags &= ~WALK_TREE_LOGICAL;
592 case 't': /* test mode */
596 case 'v': /* print version and exit */
597 printf("%s " VERSION "\n", progname);
601 case 'h': /* help! */
606 case ':': /* option missing */
607 case '?': /* unknown option */
611 if (seq_remove_acl_cmd) {
612 /* This was a set operation. Check if there are
613 actually entries of ACL_TYPE_ACCESS; if there
614 are none, we need to remove this command! */
615 if (!has_any_of_type(seq_remove_acl_cmd->c_next,
617 seq_delete_cmd(seq, seq_remove_acl_cmd);
619 if (seq_remove_default_acl_cmd) {
620 /* This was a set operation. Check if there are
621 actually entries of ACL_TYPE_DEFAULT; if there
622 are none, we need to remove this command! */
623 if (!has_any_of_type(seq_remove_default_acl_cmd->c_next,
625 seq_delete_cmd(seq, seq_remove_default_acl_cmd);
628 while (optind < argc) {
635 status = next_file(argv[optind++], seq);
643 fprintf(stderr, _("Usage: %s %s\n"),
644 progname, cmd_line_spec);
645 fprintf(stderr, _("Try `%s --help' for more information.\n"),
651 fprintf(stderr, "%s: %s\n", progname, strerror(errno));