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