]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/libarchive/tar/test/test_copy.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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
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  */
55 static int
56 compute_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. */
101 static char *filenames[201];
102
103 static void
104 compute_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
131 static void
132 create_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
190 static void
191 verify_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
278 static void
279 copy_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
319 static void
320 copy_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
362 DEFINE_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 }