2 * Copyright (c) 2001 Chris D. Faulhaber
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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 CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/types.h>
31 #include <sys/param.h>
34 #include <sys/queue.h>
46 #define OP_MERGE_ACL 0x00 /* merge acl's (-mM) */
47 #define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */
48 #define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */
49 #define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */
50 #define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */
51 #define OP_ADD_ACL 0x05 /* add acls entries at a given position */
53 /* TAILQ entry for acl operations */
58 TAILQ_ENTRY(sf_entry) next;
60 static TAILQ_HEAD(, sf_entry) entrylist;
62 /* TAILQ entry for files */
65 TAILQ_ENTRY(sf_file) next;
67 static TAILQ_HEAD(, sf_file) filelist;
74 static void add_filename(const char *filename);
75 static void usage(void);
78 add_filename(const char *filename)
82 if (strlen(filename) > PATH_MAX - 1) {
83 warn("illegal filename");
86 file = zmalloc(sizeof(struct sf_file));
87 file->filename = filename;
88 TAILQ_INSERT_TAIL(&filelist, file, next);
95 fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] "
96 "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n");
101 main(int argc, char *argv[])
105 acl_entry_t unused_entry;
106 char filename[PATH_MAX];
107 int local_error, carried_error, ch, i, entry_number, ret;
109 struct sf_file *file;
110 struct sf_entry *entry;
115 acl_type = ACL_TYPE_ACCESS;
116 carried_error = local_error = 0;
117 h_flag = have_mask = have_stdin = n_flag = need_mask = 0;
119 TAILQ_INIT(&entrylist);
120 TAILQ_INIT(&filelist);
122 while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1)
125 entry = zmalloc(sizeof(struct sf_entry));
126 entry->acl = get_acl_from_file(optarg);
127 if (entry->acl == NULL)
128 err(1, "%s: get_acl_from_file() failed", optarg);
129 entry->op = OP_MERGE_ACL;
130 TAILQ_INSERT_TAIL(&entrylist, entry, next);
133 entry = zmalloc(sizeof(struct sf_entry));
134 entry->acl = get_acl_from_file(optarg);
135 entry->op = OP_REMOVE_ACL;
136 TAILQ_INSERT_TAIL(&entrylist, entry, next);
139 entry = zmalloc(sizeof(struct sf_entry));
141 entry_number = strtol(optarg, &end, 10);
142 if (end - optarg != (int)strlen(optarg))
143 errx(1, "%s: invalid entry number", optarg);
144 if (entry_number < 0)
145 errx(1, "%s: entry number cannot be less than zero", optarg);
146 entry->entry_number = entry_number;
148 if (argv[optind] == NULL)
149 errx(1, "missing ACL");
150 entry->acl = acl_from_text(argv[optind]);
151 if (entry->acl == NULL)
152 err(1, "%s", argv[optind]);
154 entry->op = OP_ADD_ACL;
155 TAILQ_INSERT_TAIL(&entrylist, entry, next);
158 entry = zmalloc(sizeof(struct sf_entry));
159 entry->op = OP_REMOVE_EXT;
160 TAILQ_INSERT_TAIL(&entrylist, entry, next);
163 acl_type = ACL_TYPE_DEFAULT;
169 entry = zmalloc(sizeof(struct sf_entry));
170 entry->op = OP_REMOVE_DEF;
171 TAILQ_INSERT_TAIL(&entrylist, entry, next);
174 entry = zmalloc(sizeof(struct sf_entry));
175 entry->acl = acl_from_text(optarg);
176 if (entry->acl == NULL)
177 err(1, "%s", optarg);
178 entry->op = OP_MERGE_ACL;
179 TAILQ_INSERT_TAIL(&entrylist, entry, next);
185 entry = zmalloc(sizeof(struct sf_entry));
186 entry_number = strtol(optarg, &end, 10);
187 if (end - optarg == (int)strlen(optarg)) {
188 if (entry_number < 0)
189 errx(1, "%s: entry number cannot be less than zero", optarg);
190 entry->entry_number = entry_number;
191 entry->op = OP_REMOVE_BY_NUMBER;
193 entry->acl = acl_from_text(optarg);
194 if (entry->acl == NULL)
195 err(1, "%s", optarg);
196 entry->op = OP_REMOVE_ACL;
198 TAILQ_INSERT_TAIL(&entrylist, entry, next);
207 if (n_flag == 0 && TAILQ_EMPTY(&entrylist))
210 /* take list of files from stdin */
211 if (argc == 0 || strcmp(argv[0], "-") == 0) {
213 err(1, "cannot have more than one stdin");
215 bzero(&filename, sizeof(filename));
216 while (fgets(filename, (int)sizeof(filename), stdin)) {
218 filename[strlen(filename) - 1] = '\0';
219 fn_dup = strdup(filename);
221 err(1, "strdup() failed");
222 add_filename(fn_dup);
225 for (i = 0; i < argc; i++)
226 add_filename(argv[i]);
228 /* cycle through each file */
229 TAILQ_FOREACH(file, &filelist, next) {
232 if (stat(file->filename, &sb) == -1) {
233 warn("%s: stat() failed", file->filename);
238 if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) {
239 warnx("%s: default ACL may only be set on a directory",
246 ret = lpathconf(file->filename, _PC_ACL_NFS4);
248 ret = pathconf(file->filename, _PC_ACL_NFS4);
250 if (acl_type == ACL_TYPE_DEFAULT) {
251 warnx("%s: there are no default entries "
252 "in NFSv4 ACLs", file->filename);
256 acl_type = ACL_TYPE_NFS4;
257 } else if (ret == 0) {
258 if (acl_type == ACL_TYPE_NFS4)
259 acl_type = ACL_TYPE_ACCESS;
260 } else if (ret < 0 && errno != EINVAL) {
261 warn("%s: pathconf(..., _PC_ACL_NFS4) failed",
266 acl = acl_get_link_np(file->filename, acl_type);
268 acl = acl_get_file(file->filename, acl_type);
271 warn("%s: acl_get_link_np() failed",
274 warn("%s: acl_get_file() failed",
280 /* cycle through each option */
281 TAILQ_FOREACH(entry, &entrylist, next) {
287 local_error += add_acl(entry->acl,
288 entry->entry_number, &acl, file->filename);
291 local_error += merge_acl(entry->acl, &acl,
297 * Don't try to call remove_ext() for empty
300 if (acl_type == ACL_TYPE_DEFAULT &&
301 acl_get_entry(acl, ACL_FIRST_ENTRY,
302 &unused_entry) == 0) {
303 local_error += remove_default(&acl,
307 remove_ext(&acl, file->filename);
311 if (acl_type == ACL_TYPE_NFS4) {
312 warnx("%s: there are no default entries in NFSv4 ACLs; "
313 "cannot remove", file->filename);
317 if (acl_delete_def_file(file->filename) == -1) {
318 warn("%s: acl_delete_def_file() failed",
322 if (acl_type == ACL_TYPE_DEFAULT)
323 local_error += remove_default(&acl,
328 local_error += remove_acl(entry->acl, &acl,
332 case OP_REMOVE_BY_NUMBER:
333 local_error += remove_by_number(entry->entry_number,
334 &acl, file->filename);
341 * Don't try to set an empty default ACL; it will always fail.
342 * Use acl_delete_def_file(3) instead.
344 if (acl_type == ACL_TYPE_DEFAULT &&
345 acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) {
346 if (acl_delete_def_file(file->filename) == -1) {
347 warn("%s: acl_delete_def_file() failed",
354 /* don't bother setting the ACL if something is broken */
360 if (acl_type != ACL_TYPE_NFS4 && need_mask &&
361 set_acl_mask(&acl, file->filename) == -1) {
362 warnx("%s: failed to set ACL mask", file->filename);
365 if (acl_set_link_np(file->filename, acl_type,
368 warn("%s: acl_set_link_np() failed",
372 if (acl_set_file(file->filename, acl_type,
375 warn("%s: acl_set_file() failed",
383 return (carried_error);