1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm#include "test.h" 26228763Smm__FBSDID("$FreeBSD: stable/11/contrib/libarchive/tar/test/test_copy.c 358088 2020-02-19 01:50:47Z mm $"); 27228753Smm 28228753Smm#if defined(__CYGWIN__) 29228753Smm# include <limits.h> 30228753Smm# include <sys/cygwin.h> 31228753Smm#endif 32248616Smm#if defined(_WIN32) && !defined(__CYGWIN__) 33248616Smm# include <direct.h> 34248616Smm#endif 35228753Smm 36228753Smm/* 37228753Smm * Try to figure out how deep we can go in our tests. Assumes that 38228753Smm * the first call to this function has the longest starting cwd (which 39228753Smm * is currently "<testdir>/original"). This is mostly to work around 40228753Smm * limits in our Win32 support. 41228753Smm * 42228753Smm * Background: On Posix systems, PATH_MAX is merely a limit on the 43228753Smm * length of the string passed into a system call. By repeatedly 44228753Smm * calling chdir(), you can work with arbitrarily long paths on such 45228753Smm * systems. In contrast, Win32 APIs apply PATH_MAX limits to the full 46228753Smm * absolute path, so the permissible length of a system call argument 47228753Smm * varies with the cwd. Some APIs actually enforce limits 48228753Smm * significantly less than PATH_MAX to ensure that you can create 49228753Smm * files within the current working directory. The Win32 limits also 50228753Smm * apply to Cygwin before 1.7. 51228753Smm * 52228753Smm * Someday, I want to convert the Win32 support to use newer 53228753Smm * wide-character paths with '\\?\' prefix, which has a 32k PATH_MAX 54228753Smm * instead of the rather anemic 260 character limit of the older 55228753Smm * system calls. Then we can drop this mess (unless we want to 56228753Smm * continue to special-case Cygwin 1.5 and earlier). 57228753Smm */ 58228753Smmstatic int 59228753Smmcompute_loop_max(void) 60228753Smm{ 61228753Smm#if defined(_WIN32) && !defined(__CYGWIN__) 62228753Smm static int LOOP_MAX = 0; 63228753Smm char buf[MAX_PATH]; 64228753Smm size_t cwdlen; 65228753Smm 66228753Smm if (LOOP_MAX == 0) { 67228753Smm assert(_getcwd(buf, MAX_PATH) != NULL); 68228753Smm cwdlen = strlen(buf); 69228753Smm /* 12 characters = length of 8.3 filename */ 70228753Smm /* 4 characters = length of "/../" used in symlink tests */ 71228753Smm /* 1 character = length of extra "/" separator */ 72228753Smm LOOP_MAX = MAX_PATH - (int)cwdlen - 12 - 4 - 1; 73228753Smm } 74228753Smm return LOOP_MAX; 75228753Smm#elif defined(__CYGWIN__) && !defined(HAVE_CYGWIN_CONV_PATH) 76228753Smm static int LOOP_MAX = 0; 77228753Smm if (LOOP_MAX == 0) { 78228753Smm char wbuf[PATH_MAX]; 79228753Smm char pbuf[PATH_MAX]; 80228753Smm size_t wcwdlen; 81228753Smm size_t pcwdlen; 82228753Smm size_t cwdlen; 83228753Smm assert(getcwd(pbuf, PATH_MAX) != NULL); 84228753Smm pcwdlen = strlen(pbuf); 85228753Smm cygwin_conv_to_full_win32_path(pbuf, wbuf); 86228753Smm wcwdlen = strlen(wbuf); 87228753Smm cwdlen = ((wcwdlen > pcwdlen) ? wcwdlen : pcwdlen); 88228753Smm /* Cygwin helper needs an extra few characters. */ 89228753Smm LOOP_MAX = PATH_MAX - (int)cwdlen - 12 - 4 - 4; 90228753Smm } 91228753Smm return LOOP_MAX; 92228753Smm#else 93228753Smm /* cygwin-1.7 ends up here, along with "normal" unix */ 94228753Smm return 200; /* restore pre-r278 depth */ 95228753Smm#endif 96228753Smm} 97228753Smm 98228753Smm/* filenames[i] is a distinctive filename of length i. */ 99228753Smm/* To simplify interpreting failures, each filename ends with a 100228753Smm * decimal integer which is the length of the filename. E.g., A 101228753Smm * filename ending in "_92" is 92 characters long. To detect errors 102228753Smm * which drop or misplace characters, the filenames use a repeating 103228753Smm * "abcdefghijklmnopqrstuvwxyz..." pattern. */ 104228753Smmstatic char *filenames[201]; 105228753Smm 106228753Smmstatic void 107228753Smmcompute_filenames(void) 108228753Smm{ 109228753Smm char buff[250]; 110228753Smm size_t i,j; 111228753Smm 112228753Smm filenames[0] = strdup(""); 113228753Smm filenames[1] = strdup("1"); 114228753Smm filenames[2] = strdup("a2"); 115228753Smm for (i = 3; i < sizeof(filenames)/sizeof(filenames[0]); ++i) { 116228753Smm /* Fill with "abcdefghij..." */ 117228753Smm for (j = 0; j < i; ++j) 118228753Smm buff[j] = 'a' + (j % 26); 119228753Smm buff[j--] = '\0'; 120228753Smm /* Work from the end to fill in the number portion. */ 121228753Smm buff[j--] = '0' + (i % 10); 122228753Smm if (i > 9) { 123228753Smm buff[j--] = '0' + ((i / 10) % 10); 124228753Smm if (i > 99) 125248616Smm buff[j--] = '0' + (char)(i / 100); 126228753Smm } 127228753Smm buff[j] = '_'; 128228753Smm /* Guard against obvious screwups in the above code. */ 129228753Smm assertEqualInt(strlen(buff), i); 130228753Smm filenames[i] = strdup(buff); 131228753Smm } 132228753Smm} 133228753Smm 134228753Smmstatic void 135228753Smmcreate_tree(void) 136228753Smm{ 137228753Smm char buff[260]; 138228753Smm char buff2[260]; 139228753Smm int i; 140228753Smm int LOOP_MAX; 141228753Smm 142228753Smm compute_filenames(); 143228753Smm 144228753Smm /* Log that we'll be omitting some checks. */ 145228753Smm if (!canSymlink()) { 146228753Smm skipping("Symlink checks"); 147228753Smm } 148228753Smm 149228753Smm assertMakeDir("original", 0775); 150228753Smm assertEqualInt(0, chdir("original")); 151228753Smm LOOP_MAX = compute_loop_max(); 152228753Smm 153228753Smm assertMakeDir("f", 0775); 154228753Smm assertMakeDir("l", 0775); 155228753Smm assertMakeDir("m", 0775); 156228753Smm assertMakeDir("s", 0775); 157228753Smm assertMakeDir("d", 0775); 158228753Smm 159228753Smm for (i = 1; i < LOOP_MAX; i++) { 160228753Smm failure("Internal sanity check failed: i = %d", i); 161228753Smm assert(filenames[i] != NULL); 162228753Smm 163228753Smm sprintf(buff, "f/%s", filenames[i]); 164228753Smm assertMakeFile(buff, 0777, buff); 165228753Smm 166228753Smm /* Create a link named "l/abcdef..." to the above. */ 167228753Smm sprintf(buff2, "l/%s", filenames[i]); 168228753Smm assertMakeHardlink(buff2, buff); 169228753Smm 170228753Smm /* Create a link named "m/abcdef..." to the above. */ 171228753Smm sprintf(buff2, "m/%s", filenames[i]); 172228753Smm assertMakeHardlink(buff2, buff); 173228753Smm 174228753Smm if (canSymlink()) { 175228753Smm /* Create a symlink named "s/abcdef..." to the above. */ 176228753Smm sprintf(buff, "s/%s", filenames[i]); 177228753Smm sprintf(buff2, "../f/%s", filenames[i]); 178228753Smm failure("buff=\"%s\" buff2=\"%s\"", buff, buff2); 179348607Smm assertMakeSymlink(buff, buff2, 0); 180228753Smm } 181228753Smm /* Create a dir named "d/abcdef...". */ 182228753Smm buff[0] = 'd'; 183228753Smm failure("buff=\"%s\"", buff); 184228753Smm assertMakeDir(buff, 0775); 185228753Smm } 186228753Smm 187228753Smm assertEqualInt(0, chdir("..")); 188228753Smm} 189228753Smm 190228753Smm#define LIMIT_NONE 200 191228753Smm#define LIMIT_USTAR 100 192228753Smm 193228753Smmstatic void 194228753Smmverify_tree(size_t limit) 195228753Smm{ 196228753Smm char name1[260]; 197228753Smm char name2[260]; 198228753Smm size_t i, LOOP_MAX; 199228753Smm 200228753Smm LOOP_MAX = compute_loop_max(); 201228753Smm 202228753Smm /* Generate the names we know should be there and verify them. */ 203228753Smm for (i = 1; i < LOOP_MAX; i++) { 204228753Smm /* Verify a file named "f/abcdef..." */ 205228753Smm sprintf(name1, "f/%s", filenames[i]); 206228753Smm if (i <= limit) { 207228753Smm assertFileExists(name1); 208248616Smm assertFileContents(name1, (int)strlen(name1), name1); 209228753Smm } 210228753Smm 211228753Smm sprintf(name2, "l/%s", filenames[i]); 212228753Smm if (i + 2 <= limit) { 213228753Smm /* Verify hardlink "l/abcdef..." */ 214228753Smm assertIsHardlink(name1, name2); 215228753Smm /* Verify hardlink "m/abcdef..." */ 216228753Smm name2[0] = 'm'; 217228753Smm assertIsHardlink(name1, name2); 218228753Smm } 219228753Smm 220228753Smm if (canSymlink()) { 221228753Smm /* Verify symlink "s/abcdef..." */ 222228753Smm sprintf(name1, "s/%s", filenames[i]); 223228753Smm sprintf(name2, "../f/%s", filenames[i]); 224228753Smm if (strlen(name2) <= limit) 225348607Smm assertIsSymlink(name1, name2, 0); 226228753Smm } 227228753Smm 228228753Smm /* Verify dir "d/abcdef...". */ 229228753Smm sprintf(name1, "d/%s", filenames[i]); 230228753Smm if (i + 1 <= limit) { /* +1 for trailing slash */ 231228753Smm if (assertIsDir(name1, -1)) { 232228753Smm /* TODO: opendir/readdir this 233228753Smm * directory and make sure 234228753Smm * it's empty. 235228753Smm */ 236228753Smm } 237228753Smm } 238228753Smm } 239228753Smm 240228753Smm#if !defined(_WIN32) || defined(__CYGWIN__) 241228753Smm { 242228753Smm const char *dp; 243228753Smm /* Now make sure nothing is there that shouldn't be. */ 244228753Smm for (dp = "dflms"; *dp != '\0'; ++dp) { 245228753Smm DIR *d; 246228753Smm struct dirent *de; 247228753Smm char dir[2]; 248228753Smm dir[0] = *dp; dir[1] = '\0'; 249228753Smm d = opendir(dir); 250228753Smm failure("Unable to open dir '%s'", dir); 251228753Smm if (!assert(d != NULL)) 252228753Smm continue; 253228753Smm while ((de = readdir(d)) != NULL) { 254228753Smm char *p = de->d_name; 255228753Smm if (p[0] == '.') 256228753Smm continue; 257228753Smm switch(dp[0]) { 258228753Smm case 'l': case 'm': case 'd': 259358088Smm failure("strlen(p)=%zu", strlen(p)); 260228753Smm assert(strlen(p) < limit); 261228753Smm assertEqualString(p, 262228753Smm filenames[strlen(p)]); 263228753Smm break; 264228753Smm case 'f': case 's': 265358088Smm failure("strlen(p)=%zu", strlen(p)); 266228753Smm assert(strlen(p) < limit + 1); 267228753Smm assertEqualString(p, 268228753Smm filenames[strlen(p)]); 269228753Smm break; 270228753Smm default: 271228753Smm failure("File %s shouldn't be here", p); 272228753Smm assert(0); 273228753Smm } 274228753Smm } 275228753Smm closedir(d); 276228753Smm } 277228753Smm } 278228753Smm#endif 279228753Smm} 280228753Smm 281228753Smmstatic void 282228753Smmcopy_basic(void) 283228753Smm{ 284228753Smm int r; 285228753Smm 286228753Smm /* NOTE: for proper operation on cygwin-1.5 and windows, the 287228753Smm * length of the name of the directory below, "plain", must be 288311041Smm * less than or equal to the length of the name of the original 289228753Smm * directory, "original" This restriction derives from the 290228753Smm * extremely limited pathname lengths on those platforms. 291228753Smm */ 292228753Smm assertMakeDir("plain", 0775); 293228753Smm assertEqualInt(0, chdir("plain")); 294228753Smm 295228753Smm /* 296228753Smm * Use the tar program to create an archive. 297228753Smm */ 298228753Smm r = systemf("%s cf archive -C ../original f d l m s >pack.out 2>pack.err", 299228753Smm testprog); 300228753Smm failure("Error invoking \"%s cf\"", testprog); 301228753Smm assertEqualInt(r, 0); 302228753Smm 303228753Smm /* Verify that nothing went to stdout or stderr. */ 304228753Smm assertEmptyFile("pack.err"); 305228753Smm assertEmptyFile("pack.out"); 306228753Smm 307228753Smm /* 308228753Smm * Use tar to unpack the archive into another directory. 309228753Smm */ 310228753Smm r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog); 311228753Smm failure("Error invoking %s xf archive", testprog); 312228753Smm assertEqualInt(r, 0); 313228753Smm 314228753Smm /* Verify that nothing went to stdout or stderr. */ 315228753Smm assertEmptyFile("unpack.err"); 316228753Smm assertEmptyFile("unpack.out"); 317228753Smm 318228753Smm verify_tree(LIMIT_NONE); 319228753Smm assertEqualInt(0, chdir("..")); 320228753Smm} 321228753Smm 322228753Smmstatic void 323228753Smmcopy_ustar(void) 324228753Smm{ 325228753Smm const char *target = "ustar"; 326228753Smm int r; 327228753Smm 328228753Smm /* NOTE: for proper operation on cygwin-1.5 and windows, the 329228753Smm * length of the name of the directory below, "ustar", must be 330311041Smm * less than or equal to the length of the name of the original 331228753Smm * directory, "original" This restriction derives from the 332228753Smm * extremely limited pathname lengths on those platforms. 333228753Smm */ 334228753Smm assertMakeDir(target, 0775); 335228753Smm assertEqualInt(0, chdir(target)); 336228753Smm 337228753Smm /* 338228753Smm * Use the tar program to create an archive. 339228753Smm */ 340228753Smm r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err", 341228753Smm testprog); 342228753Smm failure("Error invoking \"%s cf archive --format=ustar\"", testprog); 343228753Smm assertEqualInt(r, 0); 344228753Smm 345228753Smm /* Verify that nothing went to stdout. */ 346228753Smm assertEmptyFile("pack.out"); 347228753Smm /* Stderr is non-empty, since there are a bunch of files 348228753Smm * with filenames too long to archive. */ 349228753Smm 350228753Smm /* 351228753Smm * Use tar to unpack the archive into another directory. 352228753Smm */ 353228753Smm r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog); 354228753Smm failure("Error invoking %s xf archive", testprog); 355228753Smm assertEqualInt(r, 0); 356228753Smm 357228753Smm /* Verify that nothing went to stdout or stderr. */ 358228753Smm assertEmptyFile("unpack.err"); 359228753Smm assertEmptyFile("unpack.out"); 360228753Smm 361228753Smm verify_tree(LIMIT_USTAR); 362228753Smm assertEqualInt(0, chdir("../..")); 363228753Smm} 364228753Smm 365228753SmmDEFINE_TEST(test_copy) 366228753Smm{ 367228753Smm assertUmask(0); 368228753Smm create_tree(); /* Create sample files in "original" dir. */ 369228753Smm 370228753Smm /* Test simple "tar -c | tar -x" pipeline copy. */ 371228753Smm copy_basic(); 372228753Smm 373228753Smm /* Same, but constrain to ustar format. */ 374228753Smm copy_ustar(); 375228753Smm} 376