]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - bin/setfacl/setfacl.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 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 <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                         continue;
205                 }
206
207                 if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) {
208                         warnx("%s: default ACL may only be set on a directory",
209                             file->filename);
210                         continue;
211                 }
212
213                 if (h_flag)
214                         ret = lpathconf(file->filename, _PC_ACL_NFS4);
215                 else
216                         ret = pathconf(file->filename, _PC_ACL_NFS4);
217                 if (ret > 0) {
218                         if (acl_type == ACL_TYPE_DEFAULT) {
219                                 warnx("%s: there are no default entries "
220                                    "in NFSv4 ACLs", file->filename);
221                                 continue;
222                         }
223                         acl_type = ACL_TYPE_NFS4;
224                 } else if (ret == 0) {
225                         if (acl_type == ACL_TYPE_NFS4)
226                                 acl_type = ACL_TYPE_ACCESS;
227                 } else if (ret < 0 && errno != EINVAL) {
228                         warn("%s: pathconf(..., _PC_ACL_NFS4) failed",
229                             file->filename);
230                 }
231
232                 if (h_flag)
233                         acl = acl_get_link_np(file->filename, acl_type);
234                 else
235                         acl = acl_get_file(file->filename, acl_type);
236                 if (acl == NULL) {
237                         if (h_flag)
238                                 warn("%s: acl_get_link_np() failed",
239                                     file->filename);
240                         else
241                                 warn("%s: acl_get_file() failed",
242                                     file->filename);
243                         continue;
244                 }
245
246                 /* cycle through each option */
247                 TAILQ_FOREACH(entry, &entrylist, next) {
248                         if (local_error)
249                                 continue;
250
251                         switch(entry->op) {
252                         case OP_ADD_ACL:
253                                 local_error += add_acl(entry->acl,
254                                     entry->entry_number, &acl, file->filename);
255                                 break;
256                         case OP_MERGE_ACL:
257                                 local_error += merge_acl(entry->acl, &acl,
258                                     file->filename);
259                                 need_mask = 1;
260                                 break;
261                         case OP_REMOVE_EXT:
262                                 remove_ext(&acl, file->filename);
263                                 need_mask = 0;
264                                 break;
265                         case OP_REMOVE_DEF:
266                                 if (acl_type == ACL_TYPE_NFS4) {
267                                         warnx("%s: there are no default entries in NFSv4 ACLs; "
268                                             "cannot remove", file->filename);
269                                         local_error++;
270                                         break;
271                                 }
272                                 if (acl_delete_def_file(file->filename) == -1) {
273                                         warn("%s: acl_delete_def_file() failed",
274                                             file->filename);
275                                         local_error++;
276                                 }
277                                 if (acl_type == ACL_TYPE_DEFAULT)
278                                         local_error += remove_default(&acl,
279                                             file->filename);
280                                 need_mask = 0;
281                                 break;
282                         case OP_REMOVE_ACL:
283                                 local_error += remove_acl(entry->acl, &acl,
284                                     file->filename);
285                                 need_mask = 1;
286                                 break;
287                         case OP_REMOVE_BY_NUMBER:
288                                 local_error += remove_by_number(entry->entry_number,
289                                     &acl, file->filename);
290                                 need_mask = 1;
291                                 break;
292                         }
293                 }
294
295                 /* don't bother setting the ACL if something is broken */
296                 if (local_error) {
297                         carried_error++;
298                         continue;
299                 }
300
301                 if (acl_type != ACL_TYPE_NFS4 && need_mask &&
302                     set_acl_mask(&acl, file->filename) == -1) {
303                         warnx("%s: failed to set ACL mask", file->filename);
304                         carried_error++;
305                 } else if (h_flag) {
306                         if (acl_set_link_np(file->filename, acl_type,
307                             acl) == -1) {
308                                 carried_error++;
309                                 warn("%s: acl_set_link_np() failed",
310                                     file->filename);
311                         }
312                 } else {
313                         if (acl_set_file(file->filename, acl_type,
314                             acl) == -1) {
315                                 carried_error++;
316                                 warn("%s: acl_set_file() failed",
317                                     file->filename);
318                         }
319                 }
320
321                 acl_free(acl);
322         }
323
324         return (carried_error);
325 }