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_TOPLEVEL;
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;
131 int chmod_required = 0;
133 memset(&st, 0, sizeof(st));
137 error = read_acl_comments(file, &line, &path_p, &uid, &gid,
146 if (path_p == NULL) {
148 fprintf(stderr, _("%s: %s: No filename found "
149 "in line %d, aborting\n"),
150 progname, xquote(filename, "\n\r"),
153 fprintf(stderr, _("%s: No filename found in "
154 "line %d of standard input, "
156 progname, backup_line);
162 if (!(args.seq = seq_init()))
164 if (seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_ACCESS) ||
165 seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_DEFAULT))
168 error = read_acl_seq(file, args.seq, CMD_ENTRY_REPLACE,
169 SEQ_PARSE_WITH_PERM |
174 fprintf(stderr, _("%s: %s: %s in line %d\n"),
175 progname, xquote(filename, "\n\r"), strerror(errno),
181 error = stat(path_p, &st);
182 if (opt_test && error != 0) {
183 fprintf(stderr, "%s: %s: %s\n", progname,
184 xquote(path_p, "\n\r"), strerror(errno));
189 error = do_set(path_p, &st, 0, &args);
195 if (uid != ACL_UNDEFINED_ID && uid != st.st_uid)
199 if (gid != ACL_UNDEFINED_ID && gid != st.st_gid)
204 (st.st_uid != -1 || st.st_gid != -1)) {
205 if (chown(path_p, st.st_uid, st.st_gid) != 0) {
206 fprintf(stderr, _("%s: %s: Cannot change "
207 "owner/group: %s\n"),
208 progname, xquote(path_p, "\n\r"),
213 /* chown() clears setuid/setgid so force a chmod if
214 * S_ISUID/S_ISGID was expected */
215 if ((st.st_mode & flags) & (S_ISUID | S_ISGID))
219 mask = S_ISUID | S_ISGID | S_ISVTX;
220 if (chmod_required || ((st.st_mode & mask) != (flags & mask))) {
222 args.mode = st.st_mode;
223 args.mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
224 if (chmod(path_p, flags | args.mode) != 0) {
225 fprintf(stderr, _("%s: %s: Cannot change "
227 progname, xquote(path_p, "\n\r"),
257 fprintf(stderr, "%s: %s: %s\n", progname, xquote(filename, "\n\r"),
267 printf(_("%s %s -- set file access control lists\n"),
269 printf(_("Usage: %s %s\n"),
270 progname, cmd_line_spec);
272 " -m, --modify=acl modify the current ACL(s) of file(s)\n"
273 " -M, --modify-file=file read ACL entries to modify from file\n"
274 " -x, --remove=acl remove entries from the ACL(s) of file(s)\n"
275 " -X, --remove-file=file read ACL entries to remove from file\n"
276 " -b, --remove-all remove all extended ACL entries\n"
277 " -k, --remove-default remove the default ACL\n"));
279 if (!posixly_correct) {
281 " --set=acl set the ACL of file(s), replacing the current ACL\n"
282 " --set-file=file read ACL entries to set from file\n"
283 " --mask do recalculate the effective rights mask\n"));
287 " -n, --no-mask don't recalculate the effective rights mask\n"
288 " -d, --default operations apply to the default ACL\n"));
290 if (!posixly_correct) {
292 " -R, --recursive recurse into subdirectories\n"
293 " -L, --logical logical walk, follow symbolic links\n"
294 " -P, --physical physical walk, do not follow symbolic links\n"
295 " --restore=file restore ACLs (inverse of `getfacl -R')\n"
296 " --test test mode (ACLs are not modified)\n"));
300 " -v, --version print version and exit\n"
301 " -h, --help this help text\n"));
305 int next_file(const char *arg, seq_t seq)
309 struct do_set_args args;
313 if (strcmp(arg, "-") == 0) {
314 while ((line = next_line(stdin)))
315 errors = walk_tree(line, walk_flags, 0, do_set, &args);
317 fprintf(stderr, _("%s: Standard input: %s\n"),
318 progname, strerror(errno));
322 errors = walk_tree(arg, walk_flags, 0, do_set, &args);
324 return errors ? 1 : 0;
328 #define ERRNO_ERROR(s) \
329 ({status = (s); goto errno_error; })
332 int main(int argc, char *argv[])
342 int seq_cmd, parse_mode;
344 progname = basename(argv[0]);
347 cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
348 cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
350 if (getenv(POSIXLY_CORRECT_STR))
352 if (!posixly_correct) {
353 cmd_line_options = CMD_LINE_OPTIONS;
354 cmd_line_spec = _(CMD_LINE_SPEC);
356 cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
357 cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
361 setlocale(LC_CTYPE, "");
362 setlocale(LC_MESSAGES, "");
363 bindtextdomain(PACKAGE, LOCALEDIR);
370 while ((opt = getopt_long(argc, argv, cmd_line_options,
371 long_options, NULL)) != -1) {
372 /* we remember the two REMOVE_ACL commands of the set
373 operations because we may later need to delete them. */
374 cmd_t seq_remove_default_acl_cmd = NULL;
375 cmd_t seq_remove_acl_cmd = NULL;
377 if (opt != '\1' && saw_files) {
386 case 'b': /* remove all extended entries */
387 if (seq_append_cmd(seq, CMD_REMOVE_EXTENDED_ACL,
389 seq_append_cmd(seq, CMD_REMOVE_ACL,
394 case 'k': /* remove default ACL */
395 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
400 case 'n': /* do not recalculate mask */
401 opt_recalculate = -1;
404 case 'r': /* force recalculate mask */
408 case 'd': /* operations apply to default ACL */
413 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
416 seq_remove_acl_cmd = seq->s_last;
417 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
420 seq_remove_default_acl_cmd = seq->s_last;
422 seq_cmd = CMD_ENTRY_REPLACE;
423 parse_mode = SEQ_PARSE_WITH_PERM;
424 goto set_modify_delete;
426 case 'm': /* modify */
427 seq_cmd = CMD_ENTRY_REPLACE;
428 parse_mode = SEQ_PARSE_WITH_PERM;
429 goto set_modify_delete;
431 case 'x': /* delete */
432 seq_cmd = CMD_REMOVE_ENTRY;
434 parse_mode = SEQ_PARSE_ANY_PERM;
437 parse_mode = SEQ_PARSE_ANY_PERM;
439 parse_mode = SEQ_PARSE_NO_PERM;
441 goto set_modify_delete;
444 if (!posixly_correct)
445 parse_mode |= SEQ_PARSE_DEFAULT;
447 parse_mode |= SEQ_PROMOTE_ACL;
448 if (parse_acl_seq(seq, optarg, &which,
449 seq_cmd, parse_mode) != 0) {
451 (size_t) which >= strlen(optarg)) {
470 case 'S': /* set from file */
471 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
474 seq_remove_acl_cmd = seq->s_last;
475 if (seq_append_cmd(seq, CMD_REMOVE_ACL,
478 seq_remove_default_acl_cmd = seq->s_last;
480 seq_cmd = CMD_ENTRY_REPLACE;
481 parse_mode = SEQ_PARSE_WITH_PERM;
482 goto set_modify_delete_from_file;
484 case 'M': /* modify from file */
485 seq_cmd = CMD_ENTRY_REPLACE;
486 parse_mode = SEQ_PARSE_WITH_PERM;
487 goto set_modify_delete_from_file;
489 case 'X': /* delete from file */
490 seq_cmd = CMD_REMOVE_ENTRY;
492 parse_mode = SEQ_PARSE_ANY_PERM;
495 parse_mode = SEQ_PARSE_ANY_PERM;
497 parse_mode = SEQ_PARSE_NO_PERM;
499 goto set_modify_delete_from_file;
501 set_modify_delete_from_file:
502 if (!posixly_correct)
503 parse_mode |= SEQ_PARSE_DEFAULT;
505 parse_mode |= SEQ_PROMOTE_ACL;
506 if (strcmp(optarg, "-") == 0) {
509 file = fopen(optarg, "r");
511 fprintf(stderr, "%s: %s: %s\n",
513 xquote(optarg, "\n\r"),
521 error = read_acl_seq(file, seq, seq_cmd,
522 parse_mode, &lineno, NULL);
539 xquote(optarg, "\n\r"));
544 "input\n"), progname,
554 case '\1': /* file argument */
559 status = next_file(optarg, seq);
562 case 'B': /* restore ACL backup */
565 if (strcmp(optarg, "-") == 0)
568 file = fopen(optarg, "r");
570 fprintf(stderr, "%s: %s: %s\n",
572 xquote(optarg, "\n\r"),
579 status = restore(file,
580 (file == stdin) ? NULL : optarg);
588 case 'R': /* recursive */
589 walk_flags |= WALK_TREE_RECURSIVE;
592 case 'L': /* follow symlinks */
593 walk_flags |= WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE;
594 walk_flags &= ~WALK_TREE_PHYSICAL;
597 case 'P': /* do not follow symlinks */
598 walk_flags |= WALK_TREE_PHYSICAL;
599 walk_flags &= ~(WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE |
600 WALK_TREE_DEREFERENCE_TOPLEVEL);
603 case 't': /* test mode */
607 case 'v': /* print version and exit */
608 printf("%s " VERSION "\n", progname);
612 case 'h': /* help! */
617 case ':': /* option missing */
618 case '?': /* unknown option */
622 if (seq_remove_acl_cmd) {
623 /* This was a set operation. Check if there are
624 actually entries of ACL_TYPE_ACCESS; if there
625 are none, we need to remove this command! */
626 if (!has_any_of_type(seq_remove_acl_cmd->c_next,
628 seq_delete_cmd(seq, seq_remove_acl_cmd);
630 if (seq_remove_default_acl_cmd) {
631 /* This was a set operation. Check if there are
632 actually entries of ACL_TYPE_DEFAULT; if there
633 are none, we need to remove this command! */
634 if (!has_any_of_type(seq_remove_default_acl_cmd->c_next,
636 seq_delete_cmd(seq, seq_remove_default_acl_cmd);
639 while (optind < argc) {
646 status = next_file(argv[optind++], seq);
654 fprintf(stderr, _("Usage: %s %s\n"),
655 progname, cmd_line_spec);
656 fprintf(stderr, _("Try `%s --help' for more information.\n"),
662 fprintf(stderr, "%s: %s\n", progname, strerror(errno));