1/*-
2 * Copyright (c) 2003-2007 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/*
29 * Write a file using archive_write_data call, read the file
30 * back and verify the contents.  The data written includes large
31 * blocks of nulls, so it should exercise the sparsification logic
32 * if ARCHIVE_EXTRACT_SPARSE is enabled.
33 */
34static void
35verify_write_data(struct archive *a, int sparse)
36{
37	static const char data[]="abcdefghijklmnopqrstuvwxyz";
38	struct stat st;
39	struct archive_entry *ae;
40	size_t buff_size = 64 * 1024;
41	char *buff, *p;
42	const char *msg = sparse ? "sparse" : "non-sparse";
43	FILE *f;
44
45	buff = malloc(buff_size);
46	assert(buff != NULL);
47	if (buff == NULL)
48		return;
49
50	ae = archive_entry_new();
51	assert(ae != NULL);
52	archive_entry_set_size(ae, 8 * buff_size);
53	archive_entry_set_pathname(ae, "test_write_data");
54	archive_entry_set_mode(ae, AE_IFREG | 0755);
55	assertEqualIntA(a, 0, archive_write_header(a, ae));
56
57	/* Use archive_write_data() to write three relatively sparse blocks. */
58
59	/* First has non-null data at beginning. */
60	memset(buff, 0, buff_size);
61	memcpy(buff, data, sizeof(data));
62	failure("%s", msg);
63	assertEqualInt(buff_size, archive_write_data(a, buff, buff_size));
64
65	/* Second has non-null data in the middle. */
66	memset(buff, 0, buff_size);
67	memcpy(buff + buff_size / 2 - 3, data, sizeof(data));
68	failure("%s", msg);
69	assertEqualInt(buff_size, archive_write_data(a, buff, buff_size));
70
71	/* Third has non-null data at the end. */
72	memset(buff, 0, buff_size);
73	memcpy(buff + buff_size - sizeof(data), data, sizeof(data));
74	failure("%s", msg);
75	assertEqualInt(buff_size, archive_write_data(a, buff, buff_size));
76
77	failure("%s", msg);
78	assertEqualIntA(a, 0, archive_write_finish_entry(a));
79
80	/* Test the entry on disk. */
81	assert(0 == stat(archive_entry_pathname(ae), &st));
82        assertEqualInt(st.st_size, 8 * buff_size);
83	f = fopen(archive_entry_pathname(ae), "rb");
84	assert(f != NULL);
85	if (f == NULL) {
86		free(buff);
87		return;
88	}
89
90	/* Check first block. */
91	assertEqualInt(buff_size, fread(buff, 1, buff_size, f));
92	failure("%s", msg);
93	assertEqualMem(buff, data, sizeof(data));
94	for (p = buff + sizeof(data); p < buff + buff_size; ++p) {
95		failure("offset: %d, %s", (int)(p - buff), msg);
96		if (!assertEqualInt(0, *p))
97			break;
98	}
99
100	/* Check second block. */
101	assertEqualInt(buff_size, fread(buff, 1, buff_size, f));
102	for (p = buff; p < buff + buff_size; ++p) {
103		failure("offset: %d, %s", (int)(p - buff), msg);
104		if (p == buff + buff_size / 2 - 3) {
105			assertEqualMem(p, data, sizeof(data));
106			p += sizeof(data);
107		} else if (!assertEqualInt(0, *p))
108			break;
109	}
110
111	/* Check third block. */
112	assertEqualInt(buff_size, fread(buff, 1, buff_size, f));
113	for (p = buff; p < buff + buff_size - sizeof(data); ++p) {
114		failure("offset: %d, %s", (int)(p - buff), msg);
115		if (!assertEqualInt(0, *p))
116			break;
117	}
118	failure("%s", msg);
119	assertEqualMem(buff + buff_size - sizeof(data), data, sizeof(data));
120
121	/* XXX more XXX */
122
123	assertEqualInt(0, fclose(f));
124	archive_entry_free(ae);
125	free(buff);
126}
127
128/*
129 * As above, but using the archive_write_data_block() call.
130 */
131static void
132verify_write_data_block(struct archive *a, int sparse)
133{
134	static const char data[]="abcdefghijklmnopqrstuvwxyz";
135	struct stat st;
136	struct archive_entry *ae;
137	size_t buff_size = 64 * 1024;
138	char *buff, *p;
139	const char *msg = sparse ? "sparse" : "non-sparse";
140	FILE *f;
141
142	buff = malloc(buff_size);
143	assert(buff != NULL);
144	if (buff == NULL)
145		return;
146
147	ae = archive_entry_new();
148	assert(ae != NULL);
149	archive_entry_set_size(ae, 8 * buff_size);
150	archive_entry_set_pathname(ae, "test_write_data_block");
151	archive_entry_set_mode(ae, AE_IFREG | 0755);
152	assertEqualIntA(a, 0, archive_write_header(a, ae));
153
154	/* Use archive_write_data_block() to write three
155	   relatively sparse blocks. */
156
157	/* First has non-null data at beginning. */
158	memset(buff, 0, buff_size);
159	memcpy(buff, data, sizeof(data));
160	failure("%s", msg);
161	assertEqualInt(ARCHIVE_OK,
162	    archive_write_data_block(a, buff, buff_size, 100));
163
164	/* Second has non-null data in the middle. */
165	memset(buff, 0, buff_size);
166	memcpy(buff + buff_size / 2 - 3, data, sizeof(data));
167	failure("%s", msg);
168	assertEqualInt(ARCHIVE_OK,
169	    archive_write_data_block(a, buff, buff_size, buff_size + 200));
170
171	/* Third has non-null data at the end. */
172	memset(buff, 0, buff_size);
173	memcpy(buff + buff_size - sizeof(data), data, sizeof(data));
174	failure("%s", msg);
175	assertEqualInt(ARCHIVE_OK,
176	    archive_write_data_block(a, buff, buff_size, buff_size * 2 + 300));
177
178	failure("%s", msg);
179	assertEqualIntA(a, 0, archive_write_finish_entry(a));
180
181	/* Test the entry on disk. */
182	assert(0 == stat(archive_entry_pathname(ae), &st));
183        assertEqualInt(st.st_size, 8 * buff_size);
184	f = fopen(archive_entry_pathname(ae), "rb");
185	assert(f != NULL);
186	if (f == NULL) {
187		free(buff);
188		return;
189	}
190
191	/* Check 100-byte gap at beginning */
192	assertEqualInt(100, fread(buff, 1, 100, f));
193	failure("%s", msg);
194	for (p = buff; p < buff + 100; ++p) {
195		failure("offset: %d, %s", (int)(p - buff), msg);
196		if (!assertEqualInt(0, *p))
197			break;
198	}
199
200	/* Check first block. */
201	assertEqualInt(buff_size, fread(buff, 1, buff_size, f));
202	failure("%s", msg);
203	assertEqualMem(buff, data, sizeof(data));
204	for (p = buff + sizeof(data); p < buff + buff_size; ++p) {
205		failure("offset: %d, %s", (int)(p - buff), msg);
206		if (!assertEqualInt(0, *p))
207			break;
208	}
209
210	/* Check 100-byte gap */
211	assertEqualInt(100, fread(buff, 1, 100, f));
212	failure("%s", msg);
213	for (p = buff; p < buff + 100; ++p) {
214		failure("offset: %d, %s", (int)(p - buff), msg);
215		if (!assertEqualInt(0, *p))
216			break;
217	}
218
219	/* Check second block. */
220	assertEqualInt(buff_size, fread(buff, 1, buff_size, f));
221	for (p = buff; p < buff + buff_size; ++p) {
222		failure("offset: %d, %s", (int)(p - buff), msg);
223		if (p == buff + buff_size / 2 - 3) {
224			assertEqualMem(p, data, sizeof(data));
225			p += sizeof(data);
226		} else if (!assertEqualInt(0, *p))
227			break;
228	}
229
230	/* Check 100-byte gap */
231	assertEqualInt(100, fread(buff, 1, 100, f));
232	failure("%s", msg);
233	for (p = buff; p < buff + 100; ++p) {
234		failure("offset: %d, %s", (int)(p - buff), msg);
235		if (!assertEqualInt(0, *p))
236			break;
237	}
238
239	/* Check third block. */
240	assertEqualInt(buff_size, fread(buff, 1, buff_size, f));
241	for (p = buff; p < buff + buff_size - sizeof(data); ++p) {
242		failure("offset: %d, %s", (int)(p - buff), msg);
243		if (!assertEqualInt(0, *p))
244			break;
245	}
246	failure("%s", msg);
247	assertEqualMem(buff + buff_size - sizeof(data), data, sizeof(data));
248
249	/* Check another block size beyond last we wrote. */
250	assertEqualInt(buff_size, fread(buff, 1, buff_size, f));
251	failure("%s", msg);
252	for (p = buff; p < buff + buff_size; ++p) {
253		failure("offset: %d, %s", (int)(p - buff), msg);
254		if (!assertEqualInt(0, *p))
255			break;
256	}
257
258
259	/* XXX more XXX */
260
261	assertEqualInt(0, fclose(f));
262	free(buff);
263	archive_entry_free(ae);
264}
265
266DEFINE_TEST(test_write_disk_sparse)
267{
268	struct archive *ad;
269
270
271	/*
272	 * The return values, etc, of the write data functions
273	 * shouldn't change regardless of whether we've requested
274	 * sparsification.  (The performance and pattern of actual
275	 * write calls to the disk should vary, of course, but the
276	 * client program shouldn't see any difference.)
277	 */
278	assert((ad = archive_write_disk_new()) != NULL);
279        archive_write_disk_set_options(ad, 0);
280	verify_write_data(ad, 0);
281	verify_write_data_block(ad, 0);
282	assertEqualInt(0, archive_write_free(ad));
283
284	assert((ad = archive_write_disk_new()) != NULL);
285        archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_SPARSE);
286	verify_write_data(ad, 1);
287	verify_write_data_block(ad, 1);
288	assertEqualInt(0, archive_write_free(ad));
289
290}
291