1/*-
2 * Copyright (c) 2003-2010 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$");
27
28#if defined(__FreeBSD__) && __FreeBSD__ >= 8
29#define _ACL_PRIVATE
30#include <sys/acl.h>
31
32struct myacl_t {
33	int type;
34	int permset;
35	int tag;
36	int qual; /* GID or UID of user/group, depending on tag. */
37	const char *name; /* Name of user/group, depending on tag. */
38};
39
40static struct myacl_t acls_reg[] = {
41	/* For this test, we need the file owner to be able to read and write the ACL. */
42	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
43	  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,
44	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
45
46	/* An entry for each type. */
47	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
48	  ARCHIVE_ENTRY_ACL_USER, 108, "user108" },
49	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE,
50	  ARCHIVE_ENTRY_ACL_USER, 109, "user109" },
51
52	/* An entry for each permission. */
53	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
54	  ARCHIVE_ENTRY_ACL_USER, 112, "user112" },
55	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA,
56	  ARCHIVE_ENTRY_ACL_USER, 113, "user113" },
57	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_DATA,
58	  ARCHIVE_ENTRY_ACL_USER, 115, "user115" },
59	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_APPEND_DATA,
60	  ARCHIVE_ENTRY_ACL_USER, 117, "user117" },
61	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
62	  ARCHIVE_ENTRY_ACL_USER, 119, "user119" },
63	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
64	  ARCHIVE_ENTRY_ACL_USER, 120, "user120" },
65	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
66	  ARCHIVE_ENTRY_ACL_USER, 122, "user122" },
67	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
68	  ARCHIVE_ENTRY_ACL_USER, 123, "user123" },
69	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
70	  ARCHIVE_ENTRY_ACL_USER, 124, "user124" },
71	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
72	  ARCHIVE_ENTRY_ACL_USER, 125, "user125" },
73	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
74	  ARCHIVE_ENTRY_ACL_USER, 126, "user126" },
75	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
76	  ARCHIVE_ENTRY_ACL_USER, 127, "user127" },
77	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
78	  ARCHIVE_ENTRY_ACL_USER, 128, "user128" },
79
80	/* One entry for each qualifier. */
81	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
82	  ARCHIVE_ENTRY_ACL_USER, 135, "user135" },
83//	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
84//	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
85	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
86	  ARCHIVE_ENTRY_ACL_GROUP, 136, "group136" },
87	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
88	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
89	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
90	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
91};
92
93
94static struct myacl_t acls_dir[] = {
95	/* For this test, we need to be able to read and write the ACL. */
96	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
97	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
98
99	/* An entry for each type. */
100	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
101	  ARCHIVE_ENTRY_ACL_USER, 101, "user101" },
102	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
103	  ARCHIVE_ENTRY_ACL_USER, 102, "user102" },
104
105	/* An entry for each permission. */
106	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
107	  ARCHIVE_ENTRY_ACL_USER, 201, "user201" },
108	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_FILE,
109	  ARCHIVE_ENTRY_ACL_USER, 202, "user202" },
110	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
111	  ARCHIVE_ENTRY_ACL_USER, 203, "user203" },
112	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
113	  ARCHIVE_ENTRY_ACL_USER, 204, "user204" },
114	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
115	  ARCHIVE_ENTRY_ACL_USER, 205, "user205" },
116	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE_CHILD,
117	  ARCHIVE_ENTRY_ACL_USER, 206, "user206" },
118	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
119	  ARCHIVE_ENTRY_ACL_USER, 207, "user207" },
120	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
121	  ARCHIVE_ENTRY_ACL_USER, 208, "user208" },
122	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
123	  ARCHIVE_ENTRY_ACL_USER, 209, "user209" },
124	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
125	  ARCHIVE_ENTRY_ACL_USER, 210, "user210" },
126	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
127	  ARCHIVE_ENTRY_ACL_USER, 211, "user211" },
128	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
129	  ARCHIVE_ENTRY_ACL_USER, 212, "user212" },
130	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
131	  ARCHIVE_ENTRY_ACL_USER, 213, "user213" },
132
133	/* One entry with each inheritance value. */
134	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
135	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT,
136	  ARCHIVE_ENTRY_ACL_USER, 301, "user301" },
137	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
138	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
139	  ARCHIVE_ENTRY_ACL_USER, 302, "user302" },
140#if 0
141	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
142	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
143	  ARCHIVE_ENTRY_ACL_USER, 303, "user303" },
144	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
145	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY,
146	  ARCHIVE_ENTRY_ACL_USER, 304, "user304" },
147#endif
148
149#if 0
150	/* FreeBSD does not support audit entries. */
151	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
152	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
153	  ARCHIVE_ENTRY_ACL_USER, 401, "user401" },
154	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
155	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
156	  ARCHIVE_ENTRY_ACL_USER, 402, "user402" },
157#endif
158
159	/* One entry for each qualifier. */
160	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
161	  ARCHIVE_ENTRY_ACL_USER, 501, "user501" },
162	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
163	  ARCHIVE_ENTRY_ACL_GROUP, 502, "group502" },
164	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
165	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
166	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
167	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
168};
169
170static void
171set_acls(struct archive_entry *ae, struct myacl_t *acls, int start, int end)
172{
173	int i;
174
175	archive_entry_acl_clear(ae);
176	if (start > 0) {
177		assertEqualInt(ARCHIVE_OK,
178			archive_entry_acl_add_entry(ae,
179			    acls[0].type, acls[0].permset, acls[0].tag,
180			    acls[0].qual, acls[0].name));
181	}
182	for (i = start; i < end; i++) {
183		assertEqualInt(ARCHIVE_OK,
184		    archive_entry_acl_add_entry(ae,
185			acls[i].type, acls[i].permset, acls[i].tag,
186			acls[i].qual, acls[i].name));
187	}
188}
189
190static int
191acl_permset_to_bitmap(acl_permset_t opaque_ps)
192{
193	static struct { int machine; int portable; } perms[] = {
194		{ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE},
195		{ACL_WRITE, ARCHIVE_ENTRY_ACL_WRITE},
196		{ACL_READ, ARCHIVE_ENTRY_ACL_READ},
197		{ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA},
198		{ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY},
199		{ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA},
200		{ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE},
201		{ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA},
202		{ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY},
203		{ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS},
204		{ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS},
205		{ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD},
206		{ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES},
207		{ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES},
208		{ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE},
209		{ACL_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL},
210		{ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL},
211		{ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER},
212		{ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE}
213	};
214	int i, permset = 0;
215
216	for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i)
217		if (acl_get_perm_np(opaque_ps, perms[i].machine))
218			permset |= perms[i].portable;
219	return permset;
220}
221
222static int
223acl_flagset_to_bitmap(acl_flagset_t opaque_fs)
224{
225	static struct { int machine; int portable; } flags[] = {
226		{ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT},
227		{ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT},
228		{ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT},
229		{ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY},
230	};
231	int i, flagset = 0;
232
233	for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); ++i)
234		if (acl_get_flag_np(opaque_fs, flags[i].machine))
235			flagset |= flags[i].portable;
236	return flagset;
237}
238
239static int
240acl_match(acl_entry_t aclent, struct myacl_t *myacl)
241{
242	gid_t g, *gp;
243	uid_t u, *up;
244	acl_tag_t tag_type;
245	acl_permset_t opaque_ps;
246	acl_flagset_t opaque_fs;
247	int perms;
248
249	acl_get_tag_type(aclent, &tag_type);
250
251	/* translate the silly opaque permset to a bitmap */
252	acl_get_permset(aclent, &opaque_ps);
253	acl_get_flagset_np(aclent, &opaque_fs);
254	perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs);
255	if (perms != myacl->permset)
256		return (0);
257
258	switch (tag_type) {
259	case ACL_USER_OBJ:
260		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
261		break;
262	case ACL_USER:
263		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
264			return (0);
265		up = acl_get_qualifier(aclent);
266		u = *up;
267		acl_free(up);
268		if ((uid_t)myacl->qual != u)
269			return (0);
270		break;
271	case ACL_GROUP_OBJ:
272		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
273		break;
274	case ACL_GROUP:
275		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
276			return (0);
277		gp = acl_get_qualifier(aclent);
278		g = *gp;
279		acl_free(gp);
280		if ((gid_t)myacl->qual != g)
281			return (0);
282		break;
283	case ACL_MASK:
284		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
285		break;
286	case ACL_EVERYONE:
287		if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0);
288		break;
289	}
290	return (1);
291}
292
293static void
294compare_acls(acl_t acl, struct myacl_t *myacls, const char *filename, int start, int end)
295{
296	int *marker;
297	int entry_id = ACL_FIRST_ENTRY;
298	int matched;
299	int i, n;
300	acl_entry_t acl_entry;
301
302	n = end - start;
303	marker = malloc(sizeof(marker[0]) * (n + 1));
304	for (i = 0; i < n; i++)
305		marker[i] = i + start;
306	/* Always include the first ACE. */
307	if (start > 0) {
308	  marker[n] = 0;
309	  ++n;
310	}
311
312	/*
313	 * Iterate over acls in system acl object, try to match each
314	 * one with an item in the myacls array.
315	 */
316	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
317		/* After the first time... */
318		entry_id = ACL_NEXT_ENTRY;
319
320		/* Search for a matching entry (tag and qualifier) */
321		for (i = 0, matched = 0; i < n && !matched; i++) {
322			if (acl_match(acl_entry, &myacls[marker[i]])) {
323				/* We found a match; remove it. */
324				marker[i] = marker[n - 1];
325				n--;
326				matched = 1;
327			}
328		}
329
330		failure("ACL entry on file %s that shouldn't be there", filename);
331		assert(matched == 1);
332	}
333
334	/* Dump entries in the myacls array that weren't in the system acl. */
335	for (i = 0; i < n; ++i) {
336		failure(" ACL entry %d missing from %s: "
337		    "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
338		    marker[i], filename,
339		    myacls[marker[i]].type, myacls[marker[i]].permset,
340		    myacls[marker[i]].tag, myacls[marker[i]].qual,
341		    myacls[marker[i]].name);
342		assert(0); /* Record this as a failure. */
343	}
344	free(marker);
345}
346
347static void
348compare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char *filename, int start, int end)
349{
350	int *marker;
351	int matched;
352	int i, n;
353	int type, permset, tag, qual;
354	const char *name;
355
356	/* Count ACL entries in myacls array and allocate an indirect array. */
357	n = end - start;
358	marker = malloc(sizeof(marker[0]) * (n + 1));
359	for (i = 0; i < n; i++)
360		marker[i] = i + start;
361	/* Always include the first ACE. */
362	if (start > 0) {
363	  marker[n] = 0;
364	  ++n;
365	}
366
367	/*
368	 * Iterate over acls in entry, try to match each
369	 * one with an item in the myacls array.
370	 */
371	assertEqualInt(n, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
372	while (ARCHIVE_OK == archive_entry_acl_next(ae,
373	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, &type, &permset, &tag, &qual, &name)) {
374
375		/* Search for a matching entry (tag and qualifier) */
376		for (i = 0, matched = 0; i < n && !matched; i++) {
377			if (tag == myacls[marker[i]].tag
378			    && qual == myacls[marker[i]].qual
379			    && permset == myacls[marker[i]].permset
380			    && type == myacls[marker[i]].type) {
381				/* We found a match; remove it. */
382				marker[i] = marker[n - 1];
383				n--;
384				matched = 1;
385			}
386		}
387
388		failure("ACL entry on file that shouldn't be there: "
389			"type=%d,permset=%x,tag=%d,qual=%d",
390			type,permset,tag,qual);
391		assert(matched == 1);
392	}
393
394	/* Dump entries in the myacls array that weren't in the system acl. */
395	for (i = 0; i < n; ++i) {
396		failure(" ACL entry %d missing from %s: "
397		    "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
398		    marker[i], filename,
399		    myacls[marker[i]].type, myacls[marker[i]].permset,
400		    myacls[marker[i]].tag, myacls[marker[i]].qual,
401		    myacls[marker[i]].name);
402		assert(0); /* Record this as a failure. */
403	}
404	free(marker);
405}
406#endif
407
408/*
409 * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
410 */
411
412DEFINE_TEST(test_acl_freebsd_nfs4)
413{
414#if !defined(__FreeBSD__)
415	skipping("FreeBSD-specific NFS4 ACL restore test");
416#elif __FreeBSD__ < 8
417	skipping("NFS4 ACLs supported only on FreeBSD 8.0 and later");
418#else
419	char buff[64];
420	struct stat st;
421	struct archive *a;
422	struct archive_entry *ae;
423	int i, n;
424	acl_t acl;
425
426	/*
427	 * First, do a quick manual set/read of ACL data to
428	 * verify that the local filesystem does support ACLs.
429	 * If it doesn't, we'll simply skip the remaining tests.
430	 */
431	acl = acl_from_text("owner@:rwxp::allow,group@:rwp:f:allow");
432	assert((void *)acl != NULL);
433	/* Create a test dir and try to set an ACL on it. */
434	if (!assertMakeDir("pretest", 0755)) {
435		acl_free(acl);
436		return;
437	}
438
439	n = acl_set_file("pretest", ACL_TYPE_NFS4, acl);
440	acl_free(acl);
441	if (n != 0 && errno == EOPNOTSUPP) {
442		skipping("NFS4 ACL tests require that NFS4 ACLs"
443		    " be enabled on the filesystem");
444		return;
445	}
446	if (n != 0 && errno == EINVAL) {
447		skipping("This filesystem does not support NFS4 ACLs");
448		return;
449	}
450	failure("acl_set_file(): errno = %d (%s)",
451	    errno, strerror(errno));
452	assertEqualInt(0, n);
453
454	/* Create a write-to-disk object. */
455	assert(NULL != (a = archive_write_disk_new()));
456	archive_write_disk_set_options(a,
457	    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
458
459	/* Populate an archive entry with some metadata, including ACL info */
460	ae = archive_entry_new();
461	assert(ae != NULL);
462	archive_entry_set_pathname(ae, "testall");
463	archive_entry_set_filetype(ae, AE_IFREG);
464	archive_entry_set_perm(ae, 0654);
465	archive_entry_set_mtime(ae, 123456, 7890);
466	archive_entry_set_size(ae, 0);
467	set_acls(ae, acls_reg, 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
468
469	/* Write the entry to disk, including ACLs. */
470	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
471
472	/* Likewise for a dir. */
473	archive_entry_set_pathname(ae, "dirall");
474	archive_entry_set_filetype(ae, AE_IFDIR);
475	archive_entry_set_perm(ae, 0654);
476	archive_entry_set_mtime(ae, 123456, 7890);
477	set_acls(ae, acls_dir, 0, (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
478	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
479
480	for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
481	  sprintf(buff, "dir%d", i);
482	  archive_entry_set_pathname(ae, buff);
483	  archive_entry_set_filetype(ae, AE_IFDIR);
484	  archive_entry_set_perm(ae, 0654);
485	  archive_entry_set_mtime(ae, 123456 + i, 7891 + i);
486	  set_acls(ae, acls_dir, i, i + 1);
487	  assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
488	}
489
490	archive_entry_free(ae);
491
492	/* Close the archive. */
493	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
494	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
495
496	/* Verify the data on disk. */
497	assertEqualInt(0, stat("testall", &st));
498	assertEqualInt(st.st_mtime, 123456);
499	acl = acl_get_file("testall", ACL_TYPE_NFS4);
500	assert(acl != (acl_t)NULL);
501	compare_acls(acl, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
502	acl_free(acl);
503
504	/* Verify single-permission dirs on disk. */
505	for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
506	  sprintf(buff, "dir%d", i);
507	  assertEqualInt(0, stat(buff, &st));
508	  assertEqualInt(st.st_mtime, 123456 + i);
509	  acl = acl_get_file(buff, ACL_TYPE_NFS4);
510	  assert(acl != (acl_t)NULL);
511	  compare_acls(acl, acls_dir, buff, i, i + 1);
512	  acl_free(acl);
513	}
514
515	/* Verify "dirall" on disk. */
516	assertEqualInt(0, stat("dirall", &st));
517	assertEqualInt(st.st_mtime, 123456);
518	acl = acl_get_file("dirall", ACL_TYPE_NFS4);
519	assert(acl != (acl_t)NULL);
520	compare_acls(acl, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
521	acl_free(acl);
522
523	/* Read and compare ACL via archive_read_disk */
524	a = archive_read_disk_new();
525	assert(a != NULL);
526	ae = archive_entry_new();
527	assert(ae != NULL);
528	archive_entry_set_pathname(ae, "testall");
529	assertEqualInt(ARCHIVE_OK,
530		       archive_read_disk_entry_from_file(a, ae, -1, NULL));
531	compare_entry_acls(ae, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
532	archive_entry_free(ae);
533	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
534
535	/* Read and compare ACL via archive_read_disk */
536	a = archive_read_disk_new();
537	assert(a != NULL);
538	ae = archive_entry_new();
539	assert(ae != NULL);
540	archive_entry_set_pathname(ae, "dirall");
541	assertEqualInt(ARCHIVE_OK,
542		       archive_read_disk_entry_from_file(a, ae, -1, NULL));
543	compare_entry_acls(ae, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
544	archive_entry_free(ae);
545	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
546#endif
547}
548