test_copy.c revision 228753
140090Smsmith/*-
240090Smsmith * Copyright (c) 2003-2007 Tim Kientzle
340090Smsmith * All rights reserved.
440090Smsmith *
540090Smsmith * Redistribution and use in source and binary forms, with or without
640090Smsmith * modification, are permitted provided that the following conditions
740090Smsmith * are met:
840090Smsmith * 1. Redistributions of source code must retain the above copyright
940090Smsmith *    notice, this list of conditions and the following disclaimer.
1040090Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1140090Smsmith *    notice, this list of conditions and the following disclaimer in the
1240090Smsmith *    documentation and/or other materials provided with the distribution.
1340090Smsmith *
1440090Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
1540090Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1640090Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1740090Smsmith * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
1840090Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1940090Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2040090Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2140090Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2240090Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2340090Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2440090Smsmith */
2540090Smsmith#include "test.h"
2650477Speter__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_copy.c,v 1.3 2008/08/15 06:12:02 kientzle Exp $");
2740116Sjkh
2840090Smsmith#if defined(__CYGWIN__)
2940090Smsmith# include <limits.h>
3040090Smsmith# include <sys/cygwin.h>
3194936Smux#endif
3294936Smux
3340090Smsmith/*
3494936Smux * Try to figure out how deep we can go in our tests.  Assumes that
3594936Smux * the first call to this function has the longest starting cwd (which
3694936Smux * is currently "<testdir>/original").  This is mostly to work around
3740090Smsmith * limits in our Win32 support.
3840090Smsmith *
39106308Srwatson * Background: On Posix systems, PATH_MAX is merely a limit on the
40106308Srwatson * length of the string passed into a system call.  By repeatedly
4194936Smux * calling chdir(), you can work with arbitrarily long paths on such
4240090Smsmith * systems.  In contrast, Win32 APIs apply PATH_MAX limits to the full
4394936Smux * absolute path, so the permissible length of a system call argument
4494936Smux * varies with the cwd. Some APIs actually enforce limits
4594936Smux * significantly less than PATH_MAX to ensure that you can create
46106308Srwatson * files within the current working directory.  The Win32 limits also
4794936Smux * apply to Cygwin before 1.7.
4894936Smux *
4940090Smsmith * Someday, I want to convert the Win32 support to use newer
5094936Smux * wide-character paths with '\\?\' prefix, which has a 32k PATH_MAX
5140090Smsmith * instead of the rather anemic 260 character limit of the older
5294936Smux * system calls.  Then we can drop this mess (unless we want to
5394936Smux * continue to special-case Cygwin 1.5 and earlier).
5440090Smsmith */
5594936Smuxstatic int
5640090Smsmithcompute_loop_max(void)
5794936Smux{
5840090Smsmith#if defined(_WIN32) && !defined(__CYGWIN__)
5994936Smux	static int LOOP_MAX = 0;
6040090Smsmith	char buf[MAX_PATH];
6194936Smux	size_t cwdlen;
6294936Smux
6394936Smux	if (LOOP_MAX == 0) {
6494936Smux		assert(_getcwd(buf, MAX_PATH) != NULL);
6594936Smux		cwdlen = strlen(buf);
6694936Smux		/* 12 characters = length of 8.3 filename */
6794936Smux		/* 4 characters = length of "/../" used in symlink tests */
6894936Smux		/* 1 character = length of extra "/" separator */
6985385Sjhb		LOOP_MAX = MAX_PATH - (int)cwdlen - 12 - 4 - 1;
7094936Smux	}
7194936Smux	return LOOP_MAX;
7294936Smux#elif defined(__CYGWIN__) && !defined(HAVE_CYGWIN_CONV_PATH)
7394936Smux	static int LOOP_MAX = 0;
7494936Smux	if (LOOP_MAX == 0) {
7594936Smux		char wbuf[PATH_MAX];
7694936Smux		char pbuf[PATH_MAX];
7794936Smux		size_t wcwdlen;
7894936Smux		size_t pcwdlen;
7994936Smux	        size_t cwdlen;
8094936Smux		assert(getcwd(pbuf, PATH_MAX) != NULL);
8194936Smux		pcwdlen = strlen(pbuf);
8294936Smux		cygwin_conv_to_full_win32_path(pbuf, wbuf);
8394936Smux		wcwdlen = strlen(wbuf);
8494936Smux		cwdlen = ((wcwdlen > pcwdlen) ? wcwdlen : pcwdlen);
8594936Smux		/* Cygwin helper needs an extra few characters. */
8694936Smux		LOOP_MAX = PATH_MAX - (int)cwdlen - 12 - 4 - 4;
8794936Smux	}
8894936Smux	return LOOP_MAX;
8994936Smux#else
9094936Smux	/* cygwin-1.7 ends up here, along with "normal" unix */
9195839Speter	return 200; /* restore pre-r278 depth */
9294936Smux#endif
9394936Smux}
9494936Smux
95107849Salfred/* filenames[i] is a distinctive filename of length i. */
96106308Srwatson/* To simplify interpreting failures, each filename ends with a
97106308Srwatson * decimal integer which is the length of the filename.  E.g., A
98106308Srwatson * filename ending in "_92" is 92 characters long.  To detect errors
99106308Srwatson * which drop or misplace characters, the filenames use a repeating
100106308Srwatson * "abcdefghijklmnopqrstuvwxyz..." pattern. */
10194936Smuxstatic char *filenames[201];
10294936Smux
103107849Salfredstatic void
10494936Smuxcompute_filenames(void)
10594936Smux{
10694936Smux	char buff[250];
10794936Smux	size_t i,j;
10894936Smux
10994936Smux	filenames[0] = strdup("");
11094936Smux	filenames[1] = strdup("1");
11194936Smux	filenames[2] = strdup("a2");
11294936Smux	for (i = 3; i < sizeof(filenames)/sizeof(filenames[0]); ++i) {
113107849Salfred		/* Fill with "abcdefghij..." */
114107849Salfred		for (j = 0; j < i; ++j)
115107849Salfred			buff[j] = 'a' + (j % 26);
11694936Smux		buff[j--] = '\0';
11794936Smux		/* Work from the end to fill in the number portion. */
11894936Smux		buff[j--] = '0' + (i % 10);
11994936Smux		if (i > 9) {
12094936Smux			buff[j--] = '0' + ((i / 10) % 10);
12194936Smux			if (i > 99)
12294936Smux				buff[j--] = '0' + (i / 100);
12394936Smux		}
12494936Smux		buff[j] = '_';
12594936Smux		/* Guard against obvious screwups in the above code. */
12694936Smux		assertEqualInt(strlen(buff), i);
127107849Salfred		filenames[i] = strdup(buff);
128107849Salfred	}
12994936Smux}
13094936Smux
13194936Smuxstatic void
13294936Smuxcreate_tree(void)
13394936Smux{
13494936Smux	char buff[260];
13594936Smux	char buff2[260];
136107849Salfred	int i;
13794936Smux	int LOOP_MAX;
13894936Smux
13994936Smux	compute_filenames();
140107849Salfred
14194936Smux	/* Log that we'll be omitting some checks. */
142106308Srwatson	if (!canSymlink()) {
143106308Srwatson		skipping("Symlink checks");
144106308Srwatson	}
145106308Srwatson
146106308Srwatson	assertMakeDir("original", 0775);
14794936Smux	assertEqualInt(0, chdir("original"));
14894936Smux	LOOP_MAX = compute_loop_max();
14994936Smux
15094936Smux	assertMakeDir("f", 0775);
15194936Smux	assertMakeDir("l", 0775);
15294936Smux	assertMakeDir("m", 0775);
153107849Salfred	assertMakeDir("s", 0775);
154107849Salfred	assertMakeDir("d", 0775);
155107849Salfred
15694936Smux	for (i = 1; i < LOOP_MAX; i++) {
15794936Smux		failure("Internal sanity check failed: i = %d", i);
15894936Smux		assert(filenames[i] != NULL);
15994936Smux
16094936Smux		sprintf(buff, "f/%s", filenames[i]);
16194936Smux		assertMakeFile(buff, 0777, buff);
162107849Salfred
16394936Smux		/* Create a link named "l/abcdef..." to the above. */
16494936Smux		sprintf(buff2, "l/%s", filenames[i]);
16594936Smux		assertMakeHardlink(buff2, buff);
16694936Smux
16794936Smux		/* Create a link named "m/abcdef..." to the above. */
16894936Smux		sprintf(buff2, "m/%s", filenames[i]);
16994936Smux		assertMakeHardlink(buff2, buff);
170107849Salfred
17194936Smux		if (canSymlink()) {
17294936Smux			/* Create a symlink named "s/abcdef..." to the above. */
17394936Smux			sprintf(buff, "s/%s", filenames[i]);
17494936Smux			sprintf(buff2, "../f/%s", filenames[i]);
175106308Srwatson			failure("buff=\"%s\" buff2=\"%s\"", buff, buff2);
176106308Srwatson			assertMakeSymlink(buff, buff2);
177106308Srwatson		}
178106308Srwatson		/* Create a dir named "d/abcdef...". */
179106308Srwatson		buff[0] = 'd';
18094936Smux		failure("buff=\"%s\"", buff);
18194936Smux		assertMakeDir(buff, 0775);
18294936Smux	}
183106308Srwatson
184106308Srwatson	assertEqualInt(0, chdir(".."));
185106308Srwatson}
186106308Srwatson
187106308Srwatson#define LIMIT_NONE 200
18894936Smux#define LIMIT_USTAR 100
18994936Smux
19094936Smuxstatic void
19194936Smuxverify_tree(size_t limit)
19294936Smux{
19394936Smux	char name1[260];
19494936Smux	char name2[260];
19594936Smux	size_t i, LOOP_MAX;
19694936Smux
19794936Smux	LOOP_MAX = compute_loop_max();
19894936Smux
19994936Smux	/* Generate the names we know should be there and verify them. */
20094936Smux	for (i = 1; i < LOOP_MAX; i++) {
20194936Smux		/* Verify a file named "f/abcdef..." */
20294936Smux		sprintf(name1, "f/%s", filenames[i]);
20394936Smux		if (i <= limit) {
20494936Smux			assertFileExists(name1);
20594936Smux			assertFileContents(name1, strlen(name1), name1);
20694936Smux		}
20794936Smux
20894936Smux		sprintf(name2, "l/%s", filenames[i]);
20994936Smux		if (i + 2 <= limit) {
21094936Smux			/* Verify hardlink "l/abcdef..." */
21194936Smux			assertIsHardlink(name1, name2);
21294936Smux			/* Verify hardlink "m/abcdef..." */
21394936Smux			name2[0] = 'm';
21494936Smux			assertIsHardlink(name1, name2);
21594936Smux		}
21694936Smux
21794936Smux		if (canSymlink()) {
21894936Smux			/* Verify symlink "s/abcdef..." */
21994936Smux			sprintf(name1, "s/%s", filenames[i]);
22094936Smux			sprintf(name2, "../f/%s", filenames[i]);
22194936Smux			if (strlen(name2) <= limit)
22294936Smux				assertIsSymlink(name1, name2);
22394936Smux		}
22494936Smux
22594936Smux		/* Verify dir "d/abcdef...". */
22694936Smux		sprintf(name1, "d/%s", filenames[i]);
22794936Smux		if (i + 1 <= limit) { /* +1 for trailing slash */
22894936Smux			if (assertIsDir(name1, -1)) {
22994936Smux				/* TODO: opendir/readdir this
23094936Smux				 * directory and make sure
23194936Smux				 * it's empty.
23294936Smux				 */
23394936Smux			}
23494936Smux		}
23594936Smux	}
23694936Smux
23794936Smux#if !defined(_WIN32) || defined(__CYGWIN__)
23894936Smux	{
23994936Smux		const char *dp;
24094936Smux		/* Now make sure nothing is there that shouldn't be. */
24194936Smux		for (dp = "dflms"; *dp != '\0'; ++dp) {
24294936Smux			DIR *d;
24394936Smux			struct dirent *de;
24494936Smux			char dir[2];
24594936Smux			dir[0] = *dp; dir[1] = '\0';
24694936Smux			d = opendir(dir);
24794936Smux			failure("Unable to open dir '%s'", dir);
24894936Smux			if (!assert(d != NULL))
24994936Smux				continue;
25094936Smux			while ((de = readdir(d)) != NULL) {
25194936Smux				char *p = de->d_name;
25294936Smux				if (p[0] == '.')
25394936Smux					continue;
25494936Smux				switch(dp[0]) {
25594936Smux				case 'l': case 'm': case 'd':
25694936Smux					failure("strlen(p)=%d", strlen(p));
25794936Smux					assert(strlen(p) < limit);
25894936Smux					assertEqualString(p,
25994936Smux					    filenames[strlen(p)]);
26094936Smux					break;
26194936Smux				case 'f': case 's':
26294936Smux					failure("strlen(p)=%d", strlen(p));
26395467Sbde					assert(strlen(p) < limit + 1);
26495467Sbde					assertEqualString(p,
26594936Smux					    filenames[strlen(p)]);
26695467Sbde					break;
26795467Sbde				default:
26894936Smux					failure("File %s shouldn't be here", p);
26994936Smux					assert(0);
27094936Smux				}
27194936Smux			}
27294936Smux			closedir(d);
27394936Smux		}
27485385Sjhb	}
27594936Smux#endif
27694936Smux}
27794936Smux
27885385Sjhbstatic void
27940090Smsmithcopy_basic(void)
28078247Speter{
28140090Smsmith	int r;
28294959Smux
28394936Smux	/* NOTE: for proper operation on cygwin-1.5 and windows, the
28494936Smux	 * length of the name of the directory below, "plain", must be
28594936Smux	 * less than or equal to the lengthe of the name of the original
28694936Smux	 * directory, "original"  This restriction derives from the
28794936Smux	 * extremely limited pathname lengths on those platforms.
28894936Smux	 */
28994936Smux	assertMakeDir("plain", 0775);
29094959Smux	assertEqualInt(0, chdir("plain"));
29194959Smux
29294959Smux	/*
29394936Smux	 * Use the tar program to create an archive.
29494959Smux	 */
29594959Smux	r = systemf("%s cf archive -C ../original f d l m s >pack.out 2>pack.err",
29694959Smux	    testprog);
29794936Smux	failure("Error invoking \"%s cf\"", testprog);
29894959Smux	assertEqualInt(r, 0);
29994936Smux
30094936Smux	/* Verify that nothing went to stdout or stderr. */
30194936Smux	assertEmptyFile("pack.err");
30240090Smsmith	assertEmptyFile("pack.out");
30340090Smsmith
30442706Smsmith	/*
30594936Smux	 * Use tar to unpack the archive into another directory.
30694936Smux	 */
30794936Smux	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
30894936Smux	failure("Error invoking %s xf archive", testprog);
30994936Smux	assertEqualInt(r, 0);
31094936Smux
31194936Smux	/* Verify that nothing went to stdout or stderr. */
31294936Smux	assertEmptyFile("unpack.err");
31394936Smux	assertEmptyFile("unpack.out");
31494936Smux
31594936Smux	verify_tree(LIMIT_NONE);
31694936Smux	assertEqualInt(0, chdir(".."));
31794936Smux}
31894936Smux
31994936Smuxstatic void
32094936Smuxcopy_ustar(void)
32194936Smux{
32294936Smux	const char *target = "ustar";
32394936Smux	int r;
32494936Smux
32594936Smux	/* NOTE: for proper operation on cygwin-1.5 and windows, the
32694959Smux	 * length of the name of the directory below, "ustar", must be
32794936Smux	 * less than or equal to the lengthe of the name of the original
32894936Smux	 * directory, "original"  This restriction derives from the
32994959Smux	 * extremely limited pathname lengths on those platforms.
33094959Smux	 */
33194936Smux	assertMakeDir(target, 0775);
33294936Smux	assertEqualInt(0, chdir(target));
33394936Smux
33494959Smux	/*
33594959Smux	 * Use the tar program to create an archive.
33694959Smux	 */
33794959Smux	r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err",
33894959Smux	    testprog);
33994959Smux	failure("Error invoking \"%s cf archive --format=ustar\"", testprog);
34094959Smux	assertEqualInt(r, 0);
34194936Smux
34294936Smux	/* Verify that nothing went to stdout. */
34394936Smux	assertEmptyFile("pack.out");
34494936Smux	/* Stderr is non-empty, since there are a bunch of files
34594936Smux	 * with filenames too long to archive. */
34694959Smux
34794936Smux	/*
34894959Smux	 * Use tar to unpack the archive into another directory.
34994959Smux	 */
35094936Smux	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
35194936Smux	failure("Error invoking %s xf archive", testprog);
35294936Smux	assertEqualInt(r, 0);
35394936Smux
35494936Smux	/* Verify that nothing went to stdout or stderr. */
35594936Smux	assertEmptyFile("unpack.err");
35694959Smux	assertEmptyFile("unpack.out");
35794936Smux
35894959Smux	verify_tree(LIMIT_USTAR);
35994936Smux	assertEqualInt(0, chdir("../.."));
36094936Smux}
36194936Smux
36294936SmuxDEFINE_TEST(test_copy)
36394936Smux{
36494936Smux	assertUmask(0);
36594936Smux	create_tree(); /* Create sample files in "original" dir. */
36694936Smux
36794959Smux	/* Test simple "tar -c | tar -x" pipeline copy. */
36894936Smux	copy_basic();
36994936Smux
37094936Smux	/* Same, but constrain to ustar format. */
37194936Smux	copy_ustar();
37294936Smux}
37394936Smux