setfacl.c revision 139969
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 139969 2005-01-10 08:39:26Z imp $");
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 file1] "
100	    "[-x entries] [-X file2] [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
113	acl_type = ACL_TYPE_ACCESS;
114	carried_error = local_error = 0;
115	h_flag = have_mask = have_stdin = n_flag = need_mask = 0;
116
117	TAILQ_INIT(&entrylist);
118	TAILQ_INIT(&filelist);
119
120	while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1)
121		switch(ch) {
122		case 'M':
123			entry = zmalloc(sizeof(struct sf_entry));
124			entry->acl = get_acl_from_file(optarg);
125			if (entry->acl == NULL)
126				err(1, "get_acl_from_file() failed");
127			entry->op = OP_MERGE_ACL;
128			TAILQ_INSERT_TAIL(&entrylist, entry, next);
129			break;
130		case 'X':
131			entry = zmalloc(sizeof(struct sf_entry));
132			entry->acl = get_acl_from_file(optarg);
133			entry->op = OP_REMOVE_ACL;
134			TAILQ_INSERT_TAIL(&entrylist, entry, next);
135			break;
136		case 'b':
137			entry = zmalloc(sizeof(struct sf_entry));
138			entry->op = OP_REMOVE_EXT;
139			TAILQ_INSERT_TAIL(&entrylist, entry, next);
140			break;
141		case 'd':
142			acl_type = ACL_TYPE_DEFAULT;
143			break;
144		case 'h':
145			h_flag = 1;
146			break;
147		case 'k':
148			entry = zmalloc(sizeof(struct sf_entry));
149			entry->op = OP_REMOVE_DEF;
150			TAILQ_INSERT_TAIL(&entrylist, entry, next);
151			break;
152		case 'm':
153			entry = zmalloc(sizeof(struct sf_entry));
154			entry->acl = acl_from_text(optarg);
155			if (entry->acl == NULL)
156				err(1, "%s", optarg);
157			entry->op = OP_MERGE_ACL;
158			TAILQ_INSERT_TAIL(&entrylist, entry, next);
159			break;
160		case 'n':
161			n_flag++;
162			break;
163		case 'x':
164			entry = zmalloc(sizeof(struct sf_entry));
165			entry->acl = acl_from_text(optarg);
166			if (entry->acl == NULL)
167				err(1, "%s", optarg);
168			entry->op = OP_REMOVE_ACL;
169			TAILQ_INSERT_TAIL(&entrylist, entry, next);
170			break;
171		default:
172			usage();
173			break;
174		}
175	argc -= optind;
176	argv += optind;
177
178	if (n_flag == 0 && TAILQ_EMPTY(&entrylist))
179		usage();
180
181	/* take list of files from stdin */
182	if (argc == 0 || strcmp(argv[0], "-") == 0) {
183		if (have_stdin)
184			err(1, "cannot have more than one stdin");
185		have_stdin = 1;
186		bzero(&filename, sizeof(filename));
187		while (fgets(filename, (int)sizeof(filename), stdin)) {
188			/* remove the \n */
189			filename[strlen(filename) - 1] = '\0';
190			add_filename(filename);
191		}
192	} else
193		for (i = 0; i < argc; i++)
194			add_filename(argv[i]);
195
196	/* cycle through each file */
197	TAILQ_FOREACH(file, &filelist, next) {
198		/* get our initial access and default ACL's */
199		acl = get_file_acls(file->filename);
200		if (acl == NULL)
201			continue;
202		if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) {
203			warnx("Default ACL not valid for %s", file->filename);
204			continue;
205		}
206
207		local_error = 0;
208
209		/* cycle through each option */
210		TAILQ_FOREACH(entry, &entrylist, next) {
211			if (local_error)
212				continue;
213
214			switch(entry->op) {
215			case OP_MERGE_ACL:
216				local_error += merge_acl(entry->acl, acl);
217				need_mask = 1;
218				break;
219			case OP_REMOVE_EXT:
220				remove_ext(acl);
221				need_mask = 0;
222				break;
223			case OP_REMOVE_DEF:
224				if (acl_delete_def_file(file->filename) == -1) {
225					warn("acl_delete_def_file() failed");
226					local_error++;
227				}
228				local_error += remove_default(acl);
229				need_mask = 0;
230				break;
231			case OP_REMOVE_ACL:
232				local_error += remove_acl(entry->acl, acl);
233				need_mask = 1;
234				break;
235			}
236		}
237
238		/* don't bother setting the ACL if something is broken */
239		if (local_error) {
240			carried_error++;
241			continue;
242		}
243
244		if (acl_type == ACL_TYPE_ACCESS)
245			final_acl = acl[ACCESS_ACL];
246		else
247			final_acl = acl[DEFAULT_ACL];
248
249		if (need_mask && (set_acl_mask(&final_acl) == -1)) {
250			warnx("failed to set ACL mask on %s", file->filename);
251			carried_error++;
252		} else if (acl_set_file(file->filename, acl_type,
253		    final_acl) == -1) {
254			carried_error++;
255			warn("acl_set_file() failed for %s", file->filename);
256		}
257
258		acl_free(acl[ACCESS_ACL]);
259		acl_free(acl[DEFAULT_ACL]);
260		free(acl);
261	}
262
263	return (carried_error);
264}
265