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