]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/setfacl/merge.c
zfs: merge openzfs/zfs@9198de8f1
[FreeBSD/FreeBSD.git] / bin / setfacl / merge.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 #include <sys/types.h>
29 #include <sys/acl.h>
30 #include <sys/stat.h>
31
32 #include <err.h>
33
34 #include "setfacl.h"
35
36 static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new,
37     int acl_brand);
38
39 static int
40 merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand)
41 {
42         acl_permset_t permset;
43         acl_entry_type_t entry_type;
44         acl_flagset_t flagset;
45         int have_entry;
46         uid_t *id, *id_new;
47
48         have_entry = 0;
49
50         id = acl_get_qualifier(*entry);
51         if (id == NULL)
52                 err(1, "acl_get_qualifier() failed");
53         id_new = acl_get_qualifier(*entry_new);
54         if (id_new == NULL)
55                 err(1, "acl_get_qualifier() failed");
56         if (*id == *id_new) {
57                 /* any other matches */
58                 if (acl_get_permset(*entry, &permset) == -1)
59                         err(1, "acl_get_permset() failed");
60                 if (acl_set_permset(*entry_new, permset) == -1)
61                         err(1, "acl_set_permset() failed");
62
63                 if (acl_brand == ACL_BRAND_NFS4) {
64                         if (acl_get_entry_type_np(*entry, &entry_type))
65                                 err(1, "acl_get_entry_type_np() failed");
66                         if (acl_set_entry_type_np(*entry_new, entry_type))
67                                 err(1, "acl_set_entry_type_np() failed");
68                         if (acl_get_flagset_np(*entry, &flagset))
69                                 err(1, "acl_get_flagset_np() failed");
70                         if (acl_set_flagset_np(*entry_new, flagset))
71                                 err(1, "acl_set_flagset_np() failed");
72                 }
73
74                 have_entry = 1;
75         }
76         acl_free(id);
77         acl_free(id_new);
78
79         return (have_entry);
80 }
81
82 /*
83  * merge an ACL into existing file's ACL
84  */
85 int
86 merge_acl(acl_t acl, acl_t *prev_acl, const char *filename)
87 {
88         acl_entry_t entry, entry_new;
89         acl_permset_t permset;
90         acl_t acl_new;
91         acl_tag_t tag, tag_new;
92         acl_entry_type_t entry_type, entry_type_new;
93         acl_flagset_t flagset;
94         int entry_id, entry_id_new, have_entry, had_entry, entry_number = 0;
95         int acl_brand, prev_acl_brand;
96
97         acl_get_brand_np(acl, &acl_brand);
98         acl_get_brand_np(*prev_acl, &prev_acl_brand);
99
100         if (branding_mismatch(acl_brand, prev_acl_brand)) {
101                 warnx("%s: branding mismatch; existing ACL is %s, "
102                     "entry to be merged is %s", filename,
103                     brand_name(prev_acl_brand), brand_name(acl_brand));
104                 return (-1);
105         }
106
107         acl_new = acl_dup(*prev_acl);
108         if (acl_new == NULL)
109                 err(1, "%s: acl_dup() failed", filename);
110
111         entry_id = ACL_FIRST_ENTRY;
112
113         while (acl_get_entry(acl, entry_id, &entry) == 1) {
114                 entry_id = ACL_NEXT_ENTRY;
115                 have_entry = 0;
116                 had_entry = 0;
117
118                 /* keep track of existing ACL_MASK entries */
119                 if (acl_get_tag_type(entry, &tag) == -1)
120                         err(1, "%s: acl_get_tag_type() failed - "
121                             "invalid ACL entry", filename);
122                 if (tag == ACL_MASK)
123                         have_mask = true;
124
125                 /* check against the existing ACL entries */
126                 entry_id_new = ACL_FIRST_ENTRY;
127                 while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) {
128                         entry_id_new = ACL_NEXT_ENTRY;
129
130                         if (acl_get_tag_type(entry, &tag) == -1)
131                                 err(1, "%s: acl_get_tag_type() failed",
132                                     filename);
133                         if (acl_get_tag_type(entry_new, &tag_new) == -1)
134                                 err(1, "%s: acl_get_tag_type() failed",
135                                     filename);
136                         if (tag != tag_new)
137                                 continue;
138
139                         /*
140                          * For NFSv4, in addition to "tag" and "id" we also
141                          * compare "entry_type".
142                          */
143                         if (acl_brand == ACL_BRAND_NFS4) {
144                                 if (acl_get_entry_type_np(entry, &entry_type))
145                                         err(1, "%s: acl_get_entry_type_np() "
146                                             "failed", filename);
147                                 if (acl_get_entry_type_np(entry_new, &entry_type_new))
148                                         err(1, "%s: acl_get_entry_type_np() "
149                                             "failed", filename);
150                                 if (entry_type != entry_type_new)
151                                         continue;
152                         }
153                 
154                         switch(tag) {
155                         case ACL_USER:
156                         case ACL_GROUP:
157                                 have_entry = merge_user_group(&entry,
158                                     &entry_new, acl_brand);
159                                 if (have_entry == 0)
160                                         break;
161                                 /* FALLTHROUGH */
162                         case ACL_USER_OBJ:
163                         case ACL_GROUP_OBJ:
164                         case ACL_OTHER:
165                         case ACL_MASK:
166                         case ACL_EVERYONE:
167                                 if (acl_get_permset(entry, &permset) == -1)
168                                         err(1, "%s: acl_get_permset() failed",
169                                             filename);
170                                 if (acl_set_permset(entry_new, permset) == -1)
171                                         err(1, "%s: acl_set_permset() failed",
172                                             filename);
173
174                                 if (acl_brand == ACL_BRAND_NFS4) {
175                                         if (acl_get_entry_type_np(entry, &entry_type))
176                                                 err(1, "%s: acl_get_entry_type_np() failed",
177                                                     filename);
178                                         if (acl_set_entry_type_np(entry_new, entry_type))
179                                                 err(1, "%s: acl_set_entry_type_np() failed",
180                                                     filename);
181                                         if (acl_get_flagset_np(entry, &flagset))
182                                                 err(1, "%s: acl_get_flagset_np() failed",
183                                                     filename);
184                                         if (acl_set_flagset_np(entry_new, flagset))
185                                                 err(1, "%s: acl_set_flagset_np() failed",
186                                                     filename);
187                                 }
188                                 had_entry = have_entry = 1;
189                                 break;
190                         default:
191                                 /* should never be here */
192                                 errx(1, "%s: invalid tag type: %i", filename, tag);
193                                 break;
194                         }
195                 }
196
197                 /* if this entry has not been found, it must be new */
198                 if (had_entry == 0) {
199
200                         /*
201                          * NFSv4 ACL entries must be prepended to the ACL.
202                          * Appending them at the end makes no sense, since
203                          * in most cases they wouldn't even get evaluated.
204                          */
205                         if (acl_brand == ACL_BRAND_NFS4) {
206                                 if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) {
207                                         warn("%s: acl_create_entry_np() failed", filename); 
208                                         acl_free(acl_new);
209                                         return (-1);
210                                 }
211                                 /*
212                                  * Without this increment, adding several
213                                  * entries at once, for example
214                                  * "setfacl -m user:1:r:allow,user:2:r:allow",
215                                  * would make them appear in reverse order.
216                                  */
217                                 entry_number++;
218                         } else {
219                                 if (acl_create_entry(&acl_new, &entry_new) == -1) {
220                                         warn("%s: acl_create_entry() failed", filename); 
221                                         acl_free(acl_new);
222                                         return (-1);
223                                 }
224                         }
225                         if (acl_copy_entry(entry_new, entry) == -1)
226                                 err(1, "%s: acl_copy_entry() failed", filename);
227                 }
228         }
229
230         acl_free(*prev_acl);
231         *prev_acl = acl_new;
232
233         return (0);
234 }
235
236 int
237 add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename)
238 {
239         acl_entry_t entry, entry_new;
240         acl_t acl_new;
241         int entry_id, acl_brand, prev_acl_brand;
242
243         acl_get_brand_np(acl, &acl_brand);
244         acl_get_brand_np(*prev_acl, &prev_acl_brand);
245
246         if (prev_acl_brand != ACL_BRAND_NFS4) {
247                 warnx("%s: the '-a' option is only applicable to NFSv4 ACLs",
248                     filename);
249                 return (-1);
250         }
251
252         if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) {
253                 warnx("%s: branding mismatch; existing ACL is NFSv4, "
254                     "entry to be added is %s", filename,
255                     brand_name(acl_brand));
256                 return (-1);
257         }
258
259         acl_new = acl_dup(*prev_acl);
260         if (acl_new == NULL)
261                 err(1, "%s: acl_dup() failed", filename);
262
263         entry_id = ACL_FIRST_ENTRY;
264
265         while (acl_get_entry(acl, entry_id, &entry) == 1) {
266                 entry_id = ACL_NEXT_ENTRY;
267
268                 if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) {
269                         warn("%s: acl_create_entry_np() failed", filename); 
270                         acl_free(acl_new);
271                         return (-1);
272                 }
273
274                 /*
275                  * Without this increment, adding several
276                  * entries at once, for example
277                  * "setfacl -m user:1:r:allow,user:2:r:allow",
278                  * would make them appear in reverse order.
279                  */
280                 entry_number++;
281
282                 if (acl_copy_entry(entry_new, entry) == -1)
283                         err(1, "%s: acl_copy_entry() failed", filename);
284         }
285
286         acl_free(*prev_acl);
287         *prev_acl = acl_new;
288
289         return (0);
290 }