Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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;
}
Пример #4
0
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;
}
Пример #5
0
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;
}
Пример #6
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;
}
Пример #7
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;
}