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: head/lib/libarchive/test/test_write_disk.c 201247 2009-12-30 05:59:21Z kientzle $");
27
28#if ARCHIVE_VERSION_NUMBER >= 1009000
29
30#define UMASK 022
31/*
32 * When comparing mode values, ignore high-order bits
33 * that are set on some OSes.  This should cover the bits
34 * we're interested in (standard mode bits + file type bits)
35 * while ignoring extra markers such as Haiku/BeOS index
36 * flags.
37 */
38#define MODE_MASK 0777777
39
40static void create(struct archive_entry *ae, const char *msg)
41{
42	struct archive *ad;
43	struct stat st;
44
45	/* Write the entry to disk. */
46	assert((ad = archive_write_disk_new()) != NULL);
47	failure("%s", msg);
48	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
49	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
50#if ARCHIVE_VERSION_NUMBER < 2000000
51	archive_write_finish(ad);
52#else
53	assertEqualInt(0, archive_write_finish(ad));
54#endif
55	/* Test the entries on disk. */
56	assert(0 == stat(archive_entry_pathname(ae), &st));
57	failure("%s", msg);
58
59#if !defined(_WIN32) || defined(__CYGWIN__)
60	/* When verifying a dir, ignore the S_ISGID bit, as some systems set
61	 * that automatically. */
62	if (archive_entry_filetype(ae) == AE_IFDIR)
63		st.st_mode &= ~S_ISGID;
64	assertEqualInt(st.st_mode & MODE_MASK,
65	    archive_entry_mode(ae) & ~UMASK & MODE_MASK);
66#endif
67}
68
69static void create_reg_file(struct archive_entry *ae, const char *msg)
70{
71	static const char data[]="abcdefghijklmnopqrstuvwxyz";
72	struct archive *ad;
73
74	/* Write the entry to disk. */
75	assert((ad = archive_write_disk_new()) != NULL);
76        archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME);
77	failure("%s", msg);
78	/*
79	 * A touchy API design issue: archive_write_data() does (as of
80	 * 2.4.12) enforce the entry size as a limit on the data
81	 * written to the file.  This was not enforced prior to
82	 * 2.4.12.  The change was prompted by the refined
83	 * hardlink-restore semantics introduced at that time.  In
84	 * short, libarchive needs to know whether a "hardlink entry"
85	 * is going to overwrite the contents so that it can know
86	 * whether or not to open the file for writing.  This implies
87	 * that there is a fundamental semantic difference between an
88	 * entry with a zero size and one with a non-zero size in the
89	 * case of hardlinks and treating the hardlink case
90	 * differently from the regular file case is just asking for
91	 * trouble.  So, a zero size must always mean that no data
92	 * will be accepted, which is consistent with the file size in
93	 * the entry being a maximum size.
94	 */
95	archive_entry_set_size(ae, sizeof(data));
96	archive_entry_set_mtime(ae, 123456789, 0);
97	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
98	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
99	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
100#if ARCHIVE_VERSION_NUMBER < 2000000
101	archive_write_finish(ad);
102#else
103	assertEqualInt(0, archive_write_finish(ad));
104#endif
105	/* Test the entries on disk. */
106	assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777);
107	assertFileSize(archive_entry_pathname(ae), sizeof(data));
108	/* test_write_disk_times has more detailed tests of this area. */
109	assertFileMtime(archive_entry_pathname(ae), 123456789, 0);
110        failure("No atime given, so atime should get set to current time");
111	assertFileAtimeRecent(archive_entry_pathname(ae));
112}
113
114static void create_reg_file2(struct archive_entry *ae, const char *msg)
115{
116	const int datasize = 100000;
117	char *data;
118	struct archive *ad;
119	int i;
120
121	data = malloc(datasize);
122	for (i = 0; i < datasize; i++)
123		data[i] = (char)(i % 256);
124
125	/* Write the entry to disk. */
126	assert((ad = archive_write_disk_new()) != NULL);
127	failure("%s", msg);
128	/*
129	 * See above for an explanation why this next call
130	 * is necessary.
131	 */
132	archive_entry_set_size(ae, datasize);
133	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
134	for (i = 0; i < datasize - 999; i += 1000) {
135		assertEqualIntA(ad, ARCHIVE_OK,
136		    archive_write_data_block(ad, data + i, 1000, i));
137	}
138	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
139	assertEqualInt(0, archive_write_finish(ad));
140
141	/* Test the entries on disk. */
142	assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777);
143	assertFileSize(archive_entry_pathname(ae), i);
144	assertFileContents(data, datasize, archive_entry_pathname(ae));
145	free(data);
146}
147
148static void create_reg_file3(struct archive_entry *ae, const char *msg)
149{
150	static const char data[]="abcdefghijklmnopqrstuvwxyz";
151	struct archive *ad;
152	struct stat st;
153
154	/* Write the entry to disk. */
155	assert((ad = archive_write_disk_new()) != NULL);
156	failure("%s", msg);
157	/* Set the size smaller than the data and verify the truncation. */
158	archive_entry_set_size(ae, 5);
159	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
160	assertEqualInt(5, archive_write_data(ad, data, sizeof(data)));
161	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
162#if ARCHIVE_VERSION_NUMBER < 2000000
163	archive_write_finish(ad);
164#else
165	assertEqualInt(0, archive_write_finish(ad));
166#endif
167	/* Test the entry on disk. */
168	assert(0 == stat(archive_entry_pathname(ae), &st));
169	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
170	    st.st_mode, archive_entry_mode(ae));
171#if !defined(_WIN32) || defined(__CYGWIN__)
172	assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
173#endif
174	assertEqualInt(st.st_size, 5);
175}
176
177
178static void create_reg_file4(struct archive_entry *ae, const char *msg)
179{
180	static const char data[]="abcdefghijklmnopqrstuvwxyz";
181	struct archive *ad;
182	struct stat st;
183
184	/* Write the entry to disk. */
185	assert((ad = archive_write_disk_new()) != NULL);
186	/* Leave the size unset.  The data should not be truncated. */
187	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
188	assertEqualInt(ARCHIVE_OK,
189	    archive_write_data_block(ad, data, sizeof(data), 0));
190	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
191#if ARCHIVE_VERSION_NUMBER < 2000000
192	archive_write_finish(ad);
193#else
194	assertEqualInt(0, archive_write_finish(ad));
195#endif
196	/* Test the entry on disk. */
197	assert(0 == stat(archive_entry_pathname(ae), &st));
198	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
199	    st.st_mode, archive_entry_mode(ae));
200#if !defined(_WIN32) || defined(__CYGWIN__)
201	assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
202#endif
203	failure(msg);
204	assertEqualInt(st.st_size, sizeof(data));
205}
206
207#if defined(_WIN32) && !defined(__CYGWIN__)
208static void create_reg_file_win(struct archive_entry *ae, const char *msg)
209{
210	static const char data[]="abcdefghijklmnopqrstuvwxyz";
211	struct archive *ad;
212	struct stat st;
213	char *p, *fname;
214	size_t l;
215
216	/* Write the entry to disk. */
217	assert((ad = archive_write_disk_new()) != NULL);
218        archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME);
219	failure("%s", msg);
220	archive_entry_set_size(ae, sizeof(data));
221	archive_entry_set_mtime(ae, 123456789, 0);
222	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
223	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
224	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
225#if ARCHIVE_VERSION_NUMBER < 2000000
226	archive_write_finish(ad);
227#else
228	assertEqualInt(0, archive_write_finish(ad));
229#endif
230	/* Test the entries on disk. */
231	l = strlen(archive_entry_pathname(ae));
232	fname = malloc(l + 1);
233	assert(NULL != fname);
234	strcpy(fname, archive_entry_pathname(ae));
235	/* Replace unusable characters in Windows to '_' */
236	for (p = fname; *p != '\0'; p++)
237		if (*p == ':' || *p == '*' || *p == '?' ||
238		    *p == '"' || *p == '<' || *p == '>' || *p == '|')
239			*p = '_';
240	assert(0 == stat(fname, &st));
241	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
242	    st.st_mode, archive_entry_mode(ae));
243	assertEqualInt(st.st_size, sizeof(data));
244}
245#endif /* _WIN32 && !__CYGWIN__ */
246#endif
247
248DEFINE_TEST(test_write_disk)
249{
250#if ARCHIVE_VERSION_NUMBER < 1009000
251	skipping("archive_write_disk interface");
252#else
253	struct archive_entry *ae;
254
255	/* Force the umask to something predictable. */
256	assertUmask(UMASK);
257
258	/* A regular file. */
259	assert((ae = archive_entry_new()) != NULL);
260	archive_entry_copy_pathname(ae, "file");
261	archive_entry_set_mode(ae, S_IFREG | 0755);
262	create_reg_file(ae, "Test creating a regular file");
263	archive_entry_free(ae);
264
265	/* Another regular file. */
266	assert((ae = archive_entry_new()) != NULL);
267	archive_entry_copy_pathname(ae, "file2");
268	archive_entry_set_mode(ae, S_IFREG | 0755);
269	create_reg_file2(ae, "Test creating another regular file");
270	archive_entry_free(ae);
271
272	/* A regular file with a size restriction */
273	assert((ae = archive_entry_new()) != NULL);
274	archive_entry_copy_pathname(ae, "file3");
275	archive_entry_set_mode(ae, S_IFREG | 0755);
276	create_reg_file3(ae, "Regular file with size restriction");
277	archive_entry_free(ae);
278
279	/* A regular file with an unspecified size */
280	assert((ae = archive_entry_new()) != NULL);
281	archive_entry_copy_pathname(ae, "file3");
282	archive_entry_set_mode(ae, S_IFREG | 0755);
283	create_reg_file4(ae, "Regular file with unspecified size");
284	archive_entry_free(ae);
285
286	/* A regular file over an existing file */
287	assert((ae = archive_entry_new()) != NULL);
288	archive_entry_copy_pathname(ae, "file");
289	archive_entry_set_mode(ae, S_IFREG | 0724);
290	create(ae, "Test creating a file over an existing file.");
291	archive_entry_free(ae);
292
293	/* A directory. */
294	assert((ae = archive_entry_new()) != NULL);
295	archive_entry_copy_pathname(ae, "dir");
296	archive_entry_set_mode(ae, S_IFDIR | 0555);
297	create(ae, "Test creating a regular dir.");
298	archive_entry_free(ae);
299
300	/* A directory over an existing file. */
301	assert((ae = archive_entry_new()) != NULL);
302	archive_entry_copy_pathname(ae, "file");
303	archive_entry_set_mode(ae, S_IFDIR | 0742);
304	create(ae, "Test creating a dir over an existing file.");
305	archive_entry_free(ae);
306
307	/* A file over an existing dir. */
308	assert((ae = archive_entry_new()) != NULL);
309	archive_entry_copy_pathname(ae, "file");
310	archive_entry_set_mode(ae, S_IFREG | 0744);
311	create(ae, "Test creating a file over an existing dir.");
312	archive_entry_free(ae);
313
314#if defined(_WIN32) && !defined(__CYGWIN__)
315	/* A file with unusable characters in its file name. */
316	assert((ae = archive_entry_new()) != NULL);
317	archive_entry_copy_pathname(ae, "f:i*l?e\"f<i>l|e");
318	archive_entry_set_mode(ae, S_IFREG | 0755);
319	create_reg_file_win(ae, "Test creating a regular file"
320	    " with unusable characters in its file name");
321	archive_entry_free(ae);
322
323	/* A file with unusable characters in its directory name. */
324	assert((ae = archive_entry_new()) != NULL);
325	archive_entry_copy_pathname(ae, "d:i*r?e\"c<t>o|ry/file1");
326	archive_entry_set_mode(ae, S_IFREG | 0755);
327	create_reg_file_win(ae, "Test creating a regular file"
328	    " with unusable characters in its file name");
329	archive_entry_free(ae);
330#endif /* _WIN32 && !__CYGWIN__ */
331#endif
332}
333