Path: blob/main/contrib/libarchive/tar/test/test_copy.c
39507 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2003-2007 Tim Kientzle4* All rights reserved.5*/6#include "test.h"78#if defined(__CYGWIN__)9# include <limits.h>10# include <sys/cygwin.h>11#endif12#if defined(_WIN32) && !defined(__CYGWIN__)13# include <direct.h>14#endif1516/*17* Try to figure out how deep we can go in our tests. Assumes that18* the first call to this function has the longest starting cwd (which19* is currently "<testdir>/original"). This is mostly to work around20* limits in our Win32 support.21*22* Background: On Posix systems, PATH_MAX is merely a limit on the23* length of the string passed into a system call. By repeatedly24* calling chdir(), you can work with arbitrarily long paths on such25* systems. In contrast, Win32 APIs apply PATH_MAX limits to the full26* absolute path, so the permissible length of a system call argument27* varies with the cwd. Some APIs actually enforce limits28* significantly less than PATH_MAX to ensure that you can create29* files within the current working directory. The Win32 limits also30* apply to Cygwin before 1.7.31*32* Someday, I want to convert the Win32 support to use newer33* wide-character paths with '\\?\' prefix, which has a 32k PATH_MAX34* instead of the rather anemic 260 character limit of the older35* system calls. Then we can drop this mess (unless we want to36* continue to special-case Cygwin 1.5 and earlier).37*/38static int39compute_loop_max(void)40{41#if defined(_WIN32) && !defined(__CYGWIN__)42static int LOOP_MAX = 0;43char buf[MAX_PATH];44size_t cwdlen;4546if (LOOP_MAX == 0) {47assert(_getcwd(buf, MAX_PATH) != NULL);48cwdlen = strlen(buf);49/* 12 characters = length of 8.3 filename */50/* 4 characters = length of "/../" used in symlink tests */51/* 1 character = length of extra "/" separator */52LOOP_MAX = MAX_PATH - (int)cwdlen - 12 - 4 - 1;53}54return LOOP_MAX;55#elif defined(__CYGWIN__) && !defined(HAVE_CYGWIN_CONV_PATH)56static int LOOP_MAX = 0;57if (LOOP_MAX == 0) {58char wbuf[PATH_MAX];59char pbuf[PATH_MAX];60size_t wcwdlen;61size_t pcwdlen;62size_t cwdlen;63assert(getcwd(pbuf, PATH_MAX) != NULL);64pcwdlen = strlen(pbuf);65cygwin_conv_to_full_win32_path(pbuf, wbuf);66wcwdlen = strlen(wbuf);67cwdlen = ((wcwdlen > pcwdlen) ? wcwdlen : pcwdlen);68/* Cygwin helper needs an extra few characters. */69LOOP_MAX = PATH_MAX - (int)cwdlen - 12 - 4 - 4;70}71return LOOP_MAX;72#else73/* cygwin-1.7 ends up here, along with "normal" unix */74return 200; /* restore pre-r278 depth */75#endif76}7778/* filenames[i] is a distinctive filename of length i. */79/* To simplify interpreting failures, each filename ends with a80* decimal integer which is the length of the filename. E.g., A81* filename ending in "_92" is 92 characters long. To detect errors82* which drop or misplace characters, the filenames use a repeating83* "abcdefghijklmnopqrstuvwxyz..." pattern. */84static char *filenames[201];8586static void87compute_filenames(void)88{89char buff[250];90size_t i,j;9192filenames[0] = strdup("");93filenames[1] = strdup("1");94filenames[2] = strdup("a2");95for (i = 3; i < sizeof(filenames)/sizeof(filenames[0]); ++i) {96/* Fill with "abcdefghij..." */97for (j = 0; j < i; ++j)98buff[j] = 'a' + (j % 26);99buff[j--] = '\0';100/* Work from the end to fill in the number portion. */101buff[j--] = '0' + (i % 10);102if (i > 9) {103buff[j--] = '0' + ((i / 10) % 10);104if (i > 99)105buff[j--] = '0' + (char)(i / 100);106}107buff[j] = '_';108/* Guard against obvious screwups in the above code. */109assertEqualInt(strlen(buff), i);110filenames[i] = strdup(buff);111}112}113114static void115create_tree(void)116{117char buff[260];118char buff2[260];119int i;120int LOOP_MAX;121122compute_filenames();123124/* Log that we'll be omitting some checks. */125if (!canSymlink()) {126skipping("Symlink checks");127}128129assertMakeDir("original", 0775);130assertEqualInt(0, chdir("original"));131LOOP_MAX = compute_loop_max();132133assertMakeDir("f", 0775);134assertMakeDir("l", 0775);135assertMakeDir("m", 0775);136assertMakeDir("s", 0775);137assertMakeDir("d", 0775);138139for (i = 1; i < LOOP_MAX; i++) {140failure("Internal sanity check failed: i = %d", i);141assert(filenames[i] != NULL);142143snprintf(buff, sizeof(buff), "f/%s", filenames[i]);144assertMakeFile(buff, 0777, buff);145146/* Create a link named "l/abcdef..." to the above. */147snprintf(buff2, sizeof(buff2), "l/%s", filenames[i]);148assertMakeHardlink(buff2, buff);149150/* Create a link named "m/abcdef..." to the above. */151snprintf(buff2, sizeof(buff2), "m/%s", filenames[i]);152assertMakeHardlink(buff2, buff);153154if (canSymlink()) {155/* Create a symlink named "s/abcdef..." to the above. */156snprintf(buff, sizeof(buff), "s/%s", filenames[i]);157snprintf(buff2, sizeof(buff2), "../f/%s", filenames[i]);158failure("buff=\"%s\" buff2=\"%s\"", buff, buff2);159assertMakeSymlink(buff, buff2, 0);160}161/* Create a dir named "d/abcdef...". */162buff[0] = 'd';163failure("buff=\"%s\"", buff);164assertMakeDir(buff, 0775);165}166167assertEqualInt(0, chdir(".."));168}169170#define LIMIT_NONE 200171#define LIMIT_USTAR 100172173static void174verify_tree(size_t limit, const char *format)175{176char name1[260];177char name2[260];178size_t i, LOOP_MAX;179180LOOP_MAX = compute_loop_max();181182/* Generate the names we know should be there and verify them. */183for (i = 1; i < LOOP_MAX; i++) {184/* Verify a file named "f/abcdef..." */185snprintf(name1, sizeof(name1), "f/%s", filenames[i]);186if (i <= limit) {187failure("Verifying %s", format);188assertFileExists(name1);189assertFileContents(name1, (int)strlen(name1), name1);190}191192snprintf(name2, sizeof(name2), "l/%s", filenames[i]);193if (i + 2 <= limit) {194/* Verify hardlink "l/abcdef..." */195failure("Verifying %s", format);196assertIsHardlink(name1, name2);197/* Verify hardlink "m/abcdef..." */198name2[0] = 'm';199assertIsHardlink(name1, name2);200}201202if (canSymlink()) {203/* Verify symlink "s/abcdef..." */204snprintf(name1, sizeof(name1), "s/%s", filenames[i]);205snprintf(name2, sizeof(name2), "../f/%s", filenames[i]);206if (strlen(name2) <= limit) {207failure("Verifying %s", format);208assertIsSymlink(name1, name2, 0);209}210}211212/* Verify dir "d/abcdef...". */213snprintf(name1, sizeof(name1), "d/%s", filenames[i]);214if (i + 1 <= limit) { /* +1 for trailing slash */215failure("Verifying %s", format);216if (assertIsDir(name1, -1)) {217/* TODO: opendir/readdir this218* directory and make sure219* it's empty.220*/221}222}223}224225#if !defined(_WIN32) || defined(__CYGWIN__)226{227const char *dp;228/* Now make sure nothing is there that shouldn't be. */229for (dp = "dflms"; *dp != '\0'; ++dp) {230DIR *d;231struct dirent *de;232char dir[2];233dir[0] = *dp; dir[1] = '\0';234d = opendir(dir);235failure("Unable to open dir '%s' for testing %s", dir, format);236if (!assert(d != NULL))237continue;238while ((de = readdir(d)) != NULL) {239char *p = de->d_name;240if (p[0] == '.')241continue;242switch(dp[0]) {243case 'l': case 'm': case 'd':244failure("strlen(p)=%zu", strlen(p));245assert(strlen(p) < limit);246assertEqualString(p,247filenames[strlen(p)]);248break;249case 'f': case 's':250failure("strlen(p)=%zu", strlen(p));251assert(strlen(p) < limit + 1);252assertEqualString(p,253filenames[strlen(p)]);254break;255default:256failure("File %s shouldn't be here", p);257assert(0);258}259}260closedir(d);261}262}263#endif264}265266static void267copy_basic(const char *extra_args, const char *name)268{269int r;270271/* NOTE: for proper operation on cygwin-1.5 and windows, the272* length of the name of the directory below must be273* less than or equal to the length of the name of the original274* directory, "original" This restriction derives from the275* extremely limited pathname lengths on those platforms.276*/277assertMakeDir(name, 0775);278assertEqualInt(0, chdir(name));279280/*281* Use the tar program to create an archive.282*/283r = systemf("%s cf archive %s -C ../original f d l m s >pack.out 2>pack.err",284testprog, extra_args);285failure("Error invoking \"%s cf archive %s\"", testprog, extra_args);286assertEqualInt(r, 0);287288/* Verify that nothing went to stdout or stderr. */289assertEmptyFile("pack.err");290assertEmptyFile("pack.out");291292/*293* Use tar to unpack the archive into another directory.294*/295r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);296failure("Error invoking %s xf archive", testprog);297assertEqualInt(r, 0);298299/* Verify that nothing went to stdout or stderr. */300assertEmptyFile("unpack.err");301assertEmptyFile("unpack.out");302303verify_tree(LIMIT_NONE, name);304assertEqualInt(0, chdir(".."));305}306307static void308copy_ustar(void)309{310const char *target = "ustar";311int r;312313/* NOTE: for proper operation on cygwin-1.5 and windows, the314* length of the name of the directory below, "ustar", must be315* less than or equal to the length of the name of the original316* directory, "original" This restriction derives from the317* extremely limited pathname lengths on those platforms.318*/319assertMakeDir(target, 0775);320assertEqualInt(0, chdir(target));321322/*323* Use the tar program to create an archive.324*/325r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err",326testprog);327failure("Error invoking \"%s cf archive --format=ustar\"", testprog);328assertEqualInt(r, 0);329330/* Verify that nothing went to stdout. */331assertEmptyFile("pack.out");332/* Stderr is non-empty, since there are a bunch of files333* with filenames too long to archive. */334335/*336* Use tar to unpack the archive into another directory.337*/338r = systemf("%s xf archive >unpack.out 2>unpack.err", testprog);339failure("Error invoking %s xf archive", testprog);340assertEqualInt(r, 0);341342/* Verify that nothing went to stdout or stderr. */343assertEmptyFile("unpack.err");344assertEmptyFile("unpack.out");345346verify_tree(LIMIT_USTAR, "ustar");347assertEqualInt(0, chdir(".."));348}349350DEFINE_TEST(test_copy)351{352assertUmask(0);353create_tree(); /* Create sample files in "original" dir. */354355/* Test simple "tar -c | tar -x" pipeline copy. */356copy_basic("", "default");357358/* Same, but constrain to ustar format. */359copy_ustar();360361/* Same, but with pax format. */362copy_basic(" --format pax", "pax");363}364365366