xref: /freebsd/bin/setfacl/setfacl.c (revision b53e9221214d6406927b73c8e3d15ab8043a3bb2)
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$");
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 
44 static void   add_filename(const char *filename);
45 static acl_t *get_file_acls(const char *filename);
46 static void   usage(void);
47 
48 static void
49 add_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 
62 static acl_t *
63 get_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 
95 static void
96 usage(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 
104 int
105 main(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