test_copy.c revision 248616
1139823Simp/*-
21541Srgrimes * Copyright (c) 2003-2007 Tim Kientzle
31541Srgrimes * All rights reserved.
4137668Smlaier *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes *
141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
151541Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
161541Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171541Srgrimes * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
181541Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
191541Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
201541Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
211541Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
221541Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
231541Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
241541Srgrimes */
251541Srgrimes#include "test.h"
261541Srgrimes__FBSDID("$FreeBSD: head/contrib/libarchive/tar/test/test_copy.c 248616 2013-03-22 13:36:03Z mm $");
271541Srgrimes
281541Srgrimes#if defined(__CYGWIN__)
291541Srgrimes# include <limits.h>
3010939Swollman# include <sys/cygwin.h>
311541Srgrimes#endif
321541Srgrimes#if defined(_WIN32) && !defined(__CYGWIN__)
33172467Ssilby# include <direct.h>
34172467Ssilby#endif
35172467Ssilby
36204902Sqingli/*
37143868Sglebius * Try to figure out how deep we can go in our tests.  Assumes that
381541Srgrimes * the first call to this function has the longest starting cwd (which
391549Srgrimes * is currently "<testdir>/original").  This is mostly to work around
4024204Sbde * limits in our Win32 support.
411541Srgrimes *
42164033Srwatson * Background: On Posix systems, PATH_MAX is merely a limit on the
431541Srgrimes * length of the string passed into a system call.  By repeatedly
44186948Sbz * calling chdir(), you can work with arbitrarily long paths on such
4512704Sphk * systems.  In contrast, Win32 APIs apply PATH_MAX limits to the full
46186948Sbz * absolute path, so the permissible length of a system call argument
4712704Sphk * varies with the cwd. Some APIs actually enforce limits
48192011Sqingli * significantly less than PATH_MAX to ensure that you can create
491541Srgrimes * files within the current working directory.  The Win32 limits also
501541Srgrimes * apply to Cygwin before 1.7.
51195914Sqingli *
52215207Sgnn * Someday, I want to convert the Win32 support to use newer
53192011Sqingli * wide-character paths with '\\?\' prefix, which has a 32k PATH_MAX
54186119Sqingli * instead of the rather anemic 260 character limit of the older
5555009Sshin * system calls.  Then we can drop this mess (unless we want to
561541Srgrimes * continue to special-case Cygwin 1.5 and earlier).
57192011Sqingli */
581541Srgrimesstatic int
59228571Sglebiuscompute_loop_max(void)
601541Srgrimes{
611541Srgrimes#if defined(_WIN32) && !defined(__CYGWIN__)
6281127Sume	static int LOOP_MAX = 0;
63170613Sbms	char buf[MAX_PATH];
64228571Sglebius	size_t cwdlen;
65189592Sbms
66195699Srwatson	if (LOOP_MAX == 0) {
67195699Srwatson		assert(_getcwd(buf, MAX_PATH) != NULL);
681541Srgrimes		cwdlen = strlen(buf);
6992723Salfred		/* 12 characters = length of 8.3 filename */
7092723Salfred		/* 4 characters = length of "/../" used in symlink tests */
7192723Salfred		/* 1 character = length of extra "/" separator */
7292723Salfred		LOOP_MAX = MAX_PATH - (int)cwdlen - 12 - 4 - 1;
7355009Sshin	}
7492723Salfred	return LOOP_MAX;
75228571Sglebius#elif defined(__CYGWIN__) && !defined(HAVE_CYGWIN_CONV_PATH)
76230207Sglebius	static int LOOP_MAX = 0;
77167729Sbms	if (LOOP_MAX == 0) {
781541Srgrimes		char wbuf[PATH_MAX];
79228571Sglebius		char pbuf[PATH_MAX];
80228571Sglebius		size_t wcwdlen;
81228571Sglebius		size_t pcwdlen;
82228571Sglebius	        size_t cwdlen;
83149221Sglebius		assert(getcwd(pbuf, PATH_MAX) != NULL);
8421666Swollman		pcwdlen = strlen(pbuf);
85207369Sbz		cygwin_conv_to_full_win32_path(pbuf, wbuf);
86207369Sbz		wcwdlen = strlen(wbuf);
87207369Sbz		cwdlen = ((wcwdlen > pcwdlen) ? wcwdlen : pcwdlen);
88215207Sgnn		/* Cygwin helper needs an extra few characters. */
89215207Sgnn		LOOP_MAX = PATH_MAX - (int)cwdlen - 12 - 4 - 4;
90215207Sgnn	}
911541Srgrimes	return LOOP_MAX;
921541Srgrimes#else
93226401Sglebius	/* cygwin-1.7 ends up here, along with "normal" unix */
941541Srgrimes	return 200; /* restore pre-r278 depth */
951549Srgrimes#endif
96169454Srwatson}
971541Srgrimes
981541Srgrimes/* filenames[i] is a distinctive filename of length i. */
991541Srgrimes/* To simplify interpreting failures, each filename ends with a
1001541Srgrimes * decimal integer which is the length of the filename.  E.g., A
101194951Srwatson * filename ending in "_92" is 92 characters long.  To detect errors
102226401Sglebius * which drop or misplace characters, the filenames use a repeating
103226401Sglebius * "abcdefghijklmnopqrstuvwxyz..." pattern. */
104226401Sglebiusstatic char *filenames[201];
105226401Sglebius
106194951Srwatsonstatic void
1071541Srgrimescompute_filenames(void)
108194951Srwatson{
1091541Srgrimes	char buff[250];
1101541Srgrimes	size_t i,j;
1111541Srgrimes
1121541Srgrimes	filenames[0] = strdup("");
113133486Sandre	filenames[1] = strdup("1");
114133486Sandre	filenames[2] = strdup("a2");
115133486Sandre	for (i = 3; i < sizeof(filenames)/sizeof(filenames[0]); ++i) {
116133486Sandre		/* Fill with "abcdefghij..." */
117169454Srwatson		for (j = 0; j < i; ++j)
118133486Sandre			buff[j] = 'a' + (j % 26);
119133486Sandre		buff[j--] = '\0';
120133486Sandre		/* Work from the end to fill in the number portion. */
121194951Srwatson		buff[j--] = '0' + (i % 10);
122133486Sandre		if (i > 9) {
123194951Srwatson			buff[j--] = '0' + ((i / 10) % 10);
124194951Srwatson			if (i > 99)
125184295Sbz				buff[j--] = '0' + (char)(i / 100);
126194951Srwatson		}
127133486Sandre		buff[j] = '_';
128194951Srwatson		/* Guard against obvious screwups in the above code. */
129184295Sbz		assertEqualInt(strlen(buff), i);
130133486Sandre		filenames[i] = strdup(buff);
131133486Sandre	}
132133486Sandre}
1331541Srgrimes
1341541Srgrimesstatic void
1351541Srgrimescreate_tree(void)
1361541Srgrimes{
1371549Srgrimes	char buff[260];
138169454Srwatson	char buff2[260];
1391541Srgrimes	int i;
1401541Srgrimes	int LOOP_MAX;
1411541Srgrimes
1421541Srgrimes	compute_filenames();
143166450Sbms
1441541Srgrimes	/* Log that we'll be omitting some checks. */
1451541Srgrimes	if (!canSymlink()) {
1461541Srgrimes		skipping("Symlink checks");
1471541Srgrimes	}
1481541Srgrimes
1491541Srgrimes	assertMakeDir("original", 0775);
1501541Srgrimes	assertEqualInt(0, chdir("original"));
1511541Srgrimes	LOOP_MAX = compute_loop_max();
1521541Srgrimes
1531541Srgrimes	assertMakeDir("f", 0775);
1541541Srgrimes	assertMakeDir("l", 0775);
1551541Srgrimes	assertMakeDir("m", 0775);
15612296Sphk	assertMakeDir("s", 0775);
157169454Srwatson	assertMakeDir("d", 0775);
1581541Srgrimes
1591541Srgrimes	for (i = 1; i < LOOP_MAX; i++) {
1601541Srgrimes		failure("Internal sanity check failed: i = %d", i);
1611541Srgrimes		assert(filenames[i] != NULL);
1621541Srgrimes
1634127Swollman		sprintf(buff, "f/%s", filenames[i]);
164133874Srwatson		assertMakeFile(buff, 0777, buff);
1651541Srgrimes
1661541Srgrimes		/* Create a link named "l/abcdef..." to the above. */
1671541Srgrimes		sprintf(buff2, "l/%s", filenames[i]);
1681541Srgrimes		assertMakeHardlink(buff2, buff);
1691541Srgrimes
17055009Sshin		/* Create a link named "m/abcdef..." to the above. */
17155009Sshin		sprintf(buff2, "m/%s", filenames[i]);
17255009Sshin		assertMakeHardlink(buff2, buff);
17355009Sshin
17455009Sshin		if (canSymlink()) {
17555009Sshin			/* Create a symlink named "s/abcdef..." to the above. */
17655009Sshin			sprintf(buff, "s/%s", filenames[i]);
17755009Sshin			sprintf(buff2, "../f/%s", filenames[i]);
17855009Sshin			failure("buff=\"%s\" buff2=\"%s\"", buff, buff2);
17955009Sshin			assertMakeSymlink(buff, buff2);
18055009Sshin		}
18155009Sshin		/* Create a dir named "d/abcdef...". */
18255009Sshin		buff[0] = 'd';
18355009Sshin		failure("buff=\"%s\"", buff);
18455009Sshin		assertMakeDir(buff, 0775);
18555009Sshin	}
18655009Sshin
18755009Sshin	assertEqualInt(0, chdir(".."));
18855009Sshin}
189184295Sbz
19055009Sshin#define LIMIT_NONE 200
19155009Sshin#define LIMIT_USTAR 100
19255009Sshin
193169454Srwatsonstatic void
19455009Sshinverify_tree(size_t limit)
19555009Sshin{
19655009Sshin	char name1[260];
19755009Sshin	char name2[260];
19855009Sshin	size_t i, LOOP_MAX;
19955009Sshin
20055009Sshin	LOOP_MAX = compute_loop_max();
20155009Sshin
20255009Sshin	/* Generate the names we know should be there and verify them. */
20355009Sshin	for (i = 1; i < LOOP_MAX; i++) {
20455009Sshin		/* Verify a file named "f/abcdef..." */
20555009Sshin		sprintf(name1, "f/%s", filenames[i]);
2061541Srgrimes		if (i <= limit) {
2071541Srgrimes			assertFileExists(name1);
208191443Srwatson			assertFileContents(name1, (int)strlen(name1), name1);
209191443Srwatson		}
2101541Srgrimes
2111541Srgrimes		sprintf(name2, "l/%s", filenames[i]);
2121549Srgrimes		if (i + 2 <= limit) {
213169454Srwatson			/* Verify hardlink "l/abcdef..." */
214169454Srwatson			assertIsHardlink(name1, name2);
2151541Srgrimes			/* Verify hardlink "m/abcdef..." */
2161541Srgrimes			name2[0] = 'm';
217184295Sbz			assertIsHardlink(name1, name2);
2181541Srgrimes		}
219168032Sbms
22084102Sjlemon		if (canSymlink()) {
221189592Sbms			/* Verify symlink "s/abcdef..." */
2221541Srgrimes			sprintf(name1, "s/%s", filenames[i]);
223194951Srwatson			sprintf(name2, "../f/%s", filenames[i]);
224168032Sbms			if (strlen(name2) <= limit)
225228768Sglebius				assertIsSymlink(name1, name2);
2261541Srgrimes		}
227228768Sglebius
228228768Sglebius		/* Verify dir "d/abcdef...". */
229228768Sglebius		sprintf(name1, "d/%s", filenames[i]);
230228768Sglebius		if (i + 1 <= limit) { /* +1 for trailing slash */
231228768Sglebius			if (assertIsDir(name1, -1)) {
232228768Sglebius				/* TODO: opendir/readdir this
233228768Sglebius				 * directory and make sure
234184295Sbz				 * it's empty.
235168032Sbms				 */
23687124Sbrian			}
237168032Sbms		}
23887124Sbrian	}
239191443Srwatson
240191443Srwatson#if !defined(_WIN32) || defined(__CYGWIN__)
241191443Srwatson	{
242191443Srwatson		const char *dp;
24355009Sshin		/* Now make sure nothing is there that shouldn't be. */
244191443Srwatson		for (dp = "dflms"; *dp != '\0'; ++dp) {
245191443Srwatson			DIR *d;
246191443Srwatson			struct dirent *de;
247191443Srwatson			char dir[2];
248227791Sglebius			dir[0] = *dp; dir[1] = '\0';
249227791Sglebius			d = opendir(dir);
250227791Sglebius			failure("Unable to open dir '%s'", dir);
251227791Sglebius			if (!assert(d != NULL))
252227791Sglebius				continue;
253227791Sglebius			while ((de = readdir(d)) != NULL) {
254227791Sglebius				char *p = de->d_name;
255227791Sglebius				if (p[0] == '.')
256227791Sglebius					continue;
257227791Sglebius				switch(dp[0]) {
258227791Sglebius				case 'l': case 'm': case 'd':
259227831Sglebius					failure("strlen(p)=%d", strlen(p));
260227831Sglebius					assert(strlen(p) < limit);
261227791Sglebius					assertEqualString(p,
262227791Sglebius					    filenames[strlen(p)]);
263228768Sglebius					break;
264228768Sglebius				case 'f': case 's':
265228768Sglebius					failure("strlen(p)=%d", strlen(p));
266228768Sglebius					assert(strlen(p) < limit + 1);
267228768Sglebius					assertEqualString(p,
268228768Sglebius					    filenames[strlen(p)]);
269228768Sglebius					break;
270227791Sglebius				default:
271227791Sglebius					failure("File %s shouldn't be here", p);
272227791Sglebius					assert(0);
273227791Sglebius				}
274228768Sglebius			}
275227791Sglebius			closedir(d);
276191443Srwatson		}
277191443Srwatson	}
278191443Srwatson#endif
279191443Srwatson}
280230207Sglebius
281230207Sglebiusstatic void
282191443Srwatsoncopy_basic(void)
28355009Sshin{
284164033Srwatson	int r;
285164033Srwatson
286164033Srwatson	/* NOTE: for proper operation on cygwin-1.5 and windows, the
287164033Srwatson	 * length of the name of the directory below, "plain", must be
288164033Srwatson	 * less than or equal to the lengthe of the name of the original
289184295Sbz	 * directory, "original"  This restriction derives from the
290184295Sbz	 * extremely limited pathname lengths on those platforms.
291164033Srwatson	 */
292164033Srwatson	assertMakeDir("plain", 0775);
29355009Sshin	assertEqualInt(0, chdir("plain"));
294164033Srwatson
295164033Srwatson	/*
296164033Srwatson	 * Use the tar program to create an archive.
297164033Srwatson	 */
298164033Srwatson	r = systemf("%s cf archive -C ../original f d l m s >pack.out 2>pack.err",
299184295Sbz	    testprog);
300184295Sbz	failure("Error invoking \"%s cf\"", testprog);
301164033Srwatson	assertEqualInt(r, 0);
302164033Srwatson
30355009Sshin	/* Verify that nothing went to stdout or stderr. */
304184295Sbz	assertEmptyFile("pack.err");
305184295Sbz	assertEmptyFile("pack.out");
30683366Sjulian
307191443Srwatson	/*
308191443Srwatson	 * Use tar to unpack the archive into another directory.
309191443Srwatson	 */
310191443Srwatson	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
311191443Srwatson	failure("Error invoking %s xf archive", testprog);
31255009Sshin	assertEqualInt(r, 0);
31355009Sshin
314191443Srwatson	/* Verify that nothing went to stdout or stderr. */
315191443Srwatson	assertEmptyFile("unpack.err");
316191443Srwatson	assertEmptyFile("unpack.out");
3171541Srgrimes
318191456Srwatson	verify_tree(LIMIT_NONE);
319191456Srwatson	assertEqualInt(0, chdir(".."));
320191456Srwatson}
321191456Srwatson
322191456Srwatsonstatic void
323191456Srwatsoncopy_ustar(void)
324191456Srwatson{
325191456Srwatson	const char *target = "ustar";
326191456Srwatson	int r;
327191456Srwatson
328191456Srwatson	/* NOTE: for proper operation on cygwin-1.5 and windows, the
329191456Srwatson	 * length of the name of the directory below, "ustar", must be
330191456Srwatson	 * less than or equal to the lengthe of the name of the original
331191456Srwatson	 * directory, "original"  This restriction derives from the
332191456Srwatson	 * extremely limited pathname lengths on those platforms.
333191456Srwatson	 */
334191456Srwatson	assertMakeDir(target, 0775);
335191456Srwatson	assertEqualInt(0, chdir(target));
336191456Srwatson
337191456Srwatson	/*
338191456Srwatson	 * Use the tar program to create an archive.
3391541Srgrimes	 */
34014632Sfenner	r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err",
341191443Srwatson	    testprog);
342191443Srwatson	failure("Error invoking \"%s cf archive --format=ustar\"", testprog);
3431541Srgrimes	assertEqualInt(r, 0);
344191443Srwatson
345194951Srwatson	/* Verify that nothing went to stdout. */
346191443Srwatson	assertEmptyFile("pack.out");
347191443Srwatson	/* Stderr is non-empty, since there are a bunch of files
348191443Srwatson	 * with filenames too long to archive. */
349191443Srwatson
350191443Srwatson	/*
351191443Srwatson	 * Use tar to unpack the archive into another directory.
352191443Srwatson	 */
353191443Srwatson	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
354191443Srwatson	failure("Error invoking %s xf archive", testprog);
355194760Srwatson	assertEqualInt(r, 0);
356194760Srwatson
357194951Srwatson	/* Verify that nothing went to stdout or stderr. */
358191443Srwatson	assertEmptyFile("unpack.err");
359229621Sjhb	assertEmptyFile("unpack.out");
360191443Srwatson
361191443Srwatson	verify_tree(LIMIT_USTAR);
362191443Srwatson	assertEqualInt(0, chdir("../.."));
363191443Srwatson}
364191443Srwatson
365191443SrwatsonDEFINE_TEST(test_copy)
366191443Srwatson{
367191443Srwatson	assertUmask(0);
36884102Sjlemon	create_tree(); /* Create sample files in "original" dir. */
36984102Sjlemon
370191443Srwatson	/* Test simple "tar -c | tar -x" pipeline copy. */
371194760Srwatson	copy_basic();
372194760Srwatson
373229621Sjhb	/* Same, but constrain to ustar format. */
37484102Sjlemon	copy_ustar();
375191443Srwatson}
376191443Srwatson