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 
28 #ifdef HAVE_SYS_XATTR_H
29 #include <sys/xattr.h>
30 #endif
31 
32 #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
33 	&& defined(HAVE_ZLIB_H)
34 static int
35 has_xattr(const char *filename, const char *xattrname)
36 {
37 	char *nl, *nlp;
38 	ssize_t r;
39 	int existing;
40 
41 	r = listxattr(filename, NULL, 0, XATTR_SHOWCOMPRESSION);
42 	if (r < 0)
43 		return (0);
44 	if (r == 0)
45 		return (0);
46 
47 	assert((nl = malloc(r)) != NULL);
48 	if (nl == NULL)
49 		return (0);
50 
51 	r = listxattr(filename, nl, r, XATTR_SHOWCOMPRESSION);
52 	if (r < 0) {
53 		free(nl);
54 		return (0);
55 	}
56 
57 	existing = 0;
58 	for (nlp = nl; nlp < nl + r; nlp += strlen(nlp) + 1) {
59 		if (strcmp(nlp, xattrname) == 0) {
60 			existing = 1;
61 			break;
62 		}
63 	}
64 	free(nl);
65 	return (existing);
66 }
67 static int
68 get_rsrc_footer(const char *filename, char *buff, size_t s)
69 {
70 	ssize_t r;
71 
72 	r = getxattr(filename, "com.apple.ResourceFork", NULL, 0, 0,
73 	    XATTR_SHOWCOMPRESSION);
74 	if (r < (ssize_t)s)
75 		return (-1);
76 	r = getxattr(filename, "com.apple.ResourceFork", buff, s,
77 	    r - s, XATTR_SHOWCOMPRESSION);
78 	if (r < (ssize_t)s)
79 		return (-1);
80 	return (0);
81 }
82 
83 #endif
84 
85 /*
86  * Exercise HFS+ Compression.
87  */
88 DEFINE_TEST(test_write_disk_hfs_compression)
89 {
90 #if !defined(__APPLE__) || !defined(UF_COMPRESSED) || !defined(HAVE_SYS_XATTR_H)\
91 	|| !defined(HAVE_ZLIB_H)
92 	skipping("MacOS-specific HFS+ Compression test");
93 #else
94 	const char *refname = "test_write_disk_hfs_compression.tgz";
95 	struct archive *ad, *a;
96 	struct archive_entry *ae;
97 	struct stat st;
98 	char rsrc[50];
99 	static const char rsrc_footer[50] = {
100 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 		0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c',  'm',
104 		'p', 'f',   0x00, 0x00, 0x00, 0x0a, 0x00, 0x01,
105 		0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 		0x00, 0x00
107 	};
108 
109 	extract_reference_file(refname);
110 
111 	/*
112 	 * Extract an archive to disk with HFS+ Compression.
113 	 */
114 	assert((ad = archive_write_disk_new()) != NULL);
115 	assertEqualIntA(ad, ARCHIVE_OK,
116 	    archive_write_disk_set_standard_lookup(ad));
117 	assertEqualIntA(ad, ARCHIVE_OK,
118 	    archive_write_disk_set_options(ad,
119 		ARCHIVE_EXTRACT_TIME |
120 		ARCHIVE_EXTRACT_SECURE_SYMLINKS |
121 		ARCHIVE_EXTRACT_SECURE_NODOTDOT |
122 		ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED));
123 
124 	assert((a = archive_read_new()) != NULL);
125 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
126 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
127 	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
128 	    refname, 512 * 20));
129 
130 	assertMakeDir("hfscmp", 0755);
131 	assertChdir("hfscmp");
132 
133 	/* Extract file1. */
134 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
135 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
136 	/* Extract README. */
137 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
138 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
139 	/* Extract NEWS. */
140 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
141 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
142 	/* Extract Makefile. */
143 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
144 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
145 
146 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
147 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
148 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
149 	assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
150 
151 	/* Test file1. */
152 	assertEqualInt(0, stat("file1", &st));
153 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
154 	assertFileSize("file1", 8);
155 	failure("'%s' should not have Resource Fork", "file1");
156 	assertEqualInt(0, has_xattr("file1", "com.apple.ResourceFork"));
157 	failure("'%s' should have decompfs xattr", "file1");
158 	assertEqualInt(1, has_xattr("file1", "com.apple.decmpfs"));
159 
160 	/* Test README. */
161 	assertEqualInt(0, stat("README", &st));
162 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
163 	assertFileSize("README", 6586);
164 	failure("'%s' should not have Resource Fork", "README");
165 	assertEqualInt(0, has_xattr("README", "com.apple.ResourceFork"));
166 	failure("'%s' should have decompfs xattr", "README");
167 	assertEqualInt(1, has_xattr("README", "com.apple.decmpfs"));
168 
169 	/* Test NEWS. */
170 	assertEqualInt(0, stat("NEWS", &st));
171 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
172 	assertFileSize("NEWS", 28438);
173 	failure("'%s' should have Resource Fork", "NEWS");
174 	assertEqualInt(1, has_xattr("NEWS", "com.apple.ResourceFork"));
175 	failure("'%s' should have decompfs xattr", "NEWS");
176 	assertEqualInt(1, has_xattr("NEWS", "com.apple.decmpfs"));
177 	assertEqualInt(0, get_rsrc_footer("NEWS", rsrc, sizeof(rsrc)));
178 	failure("Resource Fork should have consistent 50 bytes data");
179 	assertEqualMem(rsrc_footer, rsrc, sizeof(rsrc));
180 
181 	/* Test Makefile. */
182 	assertEqualInt(0, stat("Makefile", &st));
183 	assertEqualInt(UF_COMPRESSED, st.st_flags & UF_COMPRESSED);
184 	assertFileSize("Makefile", 1264000);
185 	failure("'%s' should have Resource Fork", "Makefile");
186 	assertEqualInt(1, has_xattr("Makefile", "com.apple.ResourceFork"));
187 	failure("'%s' should have decompfs xattr", "Makefile");
188 	assertEqualInt(1, has_xattr("Makefile", "com.apple.decmpfs"));
189 	assertEqualInt(0, get_rsrc_footer("Makefile", rsrc, sizeof(rsrc)));
190 	failure("Resource Fork should have consistent 50 bytes data");
191 	assertEqualMem(rsrc_footer, rsrc, sizeof(rsrc));
192 
193 	assertChdir("..");
194 
195 	/*
196 	 * Extract an archive to disk without HFS+ Compression.
197 	 */
198 	assert((ad = archive_write_disk_new()) != NULL);
199 	assertEqualIntA(ad, ARCHIVE_OK,
200 	    archive_write_disk_set_standard_lookup(ad));
201 	assertEqualIntA(ad, ARCHIVE_OK,
202 	    archive_write_disk_set_options(ad,
203 		ARCHIVE_EXTRACT_TIME |
204 		ARCHIVE_EXTRACT_SECURE_SYMLINKS |
205 		ARCHIVE_EXTRACT_SECURE_NODOTDOT));
206 
207 	assert((a = archive_read_new()) != NULL);
208 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
209 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
210 	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
211 	    refname, 512 * 20));
212 
213 	assertMakeDir("nocmp", 0755);
214 	assertChdir("nocmp");
215 
216 	/* Extract file1. */
217 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
218 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
219 	/* Extract README. */
220 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
221 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
222 	/* Extract NEWS. */
223 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
224 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
225 	/* Extract Makefile. */
226 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
227 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
228 
229 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
230 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
231 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
232 	assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
233 
234 	/* Test file1. */
235 	assertEqualInt(0, stat("file1", &st));
236 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
237 	assertFileSize("file1", 8);
238 	failure("'%s' should not have Resource Fork", "file1");
239 	assertEqualInt(0, has_xattr("file1", "com.apple.ResourceFork"));
240 	failure("'%s' should not have decmpfs", "file1");
241 	assertEqualInt(0, has_xattr("file1", "com.apple.decmpfs"));
242 
243 	/* Test README. */
244 	assertEqualInt(0, stat("README", &st));
245 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
246 	assertFileSize("README", 6586);
247 	failure("'%s' should not have Resource Fork", "README");
248 	assertEqualInt(0, has_xattr("README", "com.apple.ResourceFork"));
249 	failure("'%s' should not have decmpfs", "README");
250 	assertEqualInt(0, has_xattr("README", "com.apple.decmpfs"));
251 
252 	/* Test NEWS. */
253 	assertEqualInt(0, stat("NEWS", &st));
254 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
255 	assertFileSize("NEWS", 28438);
256 	failure("'%s' should not have Resource Fork", "NEWS");
257 	assertEqualInt(0, has_xattr("NEWS", "com.apple.ResourceFork"));
258 	failure("'%s' should not have decmpfs", "NEWS");
259 	assertEqualInt(0, has_xattr("NEWS", "com.apple.decmpfs"));
260 
261 	/* Test Makefile. */
262 	assertEqualInt(0, stat("Makefile", &st));
263 	assertEqualInt(0, st.st_flags & UF_COMPRESSED);
264 	assertFileSize("Makefile", 1264000);
265 	failure("'%s' should not have Resource Fork", "Makefile");
266 	assertEqualInt(0, has_xattr("Makefile", "com.apple.ResourceFork"));
267 	failure("'%s' should not have decmpfs", "Makefile");
268 	assertEqualInt(0, has_xattr("Makefile", "com.apple.decmpfs"));
269 
270 	assertChdir("..");
271 
272 	assertEqualFile("hfscmp/file1", "nocmp/file1");
273 	assertEqualFile("hfscmp/README", "nocmp/README");
274 	assertEqualFile("hfscmp/NEWS", "nocmp/NEWS");
275 	assertEqualFile("hfscmp/Makefile", "nocmp/Makefile");
276 #endif
277 }
278