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 walk_tree(const char *file, seq_t seq) { __errors = 0; __seq = seq; if (nftw(file, __do_set, 0, opt_walk_physical * FTW_PHYS) < 0) { fprintf(stderr, "%s: %s: %s\n", progname, xquote(file), strerror(errno)); __errors++; } return __errors; }
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 = NULL; 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); 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) { if (seq) { seq_free(seq); seq = NULL; } saw_files = 0; } if (seq == NULL) { if (!(seq = seq_init())) ERRNO_ERROR(1); } 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 | SEQ_PARSE_NO_RELATIVE; goto set_modify_delete; case 'm': /* modify */ seq_cmd = CMD_ENTRY_REPLACE; parse_mode = SEQ_PARSE_WITH_PERM; #if POSIXLY_CORRECT || 1 parse_mode |= SEQ_PARSE_NO_RELATIVE; #else if (posixly_correct) parse_mode |= SEQ_PARSE_NO_RELATIVE; else parse_mode |= SEQ_PARSE_ANY_RELATIVE; #endif goto set_modify_delete; case 'x': /* delete */ seq_cmd = CMD_REMOVE_ENTRY; parse_mode = SEQ_PARSE_NO_RELATIVE; #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 | SEQ_PARSE_NO_RELATIVE; goto set_modify_delete_from_file; case 'M': /* modify from file */ seq_cmd = CMD_ENTRY_REPLACE; parse_mode = SEQ_PARSE_WITH_PERM; #if POSIXLY_CORRECT || 1 parse_mode |= SEQ_PARSE_NO_RELATIVE; #else if (posixly_correct) parse_mode |= SEQ_PARSE_NO_RELATIVE; else parse_mode |= SEQ_PARSE_ANY_RELATIVE; #endif goto set_modify_delete_from_file; case 'X': /* delete from file */ seq_cmd = CMD_REMOVE_ENTRY; parse_mode = SEQ_PARSE_NO_RELATIVE; #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), 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)); } 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), 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 */ opt_recursive = 1; break; case 'L': /* follow symlinks */ opt_walk_logical = 1; opt_walk_physical = 0; break; case 'P': /* do not follow symlinks */ opt_walk_logical = 0; opt_walk_physical = 1; 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_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; }
int restore( FILE *file, const char *filename) { char *path_p; struct stat stat; uid_t uid; gid_t gid; seq_t seq = NULL; int line = 0, backup_line; int error, status = 0; memset(&stat, 0, sizeof(stat)); for(;;) { backup_line = line; error = read_acl_comments(file, &line, &path_p, &uid, &gid); if (error < 0) goto fail; if (error == 0) return 0; if (path_p == NULL) { if (filename) { fprintf(stderr, _("%s: %s: No filename found " "in line %d, aborting\n"), progname, xquote(filename), backup_line); } else { fprintf(stderr, _("%s: No filename found in " "line %d of standard input, " "aborting\n"), progname, backup_line); } goto getout; } if (!(seq = seq_init())) goto fail; if (seq_append_cmd(seq, CMD_REMOVE_ACL, ACL_TYPE_ACCESS) || seq_append_cmd(seq, CMD_REMOVE_ACL, ACL_TYPE_DEFAULT)) goto fail; error = read_acl_seq(file, seq, CMD_ENTRY_REPLACE, SEQ_PARSE_WITH_PERM | SEQ_PARSE_NO_RELATIVE | SEQ_PARSE_DEFAULT | SEQ_PARSE_MULTI, &line, NULL); if (error != 0) { fprintf(stderr, _("%s: %s: %s in line %d\n"), progname, xquote(filename), strerror(errno), line); goto getout; } error = lstat(path_p, &stat); if (opt_test && error != 0) { fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p), strerror(errno)); status = 1; } stat.st_uid = uid; stat.st_gid = gid; error = do_set(path_p, &stat, seq); if (error != 0) { status = 1; goto resume; } if (!opt_test && (uid != ACL_UNDEFINED_ID || gid != ACL_UNDEFINED_ID)) { if (chown(path_p, uid, gid) != 0) { fprintf(stderr, _("%s: %s: Cannot change " "owner/group: %s\n"), progname, xquote(path_p), strerror(errno)); status = 1; } } resume: if (path_p) { free(path_p); path_p = NULL; } if (seq) { seq_free(seq); seq = NULL; } } getout: if (path_p) { free(path_p); path_p = NULL; } if (seq) { seq_free(seq); seq = NULL; } return status; fail: fprintf(stderr, "%s: %s: %s\n", progname, xquote(filename), strerror(errno)); status = 1; goto getout; }
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; }
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; }
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; }