test_copy.c revision 228761
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: src/usr.bin/tar/test/test_copy.c,v 1.3 2008/08/15 06:12:02 kientzle Exp $");
27
28#if defined(__CYGWIN__)
29# include <limits.h>
30# include <sys/cygwin.h>
31#endif
32
33/*
34 * Try to figure out how deep we can go in our tests.  Assumes that
35 * the first call to this function has the longest starting cwd (which
36 * is currently "<testdir>/original").  This is mostly to work around
37 * limits in our Win32 support.
38 *
39 * Background: On Posix systems, PATH_MAX is merely a limit on the
40 * length of the string passed into a system call.  By repeatedly
41 * calling chdir(), you can work with arbitrarily long paths on such
42 * systems.  In contrast, Win32 APIs apply PATH_MAX limits to the full
43 * absolute path, so the permissible length of a system call argument
44 * varies with the cwd. Some APIs actually enforce limits
45 * significantly less than PATH_MAX to ensure that you can create
46 * files within the current working directory.  The Win32 limits also
47 * apply to Cygwin before 1.7.
48 *
49 * Someday, I want to convert the Win32 support to use newer
50 * wide-character paths with '\\?\' prefix, which has a 32k PATH_MAX
51 * instead of the rather anemic 260 character limit of the older
52 * system calls.  Then we can drop this mess (unless we want to
53 * continue to special-case Cygwin 1.5 and earlier).
54 */
55static int
56compute_loop_max(void)
57{
58#if defined(_WIN32) && !defined(__CYGWIN__)
59	static int LOOP_MAX = 0;
60	char buf[MAX_PATH];
61	size_t cwdlen;
62
63	if (LOOP_MAX == 0) {
64		assert(_getcwd(buf, MAX_PATH) != NULL);
65		cwdlen = strlen(buf);
66		/* 12 characters = length of 8.3 filename */
67		/* 4 characters = length of "/../" used in symlink tests */
68		/* 1 character = length of extra "/" separator */
69		LOOP_MAX = MAX_PATH - (int)cwdlen - 12 - 4 - 1;
70	}
71	return LOOP_MAX;
72#elif defined(__CYGWIN__) && !defined(HAVE_CYGWIN_CONV_PATH)
73	static int LOOP_MAX = 0;
74	if (LOOP_MAX == 0) {
75		char wbuf[PATH_MAX];
76		char pbuf[PATH_MAX];
77		size_t wcwdlen;
78		size_t pcwdlen;
79	        size_t cwdlen;
80		assert(getcwd(pbuf, PATH_MAX) != NULL);
81		pcwdlen = strlen(pbuf);
82		cygwin_conv_to_full_win32_path(pbuf, wbuf);
83		wcwdlen = strlen(wbuf);
84		cwdlen = ((wcwdlen > pcwdlen) ? wcwdlen : pcwdlen);
85		/* Cygwin helper needs an extra few characters. */
86		LOOP_MAX = PATH_MAX - (int)cwdlen - 12 - 4 - 4;
87	}
88	return LOOP_MAX;
89#else
90	/* cygwin-1.7 ends up here, along with "normal" unix */
91	return 200; /* restore pre-r278 depth */
92#endif
93}
94
95/* filenames[i] is a distinctive filename of length i. */
96/* To simplify interpreting failures, each filename ends with a
97 * decimal integer which is the length of the filename.  E.g., A
98 * filename ending in "_92" is 92 characters long.  To detect errors
99 * which drop or misplace characters, the filenames use a repeating
100 * "abcdefghijklmnopqrstuvwxyz..." pattern. */
101static char *filenames[201];
102
103static void
104compute_filenames(void)
105{
106	char buff[250];
107	size_t i,j;
108
109	filenames[0] = strdup("");
110	filenames[1] = strdup("1");
111	filenames[2] = strdup("a2");
112	for (i = 3; i < sizeof(filenames)/sizeof(filenames[0]); ++i) {
113		/* Fill with "abcdefghij..." */
114		for (j = 0; j < i; ++j)
115			buff[j] = 'a' + (j % 26);
116		buff[j--] = '\0';
117		/* Work from the end to fill in the number portion. */
118		buff[j--] = '0' + (i % 10);
119		if (i > 9) {
120			buff[j--] = '0' + ((i / 10) % 10);
121			if (i > 99)
122				buff[j--] = '0' + (i / 100);
123		}
124		buff[j] = '_';
125		/* Guard against obvious screwups in the above code. */
126		assertEqualInt(strlen(buff), i);
127		filenames[i] = strdup(buff);
128	}
129}
130
131static void
132create_tree(void)
133{
134	char buff[260];
135	char buff2[260];
136	int i;
137	int LOOP_MAX;
138
139	compute_filenames();
140
141	/* Log that we'll be omitting some checks. */
142	if (!canSymlink()) {
143		skipping("Symlink checks");
144	}
145
146	assertMakeDir("original", 0775);
147	assertEqualInt(0, chdir("original"));
148	LOOP_MAX = compute_loop_max();
149
150	assertMakeDir("f", 0775);
151	assertMakeDir("l", 0775);
152	assertMakeDir("m", 0775);
153	assertMakeDir("s", 0775);
154	assertMakeDir("d", 0775);
155
156	for (i = 1; i < LOOP_MAX; i++) {
157		failure("Internal sanity check failed: i = %d", i);
158		assert(filenames[i] != NULL);
159
160		sprintf(buff, "f/%s", filenames[i]);
161		assertMakeFile(buff, 0777, buff);
162
163		/* Create a link named "l/abcdef..." to the above. */
164		sprintf(buff2, "l/%s", filenames[i]);
165		assertMakeHardlink(buff2, buff);
166
167		/* Create a link named "m/abcdef..." to the above. */
168		sprintf(buff2, "m/%s", filenames[i]);
169		assertMakeHardlink(buff2, buff);
170
171		if (canSymlink()) {
172			/* Create a symlink named "s/abcdef..." to the above. */
173			sprintf(buff, "s/%s", filenames[i]);
174			sprintf(buff2, "../f/%s", filenames[i]);
175			failure("buff=\"%s\" buff2=\"%s\"", buff, buff2);
176			assertMakeSymlink(buff, buff2);
177		}
178		/* Create a dir named "d/abcdef...". */
179		buff[0] = 'd';
180		failure("buff=\"%s\"", buff);
181		assertMakeDir(buff, 0775);
182	}
183
184	assertEqualInt(0, chdir(".."));
185}
186
187#define LIMIT_NONE 200
188#define LIMIT_USTAR 100
189
190static void
191verify_tree(size_t limit)
192{
193	char name1[260];
194	char name2[260];
195	size_t i, LOOP_MAX;
196
197	LOOP_MAX = compute_loop_max();
198
199	/* Generate the names we know should be there and verify them. */
200	for (i = 1; i < LOOP_MAX; i++) {
201		/* Verify a file named "f/abcdef..." */
202		sprintf(name1, "f/%s", filenames[i]);
203		if (i <= limit) {
204			assertFileExists(name1);
205			assertFileContents(name1, strlen(name1), name1);
206		}
207
208		sprintf(name2, "l/%s", filenames[i]);
209		if (i + 2 <= limit) {
210			/* Verify hardlink "l/abcdef..." */
211			assertIsHardlink(name1, name2);
212			/* Verify hardlink "m/abcdef..." */
213			name2[0] = 'm';
214			assertIsHardlink(name1, name2);
215		}
216
217		if (canSymlink()) {
218			/* Verify symlink "s/abcdef..." */
219			sprintf(name1, "s/%s", filenames[i]);
220			sprintf(name2, "../f/%s", filenames[i]);
221			if (strlen(name2) <= limit)
222				assertIsSymlink(name1, name2);
223		}
224
225		/* Verify dir "d/abcdef...". */
226		sprintf(name1, "d/%s", filenames[i]);
227		if (i + 1 <= limit) { /* +1 for trailing slash */
228			if (assertIsDir(name1, -1)) {
229				/* TODO: opendir/readdir this
230				 * directory and make sure
231				 * it's empty.
232				 */
233			}
234		}
235	}
236
237#if !defined(_WIN32) || defined(__CYGWIN__)
238	{
239		const char *dp;
240		/* Now make sure nothing is there that shouldn't be. */
241		for (dp = "dflms"; *dp != '\0'; ++dp) {
242			DIR *d;
243			struct dirent *de;
244			char dir[2];
245			dir[0] = *dp; dir[1] = '\0';
246			d = opendir(dir);
247			failure("Unable to open dir '%s'", dir);
248			if (!assert(d != NULL))
249				continue;
250			while ((de = readdir(d)) != NULL) {
251				char *p = de->d_name;
252				if (p[0] == '.')
253					continue;
254				switch(dp[0]) {
255				case 'l': case 'm': case 'd':
256					failure("strlen(p)=%d", strlen(p));
257					assert(strlen(p) < limit);
258					assertEqualString(p,
259					    filenames[strlen(p)]);
260					break;
261				case 'f': case 's':
262					failure("strlen(p)=%d", strlen(p));
263					assert(strlen(p) < limit + 1);
264					assertEqualString(p,
265					    filenames[strlen(p)]);
266					break;
267				default:
268					failure("File %s shouldn't be here", p);
269					assert(0);
270				}
271			}
272			closedir(d);
273		}
274	}
275#endif
276}
277
278static void
279copy_basic(void)
280{
281	int r;
282
283	/* NOTE: for proper operation on cygwin-1.5 and windows, the
284	 * length of the name of the directory below, "plain", must be
285	 * less than or equal to the lengthe of the name of the original
286	 * directory, "original"  This restriction derives from the
287	 * extremely limited pathname lengths on those platforms.
288	 */
289	assertMakeDir("plain", 0775);
290	assertEqualInt(0, chdir("plain"));
291
292	/*
293	 * Use the tar program to create an archive.
294	 */
295	r = systemf("%s cf archive -C ../original f d l m s >pack.out 2>pack.err",
296	    testprog);
297	failure("Error invoking \"%s cf\"", testprog);
298	assertEqualInt(r, 0);
299
300	/* Verify that nothing went to stdout or stderr. */
301	assertEmptyFile("pack.err");
302	assertEmptyFile("pack.out");
303
304	/*
305	 * Use tar to unpack the archive into another directory.
306	 */
307	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
308	failure("Error invoking %s xf archive", testprog);
309	assertEqualInt(r, 0);
310
311	/* Verify that nothing went to stdout or stderr. */
312	assertEmptyFile("unpack.err");
313	assertEmptyFile("unpack.out");
314
315	verify_tree(LIMIT_NONE);
316	assertEqualInt(0, chdir(".."));
317}
318
319static void
320copy_ustar(void)
321{
322	const char *target = "ustar";
323	int r;
324
325	/* NOTE: for proper operation on cygwin-1.5 and windows, the
326	 * length of the name of the directory below, "ustar", must be
327	 * less than or equal to the lengthe of the name of the original
328	 * directory, "original"  This restriction derives from the
329	 * extremely limited pathname lengths on those platforms.
330	 */
331	assertMakeDir(target, 0775);
332	assertEqualInt(0, chdir(target));
333
334	/*
335	 * Use the tar program to create an archive.
336	 */
337	r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err",
338	    testprog);
339	failure("Error invoking \"%s cf archive --format=ustar\"", testprog);
340	assertEqualInt(r, 0);
341
342	/* Verify that nothing went to stdout. */
343	assertEmptyFile("pack.out");
344	/* Stderr is non-empty, since there are a bunch of files
345	 * with filenames too long to archive. */
346
347	/*
348	 * Use tar to unpack the archive into another directory.
349	 */
350	r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);
351	failure("Error invoking %s xf archive", testprog);
352	assertEqualInt(r, 0);
353
354	/* Verify that nothing went to stdout or stderr. */
355	assertEmptyFile("unpack.err");
356	assertEmptyFile("unpack.out");
357
358	verify_tree(LIMIT_USTAR);
359	assertEqualInt(0, chdir("../.."));
360}
361
362DEFINE_TEST(test_copy)
363{
364	assertUmask(0);
365	create_tree(); /* Create sample files in "original" dir. */
366
367	/* Test simple "tar -c | tar -x" pipeline copy. */
368	copy_basic();
369
370	/* Same, but constrain to ustar format. */
371	copy_ustar();
372}
373