1228753Smm/*-
2228753Smm * Copyright (c) 2003-2007 Tim Kientzle
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24228753Smm */
25228753Smm#include "test.h"
26228763Smm__FBSDID("$FreeBSD: stable/11/contrib/libarchive/tar/test/test_copy.c 358088 2020-02-19 01:50:47Z mm $");
27228753Smm
28228753Smm#if defined(__CYGWIN__)
29228753Smm# include <limits.h>
30228753Smm# include <sys/cygwin.h>
31228753Smm#endif
32248616Smm#if defined(_WIN32) && !defined(__CYGWIN__)
33248616Smm# include <direct.h>
34248616Smm#endif
35228753Smm
36228753Smm/*
37228753Smm * Try to figure out how deep we can go in our tests.  Assumes that
38228753Smm * the first call to this function has the longest starting cwd (which
39228753Smm * is currently "<testdir>/original").  This is mostly to work around
40228753Smm * limits in our Win32 support.
41228753Smm *
42228753Smm * Background: On Posix systems, PATH_MAX is merely a limit on the
43228753Smm * length of the string passed into a system call.  By repeatedly
44228753Smm * calling chdir(), you can work with arbitrarily long paths on such
45228753Smm * systems.  In contrast, Win32 APIs apply PATH_MAX limits to the full
46228753Smm * absolute path, so the permissible length of a system call argument
47228753Smm * varies with the cwd. Some APIs actually enforce limits
48228753Smm * significantly less than PATH_MAX to ensure that you can create
49228753Smm * files within the current working directory.  The Win32 limits also
50228753Smm * apply to Cygwin before 1.7.
51228753Smm *
52228753Smm * Someday, I want to convert the Win32 support to use newer
53228753Smm * wide-character paths with '\\?\' prefix, which has a 32k PATH_MAX
54228753Smm * instead of the rather anemic 260 character limit of the older
55228753Smm * system calls.  Then we can drop this mess (unless we want to
56228753Smm * continue to special-case Cygwin 1.5 and earlier).
57228753Smm */
58228753Smmstatic int
59228753Smmcompute_loop_max(void)
60228753Smm{
61228753Smm#if defined(_WIN32) && !defined(__CYGWIN__)
62228753Smm	static int LOOP_MAX = 0;
63228753Smm	char buf[MAX_PATH];
64228753Smm	size_t cwdlen;
65228753Smm
66228753Smm	if (LOOP_MAX == 0) {
67228753Smm		assert(_getcwd(buf, MAX_PATH) != NULL);
68228753Smm		cwdlen = strlen(buf);
69228753Smm		/* 12 characters = length of 8.3 filename */
70228753Smm		/* 4 characters = length of "/../" used in symlink tests */
71228753Smm		/* 1 character = length of extra "/" separator */
72228753Smm		LOOP_MAX = MAX_PATH - (int)cwdlen - 12 - 4 - 1;
73228753Smm	}
74228753Smm	return LOOP_MAX;
75228753Smm#elif defined(__CYGWIN__) && !defined(HAVE_CYGWIN_CONV_PATH)
76228753Smm	static int LOOP_MAX = 0;
77228753Smm	if (LOOP_MAX == 0) {
78228753Smm		char wbuf[PATH_MAX];
79228753Smm		char pbuf[PATH_MAX];
80228753Smm		size_t wcwdlen;
81228753Smm		size_t pcwdlen;
82228753Smm	        size_t cwdlen;
83228753Smm		assert(getcwd(pbuf, PATH_MAX) != NULL);
84228753Smm		pcwdlen = strlen(pbuf);
85228753Smm		cygwin_conv_to_full_win32_path(pbuf, wbuf);
86228753Smm		wcwdlen = strlen(wbuf);
87228753Smm		cwdlen = ((wcwdlen > pcwdlen) ? wcwdlen : pcwdlen);
88228753Smm		/* Cygwin helper needs an extra few characters. */
89228753Smm		LOOP_MAX = PATH_MAX - (int)cwdlen - 12 - 4 - 4;
90228753Smm	}
91228753Smm	return LOOP_MAX;
92228753Smm#else
93228753Smm	/* cygwin-1.7 ends up here, along with "normal" unix */
94228753Smm	return 200; /* restore pre-r278 depth */
95228753Smm#endif
96228753Smm}
97228753Smm
98228753Smm/* filenames[i] is a distinctive filename of length i. */
99228753Smm/* To simplify interpreting failures, each filename ends with a
100228753Smm * decimal integer which is the length of the filename.  E.g., A
101228753Smm * filename ending in "_92" is 92 characters long.  To detect errors
102228753Smm * which drop or misplace characters, the filenames use a repeating
103228753Smm * "abcdefghijklmnopqrstuvwxyz..." pattern. */
104228753Smmstatic char *filenames[201];
105228753Smm
106228753Smmstatic void
107228753Smmcompute_filenames(void)
108228753Smm{
109228753Smm	char buff[250];
110228753Smm	size_t i,j;
111228753Smm
112228753Smm	filenames[0] = strdup("");
113228753Smm	filenames[1] = strdup("1");
114228753Smm	filenames[2] = strdup("a2");
115228753Smm	for (i = 3; i < sizeof(filenames)/sizeof(filenames[0]); ++i) {
116228753Smm		/* Fill with "abcdefghij..." */
117228753Smm		for (j = 0; j < i; ++j)
118228753Smm			buff[j] = 'a' + (j % 26);
119228753Smm		buff[j--] = '\0';
120228753Smm		/* Work from the end to fill in the number portion. */
121228753Smm		buff[j--] = '0' + (i % 10);
122228753Smm		if (i > 9) {
123228753Smm			buff[j--] = '0' + ((i / 10) % 10);
124228753Smm			if (i > 99)
125248616Smm				buff[j--] = '0' + (char)(i / 100);
126228753Smm		}
127228753Smm		buff[j] = '_';
128228753Smm		/* Guard against obvious screwups in the above code. */
129228753Smm		assertEqualInt(strlen(buff), i);
130228753Smm		filenames[i] = strdup(buff);
131228753Smm	}
132228753Smm}
133228753Smm
134228753Smmstatic void
135228753Smmcreate_tree(void)
136228753Smm{
137228753Smm	char buff[260];
138228753Smm	char buff2[260];
139228753Smm	int i;
140228753Smm	int LOOP_MAX;
141228753Smm
142228753Smm	compute_filenames();
143228753Smm
144228753Smm	/* Log that we'll be omitting some checks. */
145228753Smm	if (!canSymlink()) {
146228753Smm		skipping("Symlink checks");
147228753Smm	}
148228753Smm
149228753Smm	assertMakeDir("original", 0775);
150228753Smm	assertEqualInt(0, chdir("original"));
151228753Smm	LOOP_MAX = compute_loop_max();
152228753Smm
153228753Smm	assertMakeDir("f", 0775);
154228753Smm	assertMakeDir("l", 0775);
155228753Smm	assertMakeDir("m", 0775);
156228753Smm	assertMakeDir("s", 0775);
157228753Smm	assertMakeDir("d", 0775);
158228753Smm
159228753Smm	for (i = 1; i < LOOP_MAX; i++) {
160228753Smm		failure("Internal sanity check failed: i = %d", i);
161228753Smm		assert(filenames[i] != NULL);
162228753Smm
163228753Smm		sprintf(buff, "f/%s", filenames[i]);
164228753Smm		assertMakeFile(buff, 0777, buff);
165228753Smm
166228753Smm		/* Create a link named "l/abcdef..." to the above. */
167228753Smm		sprintf(buff2, "l/%s", filenames[i]);
168228753Smm		assertMakeHardlink(buff2, buff);
169228753Smm
170228753Smm		/* Create a link named "m/abcdef..." to the above. */
171228753Smm		sprintf(buff2, "m/%s", filenames[i]);
172228753Smm		assertMakeHardlink(buff2, buff);
173228753Smm
174228753Smm		if (canSymlink()) {
175228753Smm			/* Create a symlink named "s/abcdef..." to the above. */
176228753Smm			sprintf(buff, "s/%s", filenames[i]);
177228753Smm			sprintf(buff2, "../f/%s", filenames[i]);
178228753Smm			failure("buff=\"%s\" buff2=\"%s\"", buff, buff2);
179348607Smm			assertMakeSymlink(buff, buff2, 0);
180228753Smm		}
181228753Smm		/* Create a dir named "d/abcdef...". */
182228753Smm		buff[0] = 'd';
183228753Smm		failure("buff=\"%s\"", buff);
184228753Smm		assertMakeDir(buff, 0775);
185228753Smm	}
186228753Smm
187228753Smm	assertEqualInt(0, chdir(".."));
188228753Smm}
189228753Smm
190228753Smm#define LIMIT_NONE 200
191228753Smm#define LIMIT_USTAR 100
192228753Smm
193228753Smmstatic void
194228753Smmverify_tree(size_t limit)
195228753Smm{
196228753Smm	char name1[260];
197228753Smm	char name2[260];
198228753Smm	size_t i, LOOP_MAX;
199228753Smm
200228753Smm	LOOP_MAX = compute_loop_max();
201228753Smm
202228753Smm	/* Generate the names we know should be there and verify them. */
203228753Smm	for (i = 1; i < LOOP_MAX; i++) {
204228753Smm		/* Verify a file named "f/abcdef..." */
205228753Smm		sprintf(name1, "f/%s", filenames[i]);
206228753Smm		if (i <= limit) {
207228753Smm			assertFileExists(name1);
208248616Smm			assertFileContents(name1, (int)strlen(name1), name1);
209228753Smm		}
210228753Smm
211228753Smm		sprintf(name2, "l/%s", filenames[i]);
212228753Smm		if (i + 2 <= limit) {
213228753Smm			/* Verify hardlink "l/abcdef..." */
214228753Smm			assertIsHardlink(name1, name2);
215228753Smm			/* Verify hardlink "m/abcdef..." */
216228753Smm			name2[0] = 'm';
217228753Smm			assertIsHardlink(name1, name2);
218228753Smm		}
219228753Smm
220228753Smm		if (canSymlink()) {
221228753Smm			/* Verify symlink "s/abcdef..." */
222228753Smm			sprintf(name1, "s/%s", filenames[i]);
223228753Smm			sprintf(name2, "../f/%s", filenames[i]);
224228753Smm			if (strlen(name2) <= limit)
225348607Smm				assertIsSymlink(name1, name2, 0);
226228753Smm		}
227228753Smm
228228753Smm		/* Verify dir "d/abcdef...". */
229228753Smm		sprintf(name1, "d/%s", filenames[i]);
230228753Smm		if (i + 1 <= limit) { /* +1 for trailing slash */
231228753Smm			if (assertIsDir(name1, -1)) {
232228753Smm				/* TODO: opendir/readdir this
233228753Smm				 * directory and make sure
234228753Smm				 * it's empty.
235228753Smm				 */
236228753Smm			}
237228753Smm		}
238228753Smm	}
239228753Smm
240228753Smm#if !defined(_WIN32) || defined(__CYGWIN__)
241228753Smm	{
242228753Smm		const char *dp;
243228753Smm		/* Now make sure nothing is there that shouldn't be. */
244228753Smm		for (dp = "dflms"; *dp != '\0'; ++dp) {
245228753Smm			DIR *d;
246228753Smm			struct dirent *de;
247228753Smm			char dir[2];
248228753Smm			dir[0] = *dp; dir[1] = '\0';
249228753Smm			d = opendir(dir);
250228753Smm			failure("Unable to open dir '%s'", dir);
251228753Smm			if (!assert(d != NULL))
252228753Smm				continue;
253228753Smm			while ((de = readdir(d)) != NULL) {
254228753Smm				char *p = de->d_name;
255228753Smm				if (p[0] == '.')
256228753Smm					continue;
257228753Smm				switch(dp[0]) {
258228753Smm				case 'l': case 'm': case 'd':
259358088Smm					failure("strlen(p)=%zu", strlen(p));
260228753Smm					assert(strlen(p) < limit);
261228753Smm					assertEqualString(p,
262228753Smm					    filenames[strlen(p)]);
263228753Smm					break;
264228753Smm				case 'f': case 's':
265358088Smm					failure("strlen(p)=%zu", strlen(p));
266228753Smm					assert(strlen(p) < limit + 1);
267228753Smm					assertEqualString(p,
268228753Smm					    filenames[strlen(p)]);
269228753Smm					break;
270228753Smm				default:
271228753Smm					failure("File %s shouldn't be here", p);
272228753Smm					assert(0);
273228753Smm				}
274228753Smm			}
275228753Smm			closedir(d);
276228753Smm		}
277228753Smm	}
278228753Smm#endif
279228753Smm}
280228753Smm
281228753Smmstatic void
282228753Smmcopy_basic(void)
283228753Smm{
284228753Smm	int r;
285228753Smm
286228753Smm	/* NOTE: for proper operation on cygwin-1.5 and windows, the
287228753Smm	 * length of the name of the directory below, "plain", must be
288311041Smm	 * less than or equal to the length of the name of the original
289228753Smm	 * directory, "original"  This restriction derives from the
290228753Smm	 * extremely limited pathname lengths on those platforms.
291228753Smm	 */
292228753Smm	assertMakeDir("plain", 0775);
293228753Smm	assertEqualInt(0, chdir("plain"));
294228753Smm
295228753Smm	/*
296228753Smm	 * Use the tar program to create an archive.
297228753Smm	 */
298228753Smm	r = systemf("%s cf archive -C ../original f d l m s >pack.out 2>pack.err",
299228753Smm	    testprog);
300228753Smm	failure("Error invoking \"%s cf\"", testprog);
301228753Smm	assertEqualInt(r, 0);
302228753Smm
303228753Smm	/* Verify that nothing went to stdout or stderr. */
304228753Smm	assertEmptyFile("pack.err");
305228753Smm	assertEmptyFile("pack.out");
306228753Smm
307228753Smm	/*
308228753Smm	 * Use tar to unpack the archive into another directory.
309228753Smm	 */
310228753Smm	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
311228753Smm	failure("Error invoking %s xf archive", testprog);
312228753Smm	assertEqualInt(r, 0);
313228753Smm
314228753Smm	/* Verify that nothing went to stdout or stderr. */
315228753Smm	assertEmptyFile("unpack.err");
316228753Smm	assertEmptyFile("unpack.out");
317228753Smm
318228753Smm	verify_tree(LIMIT_NONE);
319228753Smm	assertEqualInt(0, chdir(".."));
320228753Smm}
321228753Smm
322228753Smmstatic void
323228753Smmcopy_ustar(void)
324228753Smm{
325228753Smm	const char *target = "ustar";
326228753Smm	int r;
327228753Smm
328228753Smm	/* NOTE: for proper operation on cygwin-1.5 and windows, the
329228753Smm	 * length of the name of the directory below, "ustar", must be
330311041Smm	 * less than or equal to the length of the name of the original
331228753Smm	 * directory, "original"  This restriction derives from the
332228753Smm	 * extremely limited pathname lengths on those platforms.
333228753Smm	 */
334228753Smm	assertMakeDir(target, 0775);
335228753Smm	assertEqualInt(0, chdir(target));
336228753Smm
337228753Smm	/*
338228753Smm	 * Use the tar program to create an archive.
339228753Smm	 */
340228753Smm	r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err",
341228753Smm	    testprog);
342228753Smm	failure("Error invoking \"%s cf archive --format=ustar\"", testprog);
343228753Smm	assertEqualInt(r, 0);
344228753Smm
345228753Smm	/* Verify that nothing went to stdout. */
346228753Smm	assertEmptyFile("pack.out");
347228753Smm	/* Stderr is non-empty, since there are a bunch of files
348228753Smm	 * with filenames too long to archive. */
349228753Smm
350228753Smm	/*
351228753Smm	 * Use tar to unpack the archive into another directory.
352228753Smm	 */
353228753Smm	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
354228753Smm	failure("Error invoking %s xf archive", testprog);
355228753Smm	assertEqualInt(r, 0);
356228753Smm
357228753Smm	/* Verify that nothing went to stdout or stderr. */
358228753Smm	assertEmptyFile("unpack.err");
359228753Smm	assertEmptyFile("unpack.out");
360228753Smm
361228753Smm	verify_tree(LIMIT_USTAR);
362228753Smm	assertEqualInt(0, chdir("../.."));
363228753Smm}
364228753Smm
365228753SmmDEFINE_TEST(test_copy)
366228753Smm{
367228753Smm	assertUmask(0);
368228753Smm	create_tree(); /* Create sample files in "original" dir. */
369228753Smm
370228753Smm	/* Test simple "tar -c | tar -x" pipeline copy. */
371228753Smm	copy_basic();
372228753Smm
373228753Smm	/* Same, but constrain to ustar format. */
374228753Smm	copy_ustar();
375228753Smm}
376