]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - bin/setfacl/setfacl.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / bin / setfacl / setfacl.c
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 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
24  * 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 <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include "setfacl.h"
44
45 static void     add_filename(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 void
63 usage(void)
64 {
65
66         fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] "
67             "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n");
68         exit(1);
69 }
70
71 int
72 main(int argc, char *argv[])
73 {
74         acl_t acl;
75         acl_type_t acl_type;
76         char filename[PATH_MAX];
77         int local_error, carried_error, ch, i, entry_number, ret;
78         int h_flag;
79         struct sf_file *file;
80         struct sf_entry *entry;
81         const char *fn_dup;
82         char *end;
83         struct stat sb;
84
85         acl_type = ACL_TYPE_ACCESS;
86         carried_error = local_error = 0;
87         h_flag = have_mask = have_stdin = n_flag = need_mask = 0;
88
89         TAILQ_INIT(&entrylist);
90         TAILQ_INIT(&filelist);
91
92         while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1)
93                 switch(ch) {
94                 case 'M':
95                         entry = zmalloc(sizeof(struct sf_entry));
96                         entry->acl = get_acl_from_file(optarg);
97                         if (entry->acl == NULL)
98                                 err(1, "%s: get_acl_from_file() failed", optarg);
99                         entry->op = OP_MERGE_ACL;
100                         TAILQ_INSERT_TAIL(&entrylist, entry, next);
101                         break;
102                 case 'X':
103                         entry = zmalloc(sizeof(struct sf_entry));
104                         entry->acl = get_acl_from_file(optarg);
105                         entry->op = OP_REMOVE_ACL;
106                         TAILQ_INSERT_TAIL(&entrylist, entry, next);
107                         break;
108                 case 'a':
109                         entry = zmalloc(sizeof(struct sf_entry));
110
111                         entry_number = strtol(optarg, &end, 10);
112                         if (end - optarg != (int)strlen(optarg))
113                                 errx(1, "%s: invalid entry number", optarg);
114                         if (entry_number < 0)
115                                 errx(1, "%s: entry number cannot be less than zero", optarg);
116                         entry->entry_number = entry_number;
117
118                         if (argv[optind] == NULL)
119                                 errx(1, "missing ACL");
120                         entry->acl = acl_from_text(argv[optind]);
121                         if (entry->acl == NULL)
122                                 err(1, "%s", argv[optind]);
123                         optind++;
124                         entry->op = OP_ADD_ACL;
125                         TAILQ_INSERT_TAIL(&entrylist, entry, next);
126                         break;
127                 case 'b':
128                         entry = zmalloc(sizeof(struct sf_entry));
129                         entry->op = OP_REMOVE_EXT;
130                         TAILQ_INSERT_TAIL(&entrylist, entry, next);
131                         break;
132                 case 'd':
133                         acl_type = ACL_TYPE_DEFAULT;
134                         break;
135                 case 'h':
136                         h_flag = 1;
137                         break;
138                 case 'k':
139                         entry = zmalloc(sizeof(struct sf_entry));
140                         entry->op = OP_REMOVE_DEF;
141                         TAILQ_INSERT_TAIL(&entrylist, entry, next);
142                         break;
143                 case 'm':
144                         entry = zmalloc(sizeof(struct sf_entry));
145                         entry->acl = acl_from_text(optarg);
146                         if (entry->acl == NULL)
147                                 err(1, "%s", optarg);
148                         entry->op = OP_MERGE_ACL;
149                         TAILQ_INSERT_TAIL(&entrylist, entry, next);
150                         break;
151                 case 'n':
152                         n_flag++;
153                         break;
154                 case 'x':
155                         entry = zmalloc(sizeof(struct sf_entry));
156                         entry_number = strtol(optarg, &end, 10);
157                         if (end - optarg == (int)strlen(optarg)) {
158                                 if (entry_number < 0)
159                                         errx(1, "%s: entry number cannot be less than zero", optarg);
160                                 entry->entry_number = entry_number;
161                                 entry->op = OP_REMOVE_BY_NUMBER;
162                         } else {
163                                 entry->acl = acl_from_text(optarg);
164                                 if (entry->acl == NULL)
165                                         err(1, "%s", optarg);
166                                 entry->op = OP_REMOVE_ACL;
167                         }
168                         TAILQ_INSERT_TAIL(&entrylist, entry, next);
169                         break;
170                 default:
171                         usage();
172                         break;
173                 }
174         argc -= optind;
175         argv += optind;
176
177         if (n_flag == 0 && TAILQ_EMPTY(&entrylist))
178                 usage();
179
180         /* take list of files from stdin */
181         if (argc == 0 || strcmp(argv[0], "-") == 0) {
182                 if (have_stdin)
183                         err(1, "cannot have more than one stdin");
184                 have_stdin = 1;
185                 bzero(&filename, sizeof(filename));
186                 while (fgets(filename, (int)sizeof(filename), stdin)) {
187                         /* remove the \n */
188                         filename[strlen(filename) - 1] = '\0';
189                         fn_dup = strdup(filename);
190                         if (fn_dup == NULL)
191                                 err(1, "strdup() failed");
192                         add_filename(fn_dup);
193                 }
194         } else
195                 for (i = 0; i < argc; i++)
196                         add_filename(argv[i]);
197
198         /* cycle through each file */
199         TAILQ_FOREACH(file, &filelist, next) {
200                 local_error = 0;
201
202                 if (stat(file->filename, &sb) == -1) {
203                         warn("%s: stat() failed", file->filename);
204                         carried_error++;
205                         continue;
206                 }
207
208                 if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) {
209                         warnx("%s: default ACL may only be set on a directory",
210                             file->filename);
211                         carried_error++;
212                         continue;
213                 }
214
215                 if (h_flag)
216                         ret = lpathconf(file->filename, _PC_ACL_NFS4);
217                 else
218                         ret = pathconf(file->filename, _PC_ACL_NFS4);
219                 if (ret > 0) {
220                         if (acl_type == ACL_TYPE_DEFAULT) {
221                                 warnx("%s: there are no default entries "
222                                    "in NFSv4 ACLs", file->filename);
223                                 carried_error++;
224                                 continue;
225                         }
226                         acl_type = ACL_TYPE_NFS4;
227                 } else if (ret == 0) {
228                         if (acl_type == ACL_TYPE_NFS4)
229                                 acl_type = ACL_TYPE_ACCESS;
230                 } else if (ret < 0 && errno != EINVAL) {
231                         warn("%s: pathconf(..., _PC_ACL_NFS4) failed",
232                             file->filename);
233                 }
234
235                 if (h_flag)
236                         acl = acl_get_link_np(file->filename, acl_type);
237                 else
238                         acl = acl_get_file(file->filename, acl_type);
239                 if (acl == NULL) {
240                         if (h_flag)
241                                 warn("%s: acl_get_link_np() failed",
242                                     file->filename);
243                         else
244                                 warn("%s: acl_get_file() failed",
245                                     file->filename);
246                         carried_error++;
247                         continue;
248                 }
249
250                 /* cycle through each option */
251                 TAILQ_FOREACH(entry, &entrylist, next) {
252                         if (local_error)
253                                 continue;
254
255                         switch(entry->op) {
256                         case OP_ADD_ACL:
257                                 local_error += add_acl(entry->acl,
258                                     entry->entry_number, &acl, file->filename);
259                                 break;
260                         case OP_MERGE_ACL:
261                                 local_error += merge_acl(entry->acl, &acl,
262                                     file->filename);
263                                 need_mask = 1;
264                                 break;
265                         case OP_REMOVE_EXT:
266                                 remove_ext(&acl, file->filename);
267                                 need_mask = 0;
268                                 break;
269                         case OP_REMOVE_DEF:
270                                 if (acl_type == ACL_TYPE_NFS4) {
271                                         warnx("%s: there are no default entries in NFSv4 ACLs; "
272                                             "cannot remove", file->filename);
273                                         local_error++;
274                                         break;
275                                 }
276                                 if (acl_delete_def_file(file->filename) == -1) {
277                                         warn("%s: acl_delete_def_file() failed",
278                                             file->filename);
279                                         local_error++;
280                                 }
281                                 if (acl_type == ACL_TYPE_DEFAULT)
282                                         local_error += remove_default(&acl,
283                                             file->filename);
284                                 need_mask = 0;
285                                 break;
286                         case OP_REMOVE_ACL:
287                                 local_error += remove_acl(entry->acl, &acl,
288                                     file->filename);
289                                 need_mask = 1;
290                                 break;
291                         case OP_REMOVE_BY_NUMBER:
292                                 local_error += remove_by_number(entry->entry_number,
293                                     &acl, file->filename);
294                                 need_mask = 1;
295                                 break;
296                         }
297                 }
298
299                 /* don't bother setting the ACL if something is broken */
300                 if (local_error) {
301                         carried_error++;
302                         continue;
303                 }
304
305                 if (acl_type != ACL_TYPE_NFS4 && need_mask &&
306                     set_acl_mask(&acl, file->filename) == -1) {
307                         warnx("%s: failed to set ACL mask", file->filename);
308                         carried_error++;
309                 } else if (h_flag) {
310                         if (acl_set_link_np(file->filename, acl_type,
311                             acl) == -1) {
312                                 carried_error++;
313                                 warn("%s: acl_set_link_np() failed",
314                                     file->filename);
315                         }
316                 } else {
317                         if (acl_set_file(file->filename, acl_type,
318                             acl) == -1) {
319                                 carried_error++;
320                                 warn("%s: acl_set_file() failed",
321                                     file->filename);
322                         }
323                 }
324
325                 acl_free(acl);
326         }
327
328         return (carried_error);
329 }