setfacl.c revision 87259
1/*
2 * Copyright (c) 2001 Chris D. Faulhaber
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: head/bin/setfacl/setfacl.c 87259 2001-12-03 01:20:52Z jedgar $
27 */
28
29#include <sys/types.h>
30#include <sys/param.h>
31#include <sys/stat.h>
32#include <sys/acl.h>
33#include <sys/queue.h>
34
35#include <err.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include "setfacl.h"
42
43static void   add_filename(const char *filename);
44static acl_t *get_file_acls(const char *filename);
45static void   usage(void);
46
47static void
48add_filename(const char *filename)
49{
50	struct sf_file *file;
51
52	if (strlen(filename) > PATH_MAX - 1) {
53		warn("illegal filename");
54		return;
55	}
56	file = zmalloc(sizeof(struct sf_file));
57	file->filename = filename;
58	TAILQ_INSERT_TAIL(&filelist, file, next);
59}
60
61static acl_t *
62get_file_acls(const char *filename)
63{
64	acl_t *acl;
65	struct stat sb;
66
67	if (stat(filename, &sb) == -1) {
68		warn("stat() of %s failed", filename);
69		return (NULL);
70	}
71
72	acl = zmalloc(sizeof(acl_t) * 2);
73	acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS);
74	if (acl[ACCESS_ACL] == NULL)
75		err(1, "acl_get_file() failed");
76	if (S_ISDIR(sb.st_mode)) {
77		acl[DEFAULT_ACL] = acl_get_file(filename, ACL_TYPE_DEFAULT);
78		if (acl[DEFAULT_ACL] == NULL)
79			err(1, "acl_get_file() failed");
80	} else
81		acl[DEFAULT_ACL] = NULL;
82
83	return (acl);
84}
85
86static void
87usage(void)
88{
89
90	fprintf(stderr, "usage: setfacl [-bdknv] [-m entries] [-M file1] "
91	    "[-x entries] [-X file2] [file ...]\n");
92	exit(1);
93}
94
95int
96main(int argc, char *argv[])
97{
98	acl_t *acl, final_acl;
99	char filename[PATH_MAX];
100	int local_error, carried_error, ch, i;
101	struct sf_file *file;
102	struct sf_entry *entry;
103
104	acl_type = ACL_TYPE_ACCESS;
105	carried_error = local_error = 0;
106	have_mask = have_stdin = n_flag = need_mask = 0;
107
108	TAILQ_INIT(&entrylist);
109	TAILQ_INIT(&filelist);
110
111	while ((ch = getopt(argc, argv, "M:X:bdkm:nx:")) != -1)
112		switch(ch) {
113		case 'M':
114			entry = zmalloc(sizeof(struct sf_entry));
115			entry->acl = get_acl_from_file(optarg);
116			if (entry->acl == NULL)
117				err(1, "get_acl_from_file() failed");
118			entry->op = OP_MERGE_ACL;
119			TAILQ_INSERT_TAIL(&entrylist, entry, next);
120			break;
121		case 'X':
122			entry = zmalloc(sizeof(struct sf_entry));
123			entry->acl = get_acl_from_file(optarg);
124			entry->op = OP_REMOVE_ACL;
125			TAILQ_INSERT_TAIL(&entrylist, entry, next);
126			break;
127		case 'b':
128			entry = zmalloc(sizeof(struct sf_entry));
129			entry->op = OP_REMOVE_EXT;
130			TAILQ_INSERT_TAIL(&entrylist, entry, next);
131			break;
132		case 'd':
133			acl_type = ACL_TYPE_DEFAULT;
134			break;
135		case 'k':
136			entry = zmalloc(sizeof(struct sf_entry));
137			entry->op = OP_REMOVE_DEF;
138			TAILQ_INSERT_TAIL(&entrylist, entry, next);
139			break;
140		case 'm':
141			entry = zmalloc(sizeof(struct sf_entry));
142			entry->acl = acl_from_text(optarg);
143			if (entry->acl == NULL)
144				err(1, "acl_from_text() failed");
145			entry->op = OP_MERGE_ACL;
146			TAILQ_INSERT_TAIL(&entrylist, entry, next);
147			break;
148		case 'n':
149			n_flag++;
150			break;
151		case 'x':
152			entry = zmalloc(sizeof(struct sf_entry));
153			entry->acl = acl_from_text(optarg);
154			if (entry->acl == NULL)
155				err(1, "acl_from_text() failed");
156			entry->op = OP_REMOVE_ACL;
157			TAILQ_INSERT_TAIL(&entrylist, entry, next);
158			break;
159		default:
160			usage();
161			break;
162		}
163	argc -= optind;
164	argv += optind;
165
166	if (n_flag == 0 && TAILQ_EMPTY(&entrylist))
167		usage();
168
169	/* take list of files from stdin */
170	if (argc == 0 || strcmp(argv[0], "-") == 0) {
171		if (have_stdin)
172			err(1, "cannot have more than one stdin");
173		have_stdin = 1;
174		bzero(&filename, sizeof(filename));
175		while (fgets(filename, (int)sizeof(filename), stdin)) {
176			/* remove the \n */
177			filename[strlen(filename) - 1] = '\0';
178			add_filename(filename);
179		}
180	} else
181		for (i = 0; i < argc; i++)
182			add_filename(argv[i]);
183
184	/* cycle through each file */
185	TAILQ_FOREACH(file, &filelist, next) {
186		/* get our initial access and default ACL's */
187		acl = get_file_acls(file->filename);
188		if (acl == NULL)
189			continue;
190		if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) {
191			warnx("Default ACL not valid for %s", file->filename);
192			continue;
193		}
194
195		local_error = 0;
196
197		/* cycle through each option */
198		TAILQ_FOREACH(entry, &entrylist, next) {
199			if (local_error)
200				continue;
201
202			switch(entry->op) {
203			case OP_MERGE_ACL:
204				local_error += merge_acl(entry->acl, acl);
205				need_mask = 1;
206				break;
207			case OP_REMOVE_EXT:
208				remove_ext(acl);
209				need_mask = 0;
210				break;
211			case OP_REMOVE_DEF:
212				if (acl_delete_def_file(file->filename) == -1) {
213					warn("acl_delete_def_file() failed");
214					local_error++;
215				}
216				local_error += remove_default(acl);
217				need_mask = 0;
218				break;
219			case OP_REMOVE_ACL:
220				local_error += remove_acl(entry->acl, acl);
221				need_mask = 1;
222				break;
223			}
224		}
225
226		/* don't bother setting the ACL if something is broken */
227		if (local_error) {
228			carried_error++;
229			continue;
230		}
231
232		if (acl_type == ACL_TYPE_ACCESS)
233			final_acl = acl[ACCESS_ACL];
234		else
235			final_acl = acl[DEFAULT_ACL];
236
237		if (need_mask && (set_acl_mask(&final_acl) == -1)) {
238			warnx("failed to set ACL mask on %s", file->filename);
239			carried_error++;
240		} else if (acl_set_file(file->filename, acl_type,
241		    final_acl) == -1) {
242			carried_error++;
243			warn("acl_set_file() failed for %s", file->filename);
244		}
245
246		acl_free(acl[ACCESS_ACL]);
247		acl_free(acl[DEFAULT_ACL]);
248		free(acl);
249	}
250
251	return (carried_error);
252}
253