xref: /freebsd/bin/setfacl/setfacl.c (revision 7dfd9569a2f0637fb9a48157b1c1bfe5709faee3)
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 
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