1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * Copyright (c) 2012 Michihiro NAKAJIMA
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 #include "test.h"
27 __FBSDID("$FreeBSD$");
28 
29 #ifdef HAVE_SYS_XATTR_H
30 #include <sys/xattr.h>
31 #endif
32 
33 #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
34 	&& defined(HAVE_ZLIB_H)
35 static int
36 has_xattr(const char *filename, const char *xattrname)
37 {
38 	char *nl, *nlp;
39 	ssize_t r;
40 	int existing;
41 
42 	r = listxattr(filename, NULL, 0, XATTR_SHOWCOMPRESSION);
43 	if (r < 0)
44 		return (0);
45 	if (r == 0)
46 		return (0);
47 
48 	assert((nl = malloc(r)) != NULL);
49 	if (nl == NULL)
50 		return (0);
51 
52 	r = listxattr(filename, nl, r, XATTR_SHOWCOMPRESSION);
53 	if (r < 0) {
54 		free(nl);
55 		return (0);
56 	}
57 
58 	existing = 0;
59 	for (nlp = nl; nlp < nl + r; nlp += strlen(nlp) + 1) {
60 		if (strcmp(nlp, xattrname) == 0) {
61 			existing = 1;
62 			break;
63 		}
64 	}
65 	free(nl);
66 	return (existing);
67 }
68 static int
69 get_rsrc_footer(const char *filename, char *buff, size_t s)
70 {
71 	ssize_t r;
72 
73 	r = getxattr(filename, "com.apple.ResourceFork", NULL, 0, 0,
74 	    XATTR_SHOWCOMPRESSION);
75 	if (r < (ssize_t)s)
76 		return (-1);
77 	r = getxattr(filename, "com.apple.ResourceFork", buff, s,
78 	    r - s, XATTR_SHOWCOMPRESSION);
79 	if (r < (ssize_t)s)
80 		return (-1);
81 	return (0);
82 }
83 
84 #endif
85 
86 /*
87  * Exercise HFS+ Compression.
88  */
89 DEFINE_TEST(test_write_disk_hfs_compression)
90 {
91 #if !defined(__APPLE__) || !defined(UF_COMPRESSED) || !defined(HAVE_SYS_XATTR_H)\
92 	|| !defined(HAVE_ZLIB_H)
93 	skipping("MacOS-specific HFS+ Compression test");
94 #else
95 	const char *refname = "test_write_disk_hfs_compression.tgz";
96 	struct archive *ad, *a;
97 	struct archive_entry *ae;
98 	struct stat st;
99 	char rsrc[50];
100 	static const char rsrc_footer[50] = {
101 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 		0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c',  'm',
105 		'p', 'f',   0x00, 0x00, 0x00, 0x0a, 0x00, 0x01,
106 		0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 		0x00, 0x00
108 	};
109 
110 	extract_reference_file(refname);
111 
112 	/*
113 	 * Extract an archive to disk with HFS+ Compression.
114 	 */
115 	assert((ad = archive_write_disk_new()) != NULL);
116 	assertEqualIntA(ad, ARCHIVE_OK,
117 	    archive_write_disk_set_standard_lookup(ad));
118 	assertEqualIntA(ad, ARCHIVE_OK,
119 	    archive_write_disk_set_options(ad,
120 		ARCHIVE_EXTRACT_TIME |
121 		ARCHIVE_EXTRACT_SECURE_SYMLINKS |
122 		ARCHIVE_EXTRACT_SECURE_NODOTDOT |
123 		ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED));
124 
125 	assert((a = archive_read_new()) != NULL);
126 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
127 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
128 	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
129 	    refname, 512 * 20));
130 
131 	assertMakeDir("hfscmp", 0755);
132 	assertChdir("hfscmp");
133 
134 	/* Extract file1. */
135 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
136 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
137 	/* Extract README. */
138 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
139 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
140 	/* Extract NEWS. */
141 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
142 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
143 	/* Extract Makefile. */
144 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
145 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
146 
147 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
148 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
149 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
150 	assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
151 
152 	/* Test file1. */
153 	assertEqualInt(0, stat("file1", &st));
154 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
155 	assertFileSize("file1", 8);
156 	failure("'%s' should not have Resource Fork", "file1");
157 	assertEqualInt(0, has_xattr("file1", "com.apple.ResourceFork"));
158 	failure("'%s' should have decompfs xattr", "file1");
159 	assertEqualInt(1, has_xattr("file1", "com.apple.decmpfs"));
160 
161 	/* Test README. */
162 	assertEqualInt(0, stat("README", &st));
163 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
164 	assertFileSize("README", 6586);
165 	failure("'%s' should not have Resource Fork", "README");
166 	assertEqualInt(0, has_xattr("README", "com.apple.ResourceFork"));
167 	failure("'%s' should have decompfs xattr", "README");
168 	assertEqualInt(1, has_xattr("README", "com.apple.decmpfs"));
169 
170 	/* Test NEWS. */
171 	assertEqualInt(0, stat("NEWS", &st));
172 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
173 	assertFileSize("NEWS", 28438);
174 	failure("'%s' should have Resource Fork", "NEWS");
175 	assertEqualInt(1, has_xattr("NEWS", "com.apple.ResourceFork"));
176 	failure("'%s' should have decompfs xattr", "NEWS");
177 	assertEqualInt(1, has_xattr("NEWS", "com.apple.decmpfs"));
178 	assertEqualInt(0, get_rsrc_footer("NEWS", rsrc, sizeof(rsrc)));
179 	failure("Resource Fork should have consistent 50 bytes data");
180 	assertEqualMem(rsrc_footer, rsrc, sizeof(rsrc));
181 
182 	/* Test Makefile. */
183 	assertEqualInt(0, stat("Makefile", &st));
184 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
185 	assertFileSize("Makefile", 1264000);
186 	failure("'%s' should have Resource Fork", "Makefile");
187 	assertEqualInt(1, has_xattr("Makefile", "com.apple.ResourceFork"));
188 	failure("'%s' should have decompfs xattr", "Makefile");
189 	assertEqualInt(1, has_xattr("Makefile", "com.apple.decmpfs"));
190 	assertEqualInt(0, get_rsrc_footer("Makefile", rsrc, sizeof(rsrc)));
191 	failure("Resource Fork should have consistent 50 bytes data");
192 	assertEqualMem(rsrc_footer, rsrc, sizeof(rsrc));
193 
194 	assertChdir("..");
195 
196 	/*
197 	 * Extract an archive to disk without HFS+ Compression.
198 	 */
199 	assert((ad = archive_write_disk_new()) != NULL);
200 	assertEqualIntA(ad, ARCHIVE_OK,
201 	    archive_write_disk_set_standard_lookup(ad));
202 	assertEqualIntA(ad, ARCHIVE_OK,
203 	    archive_write_disk_set_options(ad,
204 		ARCHIVE_EXTRACT_TIME |
205 		ARCHIVE_EXTRACT_SECURE_SYMLINKS |
206 		ARCHIVE_EXTRACT_SECURE_NODOTDOT));
207 
208 	assert((a = archive_read_new()) != NULL);
209 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
210 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
211 	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
212 	    refname, 512 * 20));
213 
214 	assertMakeDir("nocmp", 0755);
215 	assertChdir("nocmp");
216 
217 	/* Extract file1. */
218 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
219 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
220 	/* Extract README. */
221 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
222 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
223 	/* Extract NEWS. */
224 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
225 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
226 	/* Extract Makefile. */
227 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
228 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
229 
230 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
231 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
232 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
233 	assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
234 
235 	/* Test file1. */
236 	assertEqualInt(0, stat("file1", &st));
237 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
238 	assertFileSize("file1", 8);
239 	failure("'%s' should not have Resource Fork", "file1");
240 	assertEqualInt(0, has_xattr("file1", "com.apple.ResourceFork"));
241 	failure("'%s' should not have decmpfs", "file1");
242 	assertEqualInt(0, has_xattr("file1", "com.apple.decmpfs"));
243 
244 	/* Test README. */
245 	assertEqualInt(0, stat("README", &st));
246 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
247 	assertFileSize("README", 6586);
248 	failure("'%s' should not have Resource Fork", "README");
249 	assertEqualInt(0, has_xattr("README", "com.apple.ResourceFork"));
250 	failure("'%s' should not have decmpfs", "README");
251 	assertEqualInt(0, has_xattr("README", "com.apple.decmpfs"));
252 
253 	/* Test NEWS. */
254 	assertEqualInt(0, stat("NEWS", &st));
255 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
256 	assertFileSize("NEWS", 28438);
257 	failure("'%s' should not have Resource Fork", "NEWS");
258 	assertEqualInt(0, has_xattr("NEWS", "com.apple.ResourceFork"));
259 	failure("'%s' should not have decmpfs", "NEWS");
260 	assertEqualInt(0, has_xattr("NEWS", "com.apple.decmpfs"));
261 
262 	/* Test Makefile. */
263 	assertEqualInt(0, stat("Makefile", &st));
264 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
265 	assertFileSize("Makefile", 1264000);
266 	failure("'%s' should not have Resource Fork", "Makefile");
267 	assertEqualInt(0, has_xattr("Makefile", "com.apple.ResourceFork"));
268 	failure("'%s' should not have decmpfs", "Makefile");
269 	assertEqualInt(0, has_xattr("Makefile", "com.apple.decmpfs"));
270 
271 	assertChdir("..");
272 
273 	assertEqualFile("hfscmp/file1", "nocmp/file1");
274 	assertEqualFile("hfscmp/README", "nocmp/README");
275 	assertEqualFile("hfscmp/NEWS", "nocmp/NEWS");
276 	assertEqualFile("hfscmp/Makefile", "nocmp/Makefile");
277 #endif
278 }
279