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