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
compute_loop_max(void)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
compute_filenames(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
create_tree(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
verify_tree(size_t limit,const char * format)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
copy_basic(const char * extra_args,const char * name)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
copy_ustar(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
DEFINE_TEST(test_copy)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