1238909Smm/*-
2238909Smm * Copyright (c) 2003-2010 Tim Kientzle
3238909Smm * All rights reserved.
4238909Smm *
5238909Smm * Redistribution and use in source and binary forms, with or without
6238909Smm * modification, are permitted provided that the following conditions
7238909Smm * are met:
8238909Smm * 1. Redistributions of source code must retain the above copyright
9238909Smm *    notice, this list of conditions and the following disclaimer.
10238909Smm * 2. Redistributions in binary form must reproduce the above copyright
11238909Smm *    notice, this list of conditions and the following disclaimer in the
12238909Smm *    documentation and/or other materials provided with the distribution.
13238909Smm *
14238909Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15238909Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16238909Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17238909Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18238909Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19238909Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20238909Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21238909Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22238909Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23238909Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24238909Smm */
25238909Smm#include "test.h"
26238909Smm__FBSDID("$FreeBSD$");
27238909Smm
28238909Smm#if defined(__FreeBSD__) && __FreeBSD__ >= 8
29238909Smm#define _ACL_PRIVATE
30238909Smm#include <sys/acl.h>
31238909Smm
32238909Smmstruct myacl_t {
33238909Smm	int type;
34238909Smm	int permset;
35238909Smm	int tag;
36238909Smm	int qual; /* GID or UID of user/group, depending on tag. */
37238909Smm	const char *name; /* Name of user/group, depending on tag. */
38238909Smm};
39238909Smm
40238909Smmstatic struct myacl_t acls_reg[] = {
41238909Smm	/* For this test, we need the file owner to be able to read and write the ACL. */
42238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
43238909Smm	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
44238909Smm	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
45238909Smm
46238909Smm	/* An entry for each type. */
47238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
48238909Smm	  ARCHIVE_ENTRY_ACL_USER, 108, "user108" },
49238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE,
50238909Smm	  ARCHIVE_ENTRY_ACL_USER, 109, "user109" },
51238909Smm
52238909Smm	/* An entry for each permission. */
53238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
54238909Smm	  ARCHIVE_ENTRY_ACL_USER, 112, "user112" },
55238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA,
56238909Smm	  ARCHIVE_ENTRY_ACL_USER, 113, "user113" },
57238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_DATA,
58238909Smm	  ARCHIVE_ENTRY_ACL_USER, 115, "user115" },
59238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_APPEND_DATA,
60238909Smm	  ARCHIVE_ENTRY_ACL_USER, 117, "user117" },
61238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
62238909Smm	  ARCHIVE_ENTRY_ACL_USER, 119, "user119" },
63238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
64238909Smm	  ARCHIVE_ENTRY_ACL_USER, 120, "user120" },
65238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
66238909Smm	  ARCHIVE_ENTRY_ACL_USER, 122, "user122" },
67238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
68238909Smm	  ARCHIVE_ENTRY_ACL_USER, 123, "user123" },
69238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
70238909Smm	  ARCHIVE_ENTRY_ACL_USER, 124, "user124" },
71238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
72238909Smm	  ARCHIVE_ENTRY_ACL_USER, 125, "user125" },
73238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
74238909Smm	  ARCHIVE_ENTRY_ACL_USER, 126, "user126" },
75238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
76238909Smm	  ARCHIVE_ENTRY_ACL_USER, 127, "user127" },
77238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
78238909Smm	  ARCHIVE_ENTRY_ACL_USER, 128, "user128" },
79238909Smm
80238909Smm	/* One entry for each qualifier. */
81238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
82238909Smm	  ARCHIVE_ENTRY_ACL_USER, 135, "user135" },
83238909Smm//	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
84238909Smm//	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
85238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
86238909Smm	  ARCHIVE_ENTRY_ACL_GROUP, 136, "group136" },
87238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
88238909Smm	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
89238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
90238909Smm	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
91238909Smm};
92238909Smm
93238909Smm
94238909Smmstatic struct myacl_t acls_dir[] = {
95238909Smm	/* For this test, we need to be able to read and write the ACL. */
96238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
97238909Smm	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
98238909Smm
99238909Smm	/* An entry for each type. */
100238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
101238909Smm	  ARCHIVE_ENTRY_ACL_USER, 101, "user101" },
102238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
103238909Smm	  ARCHIVE_ENTRY_ACL_USER, 102, "user102" },
104238909Smm
105238909Smm	/* An entry for each permission. */
106238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
107238909Smm	  ARCHIVE_ENTRY_ACL_USER, 201, "user201" },
108238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_FILE,
109238909Smm	  ARCHIVE_ENTRY_ACL_USER, 202, "user202" },
110238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
111238909Smm	  ARCHIVE_ENTRY_ACL_USER, 203, "user203" },
112238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
113238909Smm	  ARCHIVE_ENTRY_ACL_USER, 204, "user204" },
114238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
115238909Smm	  ARCHIVE_ENTRY_ACL_USER, 205, "user205" },
116238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE_CHILD,
117238909Smm	  ARCHIVE_ENTRY_ACL_USER, 206, "user206" },
118238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
119238909Smm	  ARCHIVE_ENTRY_ACL_USER, 207, "user207" },
120238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
121238909Smm	  ARCHIVE_ENTRY_ACL_USER, 208, "user208" },
122238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
123238909Smm	  ARCHIVE_ENTRY_ACL_USER, 209, "user209" },
124238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
125238909Smm	  ARCHIVE_ENTRY_ACL_USER, 210, "user210" },
126238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
127238909Smm	  ARCHIVE_ENTRY_ACL_USER, 211, "user211" },
128238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
129238909Smm	  ARCHIVE_ENTRY_ACL_USER, 212, "user212" },
130238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
131238909Smm	  ARCHIVE_ENTRY_ACL_USER, 213, "user213" },
132238909Smm
133238909Smm	/* One entry with each inheritance value. */
134238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
135238909Smm	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT,
136238909Smm	  ARCHIVE_ENTRY_ACL_USER, 301, "user301" },
137238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
138238909Smm	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
139238909Smm	  ARCHIVE_ENTRY_ACL_USER, 302, "user302" },
140238909Smm#if 0
141238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
142238909Smm	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
143238909Smm	  ARCHIVE_ENTRY_ACL_USER, 303, "user303" },
144238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
145238909Smm	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY,
146238909Smm	  ARCHIVE_ENTRY_ACL_USER, 304, "user304" },
147238909Smm#endif
148238909Smm
149238909Smm#if 0
150238909Smm	/* FreeBSD does not support audit entries. */
151238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
152238909Smm	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
153238909Smm	  ARCHIVE_ENTRY_ACL_USER, 401, "user401" },
154238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
155238909Smm	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
156238909Smm	  ARCHIVE_ENTRY_ACL_USER, 402, "user402" },
157238909Smm#endif
158238909Smm
159238909Smm	/* One entry for each qualifier. */
160238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
161238909Smm	  ARCHIVE_ENTRY_ACL_USER, 501, "user501" },
162238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
163238909Smm	  ARCHIVE_ENTRY_ACL_GROUP, 502, "group502" },
164238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
165238909Smm	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
166238909Smm	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
167238909Smm	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
168238909Smm};
169238909Smm
170238909Smmstatic void
171238909Smmset_acls(struct archive_entry *ae, struct myacl_t *acls, int start, int end)
172238909Smm{
173238909Smm	int i;
174238909Smm
175238909Smm	archive_entry_acl_clear(ae);
176238909Smm	if (start > 0) {
177238909Smm		assertEqualInt(ARCHIVE_OK,
178238909Smm			archive_entry_acl_add_entry(ae,
179238909Smm			    acls[0].type, acls[0].permset, acls[0].tag,
180238909Smm			    acls[0].qual, acls[0].name));
181238909Smm	}
182238909Smm	for (i = start; i < end; i++) {
183238909Smm		assertEqualInt(ARCHIVE_OK,
184238909Smm		    archive_entry_acl_add_entry(ae,
185238909Smm			acls[i].type, acls[i].permset, acls[i].tag,
186238909Smm			acls[i].qual, acls[i].name));
187238909Smm	}
188238909Smm}
189238909Smm
190238909Smmstatic int
191238909Smmacl_permset_to_bitmap(acl_permset_t opaque_ps)
192238909Smm{
193238909Smm	static struct { int machine; int portable; } perms[] = {
194238909Smm		{ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE},
195238909Smm		{ACL_WRITE, ARCHIVE_ENTRY_ACL_WRITE},
196238909Smm		{ACL_READ, ARCHIVE_ENTRY_ACL_READ},
197238909Smm		{ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA},
198238909Smm		{ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY},
199238909Smm		{ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA},
200238909Smm		{ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE},
201238909Smm		{ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA},
202238909Smm		{ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY},
203238909Smm		{ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS},
204238909Smm		{ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS},
205238909Smm		{ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD},
206238909Smm		{ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES},
207238909Smm		{ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES},
208238909Smm		{ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE},
209238909Smm		{ACL_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL},
210238909Smm		{ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL},
211238909Smm		{ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER},
212238909Smm		{ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE}
213238909Smm	};
214238909Smm	int i, permset = 0;
215238909Smm
216238909Smm	for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i)
217238909Smm		if (acl_get_perm_np(opaque_ps, perms[i].machine))
218238909Smm			permset |= perms[i].portable;
219238909Smm	return permset;
220238909Smm}
221238909Smm
222238909Smmstatic int
223238909Smmacl_flagset_to_bitmap(acl_flagset_t opaque_fs)
224238909Smm{
225238909Smm	static struct { int machine; int portable; } flags[] = {
226238909Smm		{ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT},
227238909Smm		{ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT},
228238909Smm		{ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT},
229238909Smm		{ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY},
230238909Smm	};
231238909Smm	int i, flagset = 0;
232238909Smm
233238909Smm	for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); ++i)
234238909Smm		if (acl_get_flag_np(opaque_fs, flags[i].machine))
235238909Smm			flagset |= flags[i].portable;
236238909Smm	return flagset;
237238909Smm}
238238909Smm
239238909Smmstatic int
240238909Smmacl_match(acl_entry_t aclent, struct myacl_t *myacl)
241238909Smm{
242238909Smm	gid_t g, *gp;
243238909Smm	uid_t u, *up;
244238909Smm	acl_tag_t tag_type;
245238909Smm	acl_permset_t opaque_ps;
246238909Smm	acl_flagset_t opaque_fs;
247238909Smm	int perms;
248238909Smm
249238909Smm	acl_get_tag_type(aclent, &tag_type);
250238909Smm
251238909Smm	/* translate the silly opaque permset to a bitmap */
252238909Smm	acl_get_permset(aclent, &opaque_ps);
253238909Smm	acl_get_flagset_np(aclent, &opaque_fs);
254238909Smm	perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs);
255238909Smm	if (perms != myacl->permset)
256238909Smm		return (0);
257238909Smm
258238909Smm	switch (tag_type) {
259238909Smm	case ACL_USER_OBJ:
260238909Smm		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
261238909Smm		break;
262238909Smm	case ACL_USER:
263238909Smm		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
264238909Smm			return (0);
265238909Smm		up = acl_get_qualifier(aclent);
266238909Smm		u = *up;
267238909Smm		acl_free(up);
268238909Smm		if ((uid_t)myacl->qual != u)
269238909Smm			return (0);
270238909Smm		break;
271238909Smm	case ACL_GROUP_OBJ:
272238909Smm		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
273238909Smm		break;
274238909Smm	case ACL_GROUP:
275238909Smm		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
276238909Smm			return (0);
277238909Smm		gp = acl_get_qualifier(aclent);
278238909Smm		g = *gp;
279238909Smm		acl_free(gp);
280238909Smm		if ((gid_t)myacl->qual != g)
281238909Smm			return (0);
282238909Smm		break;
283238909Smm	case ACL_MASK:
284238909Smm		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
285238909Smm		break;
286238909Smm	case ACL_EVERYONE:
287238909Smm		if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0);
288238909Smm		break;
289238909Smm	}
290238909Smm	return (1);
291238909Smm}
292238909Smm
293238909Smmstatic void
294238909Smmcompare_acls(acl_t acl, struct myacl_t *myacls, const char *filename, int start, int end)
295238909Smm{
296238909Smm	int *marker;
297238909Smm	int entry_id = ACL_FIRST_ENTRY;
298238909Smm	int matched;
299238909Smm	int i, n;
300238909Smm	acl_entry_t acl_entry;
301238909Smm
302238909Smm	n = end - start;
303238909Smm	marker = malloc(sizeof(marker[0]) * (n + 1));
304238909Smm	for (i = 0; i < n; i++)
305238909Smm		marker[i] = i + start;
306238909Smm	/* Always include the first ACE. */
307238909Smm	if (start > 0) {
308238909Smm	  marker[n] = 0;
309238909Smm	  ++n;
310238909Smm	}
311238909Smm
312238909Smm	/*
313238909Smm	 * Iterate over acls in system acl object, try to match each
314238909Smm	 * one with an item in the myacls array.
315238909Smm	 */
316238909Smm	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
317238909Smm		/* After the first time... */
318238909Smm		entry_id = ACL_NEXT_ENTRY;
319238909Smm
320238909Smm		/* Search for a matching entry (tag and qualifier) */
321238909Smm		for (i = 0, matched = 0; i < n && !matched; i++) {
322238909Smm			if (acl_match(acl_entry, &myacls[marker[i]])) {
323238909Smm				/* We found a match; remove it. */
324238909Smm				marker[i] = marker[n - 1];
325238909Smm				n--;
326238909Smm				matched = 1;
327238909Smm			}
328238909Smm		}
329238909Smm
330238909Smm		failure("ACL entry on file %s that shouldn't be there", filename);
331238909Smm		assert(matched == 1);
332238909Smm	}
333238909Smm
334238909Smm	/* Dump entries in the myacls array that weren't in the system acl. */
335238909Smm	for (i = 0; i < n; ++i) {
336238909Smm		failure(" ACL entry %d missing from %s: "
337238909Smm		    "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
338238909Smm		    marker[i], filename,
339238909Smm		    myacls[marker[i]].type, myacls[marker[i]].permset,
340238909Smm		    myacls[marker[i]].tag, myacls[marker[i]].qual,
341238909Smm		    myacls[marker[i]].name);
342238909Smm		assert(0); /* Record this as a failure. */
343238909Smm	}
344238909Smm	free(marker);
345238909Smm}
346238909Smm
347238909Smmstatic void
348238909Smmcompare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char *filename, int start, int end)
349238909Smm{
350238909Smm	int *marker;
351238909Smm	int matched;
352238909Smm	int i, n;
353238909Smm	int type, permset, tag, qual;
354238909Smm	const char *name;
355238909Smm
356238909Smm	/* Count ACL entries in myacls array and allocate an indirect array. */
357238909Smm	n = end - start;
358238909Smm	marker = malloc(sizeof(marker[0]) * (n + 1));
359238909Smm	for (i = 0; i < n; i++)
360238909Smm		marker[i] = i + start;
361238909Smm	/* Always include the first ACE. */
362238909Smm	if (start > 0) {
363238909Smm	  marker[n] = 0;
364238909Smm	  ++n;
365238909Smm	}
366238909Smm
367238909Smm	/*
368238909Smm	 * Iterate over acls in entry, try to match each
369238909Smm	 * one with an item in the myacls array.
370238909Smm	 */
371238909Smm	assertEqualInt(n, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
372238909Smm	while (ARCHIVE_OK == archive_entry_acl_next(ae,
373238909Smm	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, &type, &permset, &tag, &qual, &name)) {
374238909Smm
375238909Smm		/* Search for a matching entry (tag and qualifier) */
376238909Smm		for (i = 0, matched = 0; i < n && !matched; i++) {
377238909Smm			if (tag == myacls[marker[i]].tag
378238909Smm			    && qual == myacls[marker[i]].qual
379238909Smm			    && permset == myacls[marker[i]].permset
380238909Smm			    && type == myacls[marker[i]].type) {
381238909Smm				/* We found a match; remove it. */
382238909Smm				marker[i] = marker[n - 1];
383238909Smm				n--;
384238909Smm				matched = 1;
385238909Smm			}
386238909Smm		}
387238909Smm
388238909Smm		failure("ACL entry on file that shouldn't be there: "
389238909Smm			"type=%d,permset=%x,tag=%d,qual=%d",
390238909Smm			type,permset,tag,qual);
391238909Smm		assert(matched == 1);
392238909Smm	}
393238909Smm
394238909Smm	/* Dump entries in the myacls array that weren't in the system acl. */
395238909Smm	for (i = 0; i < n; ++i) {
396238909Smm		failure(" ACL entry %d missing from %s: "
397238909Smm		    "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
398238909Smm		    marker[i], filename,
399238909Smm		    myacls[marker[i]].type, myacls[marker[i]].permset,
400238909Smm		    myacls[marker[i]].tag, myacls[marker[i]].qual,
401238909Smm		    myacls[marker[i]].name);
402238909Smm		assert(0); /* Record this as a failure. */
403238909Smm	}
404238909Smm	free(marker);
405238909Smm}
406238909Smm#endif
407238909Smm
408238909Smm/*
409238909Smm * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
410238909Smm */
411238909Smm
412238909SmmDEFINE_TEST(test_acl_freebsd_nfs4)
413238909Smm{
414238909Smm#if !defined(__FreeBSD__)
415238909Smm	skipping("FreeBSD-specific NFS4 ACL restore test");
416238909Smm#elif __FreeBSD__ < 8
417238909Smm	skipping("NFS4 ACLs supported only on FreeBSD 8.0 and later");
418238909Smm#else
419238909Smm	char buff[64];
420238909Smm	struct stat st;
421238909Smm	struct archive *a;
422238909Smm	struct archive_entry *ae;
423238909Smm	int i, n;
424238909Smm	acl_t acl;
425238909Smm
426238909Smm	/*
427238909Smm	 * First, do a quick manual set/read of ACL data to
428238909Smm	 * verify that the local filesystem does support ACLs.
429238909Smm	 * If it doesn't, we'll simply skip the remaining tests.
430238909Smm	 */
431238909Smm	acl = acl_from_text("owner@:rwxp::allow,group@:rwp:f:allow");
432238909Smm	assert((void *)acl != NULL);
433238909Smm	/* Create a test dir and try to set an ACL on it. */
434238909Smm	if (!assertMakeDir("pretest", 0755)) {
435238909Smm		acl_free(acl);
436238909Smm		return;
437238909Smm	}
438238909Smm
439238909Smm	n = acl_set_file("pretest", ACL_TYPE_NFS4, acl);
440238909Smm	acl_free(acl);
441238909Smm	if (n != 0 && errno == EOPNOTSUPP) {
442238909Smm		skipping("NFS4 ACL tests require that NFS4 ACLs"
443238909Smm		    " be enabled on the filesystem");
444238909Smm		return;
445238909Smm	}
446238909Smm	if (n != 0 && errno == EINVAL) {
447238909Smm		skipping("This filesystem does not support NFS4 ACLs");
448238909Smm		return;
449238909Smm	}
450238909Smm	failure("acl_set_file(): errno = %d (%s)",
451238909Smm	    errno, strerror(errno));
452238909Smm	assertEqualInt(0, n);
453238909Smm
454238909Smm	/* Create a write-to-disk object. */
455238909Smm	assert(NULL != (a = archive_write_disk_new()));
456238909Smm	archive_write_disk_set_options(a,
457238909Smm	    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
458238909Smm
459238909Smm	/* Populate an archive entry with some metadata, including ACL info */
460238909Smm	ae = archive_entry_new();
461238909Smm	assert(ae != NULL);
462238909Smm	archive_entry_set_pathname(ae, "testall");
463238909Smm	archive_entry_set_filetype(ae, AE_IFREG);
464238909Smm	archive_entry_set_perm(ae, 0654);
465238909Smm	archive_entry_set_mtime(ae, 123456, 7890);
466238909Smm	archive_entry_set_size(ae, 0);
467238909Smm	set_acls(ae, acls_reg, 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
468238909Smm
469238909Smm	/* Write the entry to disk, including ACLs. */
470238909Smm	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
471238909Smm
472238909Smm	/* Likewise for a dir. */
473238909Smm	archive_entry_set_pathname(ae, "dirall");
474238909Smm	archive_entry_set_filetype(ae, AE_IFDIR);
475238909Smm	archive_entry_set_perm(ae, 0654);
476238909Smm	archive_entry_set_mtime(ae, 123456, 7890);
477238909Smm	set_acls(ae, acls_dir, 0, (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
478238909Smm	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
479238909Smm
480238909Smm	for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
481238909Smm	  sprintf(buff, "dir%d", i);
482238909Smm	  archive_entry_set_pathname(ae, buff);
483238909Smm	  archive_entry_set_filetype(ae, AE_IFDIR);
484238909Smm	  archive_entry_set_perm(ae, 0654);
485238909Smm	  archive_entry_set_mtime(ae, 123456 + i, 7891 + i);
486238909Smm	  set_acls(ae, acls_dir, i, i + 1);
487238909Smm	  assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
488238909Smm	}
489238909Smm
490238909Smm	archive_entry_free(ae);
491238909Smm
492238909Smm	/* Close the archive. */
493238909Smm	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
494238909Smm	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
495238909Smm
496238909Smm	/* Verify the data on disk. */
497238909Smm	assertEqualInt(0, stat("testall", &st));
498238909Smm	assertEqualInt(st.st_mtime, 123456);
499238909Smm	acl = acl_get_file("testall", ACL_TYPE_NFS4);
500238909Smm	assert(acl != (acl_t)NULL);
501238909Smm	compare_acls(acl, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
502238909Smm	acl_free(acl);
503238909Smm
504238909Smm	/* Verify single-permission dirs on disk. */
505238909Smm	for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
506238909Smm	  sprintf(buff, "dir%d", i);
507238909Smm	  assertEqualInt(0, stat(buff, &st));
508238909Smm	  assertEqualInt(st.st_mtime, 123456 + i);
509238909Smm	  acl = acl_get_file(buff, ACL_TYPE_NFS4);
510238909Smm	  assert(acl != (acl_t)NULL);
511238909Smm	  compare_acls(acl, acls_dir, buff, i, i + 1);
512238909Smm	  acl_free(acl);
513238909Smm	}
514238909Smm
515238909Smm	/* Verify "dirall" on disk. */
516238909Smm	assertEqualInt(0, stat("dirall", &st));
517238909Smm	assertEqualInt(st.st_mtime, 123456);
518238909Smm	acl = acl_get_file("dirall", ACL_TYPE_NFS4);
519238909Smm	assert(acl != (acl_t)NULL);
520238909Smm	compare_acls(acl, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
521238909Smm	acl_free(acl);
522238909Smm
523238909Smm	/* Read and compare ACL via archive_read_disk */
524238909Smm	a = archive_read_disk_new();
525238909Smm	assert(a != NULL);
526238909Smm	ae = archive_entry_new();
527238909Smm	assert(ae != NULL);
528238909Smm	archive_entry_set_pathname(ae, "testall");
529238909Smm	assertEqualInt(ARCHIVE_OK,
530238909Smm		       archive_read_disk_entry_from_file(a, ae, -1, NULL));
531238909Smm	compare_entry_acls(ae, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
532238909Smm	archive_entry_free(ae);
533238909Smm	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
534238909Smm
535238909Smm	/* Read and compare ACL via archive_read_disk */
536238909Smm	a = archive_read_disk_new();
537238909Smm	assert(a != NULL);
538238909Smm	ae = archive_entry_new();
539238909Smm	assert(ae != NULL);
540238909Smm	archive_entry_set_pathname(ae, "dirall");
541238909Smm	assertEqualInt(ARCHIVE_OK,
542238909Smm		       archive_read_disk_entry_from_file(a, ae, -1, NULL));
543238909Smm	compare_entry_acls(ae, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
544238909Smm	archive_entry_free(ae);
545238909Smm	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
546238909Smm#endif
547238909Smm}
548