]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libarchive/libarchive/test/test_acl_freebsd_posix1e.c
Merge compiler-rt r291274.
[FreeBSD/FreeBSD.git] / contrib / libarchive / libarchive / test / test_acl_freebsd_posix1e.c
1 /*-
2  * Copyright (c) 2003-2008 Tim Kientzle
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(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "test.h"
26 __FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-06 04:21:23Z kientzle $");
27
28 #if defined(__FreeBSD__) && __FreeBSD__ > 4
29 #include <sys/acl.h>
30
31 static struct archive_test_acl_t acls2[] = {
32         { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
33           ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
34         { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
35           ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
36         { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
37           ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
38         { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
39           ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
40         { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
41           ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
42         { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
43           ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
44           ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
45         { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
46           ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
47           ARCHIVE_ENTRY_ACL_MASK, -1, "" },
48 };
49
50 static int
51 acl_entry_get_perm(acl_entry_t aclent) {
52         int permset = 0;
53         acl_permset_t opaque_ps;
54
55         /* translate the silly opaque permset to a bitmap */
56         acl_get_permset(aclent, &opaque_ps);
57         if (acl_get_perm_np(opaque_ps, ACL_EXECUTE))
58                 permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
59         if (acl_get_perm_np(opaque_ps, ACL_WRITE))
60                 permset |= ARCHIVE_ENTRY_ACL_WRITE;
61         if (acl_get_perm_np(opaque_ps, ACL_READ))
62                 permset |= ARCHIVE_ENTRY_ACL_READ;
63         return permset;
64 }
65
66 #if 0
67 static int
68 acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_tag) {
69         int entry_id = ACL_FIRST_ENTRY;
70         acl_entry_t acl_entry;
71         acl_tag_t acl_tag_type;
72         
73         while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
74                 /* After the first time... */
75                 entry_id = ACL_NEXT_ENTRY;
76
77                 /* If this matches, return perm mask */
78                 acl_get_tag_type(acl_entry, &acl_tag_type);
79                 if (acl_tag_type == requested_tag_type) {
80                         switch (acl_tag_type) {
81                         case ACL_USER_OBJ:
82                                 if ((uid_t)requested_tag == *(uid_t *)(acl_get_qualifier(acl_entry))) {
83                                         return acl_entry_get_perm(acl_entry);
84                                 }
85                                 break;
86                         case ACL_GROUP_OBJ:
87                                 if ((gid_t)requested_tag == *(gid_t *)(acl_get_qualifier(acl_entry))) {
88                                         return acl_entry_get_perm(acl_entry);
89                                 }
90                                 break;
91                         case ACL_USER:
92                         case ACL_GROUP:
93                         case ACL_OTHER:
94                                 return acl_entry_get_perm(acl_entry);
95                         default:
96                                 failure("Unexpected ACL tag type");
97                                 assert(0);
98                         }
99                 }
100
101
102         }
103         return -1;
104 }
105 #endif
106
107 static int
108 acl_match(acl_entry_t aclent, struct archive_test_acl_t *myacl)
109 {
110         gid_t g, *gp;
111         uid_t u, *up;
112         acl_tag_t tag_type;
113
114         if (myacl->permset != acl_entry_get_perm(aclent))
115                 return (0);
116
117         acl_get_tag_type(aclent, &tag_type);
118         switch (tag_type) {
119         case ACL_USER_OBJ:
120                 if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
121                 break;
122         case ACL_USER:
123                 if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
124                         return (0);
125                 up = acl_get_qualifier(aclent);
126                 u = *up;
127                 acl_free(up);
128                 if ((uid_t)myacl->qual != u)
129                         return (0);
130                 break;
131         case ACL_GROUP_OBJ:
132                 if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
133                 break;
134         case ACL_GROUP:
135                 if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
136                         return (0);
137                 gp = acl_get_qualifier(aclent);
138                 g = *gp;
139                 acl_free(gp);
140                 if ((gid_t)myacl->qual != g)
141                         return (0);
142                 break;
143         case ACL_MASK:
144                 if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
145                 break;
146         case ACL_OTHER:
147                 if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0);
148                 break;
149         }
150         return (1);
151 }
152
153 static void
154 compare_acls(acl_t acl, struct archive_test_acl_t *myacls, int n)
155 {
156         int *marker;
157         int entry_id = ACL_FIRST_ENTRY;
158         int matched;
159         int i;
160         acl_entry_t acl_entry;
161
162         /* Count ACL entries in myacls array and allocate an indirect array. */
163         marker = malloc(sizeof(marker[0]) * n);
164         if (marker == NULL)
165                 return;
166         for (i = 0; i < n; i++)
167                 marker[i] = i;
168
169         /*
170          * Iterate over acls in system acl object, try to match each
171          * one with an item in the myacls array.
172          */
173         while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
174                 /* After the first time... */
175                 entry_id = ACL_NEXT_ENTRY;
176
177                 /* Search for a matching entry (tag and qualifier) */
178                 for (i = 0, matched = 0; i < n && !matched; i++) {
179                         if (acl_match(acl_entry, &myacls[marker[i]])) {
180                                 /* We found a match; remove it. */
181                                 marker[i] = marker[n - 1];
182                                 n--;
183                                 matched = 1;
184                         }
185                 }
186
187                 /* TODO: Print out more details in this case. */
188                 failure("ACL entry on file that shouldn't be there");
189                 assert(matched == 1);
190         }
191
192         /* Dump entries in the myacls array that weren't in the system acl. */
193         for (i = 0; i < n; ++i) {
194                 failure(" ACL entry missing from file: "
195                     "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n",
196                     myacls[marker[i]].type, myacls[marker[i]].permset,
197                     myacls[marker[i]].tag, myacls[marker[i]].qual,
198                     myacls[marker[i]].name);
199                 assert(0); /* Record this as a failure. */
200         }
201         free(marker);
202 }
203
204 #endif
205
206
207 /*
208  * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
209  */
210
211 DEFINE_TEST(test_acl_freebsd_posix1e_restore)
212 {
213 #if !defined(__FreeBSD__)
214         skipping("FreeBSD-specific ACL restore test");
215 #elif __FreeBSD__ < 5
216         skipping("ACL restore supported only on FreeBSD 5.0 and later");
217 #else
218         struct stat st;
219         struct archive *a;
220         struct archive_entry *ae;
221         int n, fd;
222         acl_t acl;
223
224         /*
225          * First, do a quick manual set/read of ACL data to
226          * verify that the local filesystem does support ACLs.
227          * If it doesn't, we'll simply skip the remaining tests.
228          */
229         acl = acl_from_text("u::rwx,u:1:rw,g::rwx,g:15:rx,o::rwx,m::rwx");
230         assert((void *)acl != NULL);
231         /* Create a test file and try to set an ACL on it. */
232         fd = open("pretest", O_WRONLY | O_CREAT | O_EXCL, 0777);
233         failure("Could not create test file?!");
234         if (!assert(fd >= 0)) {
235                 acl_free(acl);
236                 return;
237         }
238
239         n = acl_set_fd(fd, acl);
240         acl_free(acl);
241         if (n != 0 && errno == EOPNOTSUPP) {
242                 close(fd);
243                 skipping("ACL tests require that ACL support be enabled on the filesystem");
244                 return;
245         }
246         if (n != 0 && errno == EINVAL) {
247                 close(fd);
248                 skipping("This filesystem does not support POSIX.1e ACLs");
249                 return;
250         }
251         failure("acl_set_fd(): errno = %d (%s)",
252             errno, strerror(errno));
253         assertEqualInt(0, n);
254         close(fd);
255
256         /* Create a write-to-disk object. */
257         assert(NULL != (a = archive_write_disk_new()));
258         archive_write_disk_set_options(a,
259             ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
260
261         /* Populate an archive entry with some metadata, including ACL info */
262         ae = archive_entry_new();
263         assert(ae != NULL);
264         archive_entry_set_pathname(ae, "test0");
265         archive_entry_set_mtime(ae, 123456, 7890);
266         archive_entry_set_size(ae, 0);
267         archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
268         assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
269         archive_entry_free(ae);
270
271         /* Close the archive. */
272         assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
273         assertEqualInt(ARCHIVE_OK, archive_write_free(a));
274
275         /* Verify the data on disk. */
276         assertEqualInt(0, stat("test0", &st));
277         assertEqualInt(st.st_mtime, 123456);
278         acl = acl_get_file("test0", ACL_TYPE_ACCESS);
279         assert(acl != (acl_t)NULL);
280         compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0]));
281         acl_free(acl);
282 #endif
283 }
284
285 /*
286  * Verify ACL read-from-disk.  This test is FreeBSD-specific.
287  */
288 DEFINE_TEST(test_acl_freebsd_posix1e_read)
289 {
290 #if !defined(__FreeBSD__)
291         skipping("FreeBSD-specific ACL read test");
292 #elif __FreeBSD__ < 5
293         skipping("ACL read supported only on FreeBSD 5.0 and later");
294 #else
295         struct archive *a;
296         struct archive_entry *ae;
297         int n, fd;
298         const char *acl1_text, *acl2_text;
299         acl_t acl1, acl2;
300
301         /*
302          * Manually construct a directory and two files with
303          * different ACLs.  This also serves to verify that ACLs
304          * are supported on the local filesystem.
305          */
306
307         /* Create a test file f1 with acl1 */
308         acl1_text = "user::rwx\n"
309             "group::rwx\n"
310             "other::rwx\n"
311             "user:1:rw-\n"
312             "group:15:r-x\n"
313             "mask::rwx";
314         acl1 = acl_from_text(acl1_text);
315         assert((void *)acl1 != NULL);
316         fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
317         failure("Could not create test file?!");
318         if (!assert(fd >= 0)) {
319                 acl_free(acl1);
320                 return;
321         }
322         n = acl_set_fd(fd, acl1);
323         acl_free(acl1);
324         if (n != 0 && errno == EOPNOTSUPP) {
325                 close(fd);
326                 skipping("ACL tests require that ACL support be enabled on the filesystem");
327                 return;
328         }
329         if (n != 0 && errno == EINVAL) {
330                 close(fd);
331                 skipping("This filesystem does not support POSIX.1e ACLs");
332                 return;
333         }
334         failure("acl_set_fd(): errno = %d (%s)",
335             errno, strerror(errno));
336         assertEqualInt(0, n);
337         close(fd);
338
339         assertMakeDir("d", 0700);
340
341         /*
342          * Create file d/f1 with acl2
343          *
344          * This differs from acl1 in the u:1: and g:15: permissions.
345          *
346          * This file deliberately has the same name but a different ACL.
347          * Github Issue #777 explains how libarchive's directory traversal
348          * did not always correctly enter directories before attempting
349          * to read ACLs, resulting in reading the ACL from a like-named
350          * file in the wrong directory.
351          */
352         acl2_text = "user::rwx\n"
353             "group::rwx\n"
354             "other::---\n"
355             "user:1:r--\n"
356             "group:15:r--\n"
357             "mask::rwx";
358         acl2 = acl_from_text(acl2_text);
359         assert((void *)acl2 != NULL);
360         fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
361         failure("Could not create test file?!");
362         if (!assert(fd >= 0)) {
363                 acl_free(acl2);
364                 return;
365         }
366         n = acl_set_fd(fd, acl2);
367         acl_free(acl2);
368         if (n != 0 && errno == EOPNOTSUPP) {
369                 close(fd);
370                 skipping("ACL tests require that ACL support be enabled on the filesystem");
371                 return;
372         }
373         if (n != 0 && errno == EINVAL) {
374                 close(fd);
375                 skipping("This filesystem does not support POSIX.1e ACLs");
376                 return;
377         }
378         failure("acl_set_fd(): errno = %d (%s)",
379             errno, strerror(errno));
380         assertEqualInt(0, n);
381         close(fd);
382
383         /* Create a read-from-disk object. */
384         assert(NULL != (a = archive_read_disk_new()));
385         assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "."));
386         assert(NULL != (ae = archive_entry_new()));
387
388         /* Walk the dir until we see both of the files */
389         while (ARCHIVE_OK == archive_read_next_header2(a, ae)) {
390                 archive_read_disk_descend(a);
391                 if (strcmp(archive_entry_pathname(ae), "./f1") == 0) {
392                         assertEqualString(archive_entry_acl_to_text(ae, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl1_text);
393                             
394                 } else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) {
395                         assertEqualString(archive_entry_acl_to_text(ae, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl2_text);
396                 }
397         }
398
399         archive_free(a);
400 #endif
401 }