setfacl.c revision 167000
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
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/bin/setfacl/setfacl.c 167000 2007-02-26 00:42:17Z mckusick $");
29
30#include <sys/types.h>
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <sys/acl.h>
34#include <sys/queue.h>
35
36#include <err.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include "setfacl.h"
43
44static void   add_filename(const char *filename);
45static acl_t *get_file_acls(const char *filename);
46static void   usage(void);
47
48static void
49add_filename(const char *filename)
50{
51	struct sf_file *file;
52
53	if (strlen(filename) > PATH_MAX - 1) {
54		warn("illegal filename");
55		return;
56	}
57	file = zmalloc(sizeof(struct sf_file));
58	file->filename = filename;
59	TAILQ_INSERT_TAIL(&filelist, file, next);
60}
61
62static acl_t *
63get_file_acls(const char *filename)
64{
65	acl_t *acl;
66	struct stat sb;
67
68	if (stat(filename, &sb) == -1) {
69		warn("stat() of %s failed", filename);
70		return (NULL);
71	}
72
73	acl = zmalloc(sizeof(acl_t) * 2);
74	if (h_flag)
75		acl[ACCESS_ACL] = acl_get_link_np(filename, ACL_TYPE_ACCESS);
76	else
77		acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS);
78	if (acl[ACCESS_ACL] == NULL)
79		err(1, "acl_get_file() failed");
80	if (S_ISDIR(sb.st_mode)) {
81		if (h_flag)
82			acl[DEFAULT_ACL] = acl_get_link_np(filename,
83			    ACL_TYPE_DEFAULT);
84		else
85			acl[DEFAULT_ACL] = acl_get_file(filename,
86			    ACL_TYPE_DEFAULT);
87		if (acl[DEFAULT_ACL] == NULL)
88			err(1, "acl_get_file() failed");
89	} else
90		acl[DEFAULT_ACL] = NULL;
91
92	return (acl);
93}
94
95static void
96usage(void)
97{
98
99	fprintf(stderr, "usage: setfacl [-bdhkn] [-m entries] [-M file] "
100	    "[-x entries] [-X file] [file ...]\n");
101	exit(1);
102}
103
104int
105main(int argc, char *argv[])
106{
107	acl_t *acl, final_acl;
108	char filename[PATH_MAX];
109	int local_error, carried_error, ch, i;
110	struct sf_file *file;
111	struct sf_entry *entry;
112	const char *fn_dup;
113
114	acl_type = ACL_TYPE_ACCESS;
115	carried_error = local_error = 0;
116	h_flag = have_mask = have_stdin = n_flag = need_mask = 0;
117
118	TAILQ_INIT(&entrylist);
119	TAILQ_INIT(&filelist);
120
121	while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1)
122		switch(ch) {
123		case 'M':
124			entry = zmalloc(sizeof(struct sf_entry));
125			entry->acl = get_acl_from_file(optarg);
126			if (entry->acl == NULL)
127				err(1, "get_acl_from_file() failed");
128			entry->op = OP_MERGE_ACL;
129			TAILQ_INSERT_TAIL(&entrylist, entry, next);
130			break;
131		case 'X':
132			entry = zmalloc(sizeof(struct sf_entry));
133			entry->acl = get_acl_from_file(optarg);
134			entry->op = OP_REMOVE_ACL;
135			TAILQ_INSERT_TAIL(&entrylist, entry, next);
136			break;
137		case 'b':
138			entry = zmalloc(sizeof(struct sf_entry));
139			entry->op = OP_REMOVE_EXT;
140			TAILQ_INSERT_TAIL(&entrylist, entry, next);
141			break;
142		case 'd':
143			acl_type = ACL_TYPE_DEFAULT;
144			break;
145		case 'h':
146			h_flag = 1;
147			break;
148		case 'k':
149			entry = zmalloc(sizeof(struct sf_entry));
150			entry->op = OP_REMOVE_DEF;
151			TAILQ_INSERT_TAIL(&entrylist, entry, next);
152			break;
153		case 'm':
154			entry = zmalloc(sizeof(struct sf_entry));
155			entry->acl = acl_from_text(optarg);
156			if (entry->acl == NULL)
157				err(1, "%s", optarg);
158			entry->op = OP_MERGE_ACL;
159			TAILQ_INSERT_TAIL(&entrylist, entry, next);
160			break;
161		case 'n':
162			n_flag++;
163			break;
164		case 'x':
165			entry = zmalloc(sizeof(struct sf_entry));
166			entry->acl = acl_from_text(optarg);
167			if (entry->acl == NULL)
168				err(1, "%s", optarg);
169			entry->op = OP_REMOVE_ACL;
170			TAILQ_INSERT_TAIL(&entrylist, entry, next);
171			break;
172		default:
173			usage();
174			break;
175		}
176	argc -= optind;
177	argv += optind;
178
179	if (n_flag == 0 && TAILQ_EMPTY(&entrylist))
180		usage();
181
182	/* take list of files from stdin */
183	if (argc == 0 || strcmp(argv[0], "-") == 0) {
184		if (have_stdin)
185			err(1, "cannot have more than one stdin");
186		have_stdin = 1;
187		bzero(&filename, sizeof(filename));
188		while (fgets(filename, (int)sizeof(filename), stdin)) {
189			/* remove the \n */
190			filename[strlen(filename) - 1] = '\0';
191			fn_dup = strdup(filename);
192			if (fn_dup == NULL)
193				err(1, "strdup() failed");
194			add_filename(fn_dup);
195		}
196	} else
197		for (i = 0; i < argc; i++)
198			add_filename(argv[i]);
199
200	/* cycle through each file */
201	TAILQ_FOREACH(file, &filelist, next) {
202		/* get our initial access and default ACL's */
203		acl = get_file_acls(file->filename);
204		if (acl == NULL)
205			continue;
206		if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) {
207			warnx("Default ACL not valid for %s", file->filename);
208			continue;
209		}
210
211		local_error = 0;
212
213		/* cycle through each option */
214		TAILQ_FOREACH(entry, &entrylist, next) {
215			if (local_error)
216				continue;
217
218			switch(entry->op) {
219			case OP_MERGE_ACL:
220				local_error += merge_acl(entry->acl, acl);
221				need_mask = 1;
222				break;
223			case OP_REMOVE_EXT:
224				remove_ext(acl);
225				need_mask = 0;
226				break;
227			case OP_REMOVE_DEF:
228				if (acl_delete_def_file(file->filename) == -1) {
229					warn("acl_delete_def_file() failed");
230					local_error++;
231				}
232				local_error += remove_default(acl);
233				need_mask = 0;
234				break;
235			case OP_REMOVE_ACL:
236				local_error += remove_acl(entry->acl, acl);
237				need_mask = 1;
238				break;
239			}
240		}
241
242		/* don't bother setting the ACL if something is broken */
243		if (local_error) {
244			carried_error++;
245			continue;
246		}
247
248		if (acl_type == ACL_TYPE_ACCESS)
249			final_acl = acl[ACCESS_ACL];
250		else
251			final_acl = acl[DEFAULT_ACL];
252
253		if (need_mask && (set_acl_mask(&final_acl) == -1)) {
254			warnx("failed to set ACL mask on %s", file->filename);
255			carried_error++;
256		} else if (h_flag) {
257			if (acl_set_link_np(file->filename, acl_type,
258			    final_acl) == -1) {
259				carried_error++;
260				warn("acl_set_link_np() failed for %s",
261				    file->filename);
262			}
263		} else {
264			if (acl_set_file(file->filename, acl_type,
265			    final_acl) == -1) {
266				carried_error++;
267				warn("acl_set_file() failed for %s",
268				    file->filename);
269			}
270		}
271
272		acl_free(acl[ACCESS_ACL]);
273		acl_free(acl[DEFAULT_ACL]);
274		free(acl);
275	}
276
277	return (carried_error);
278}
279