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 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 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 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 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 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 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 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