Message-ID: <3E297258.50906@mif.vu.lt> Date: Sat, 18 Jan 2003 16:27:20 +0100 From: Laurynas Biveinis Organization: VU MIF User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.2.1) Gecko/20021130 X-Accept-Language: lt, en, en-us MIME-Version: 1.0 To: djgpp-workers AT delorie DOT com, Richard Dawe Subject: Re: Take on __solve_symlinks() References: <3E286C07 DOT 7040200 AT mif DOT vu DOT lt> <3E2943DA DOT 523770FC AT phekda DOT freeserve DOT co DOT uk> In-Reply-To: <3E2943DA.523770FC@phekda.freeserve.co.uk> Content-Type: multipart/mixed; boundary="------------070504000101040808090705" X-OriginalArrivalTime: 18 Jan 2003 14:25:22.0034 (UTC) FILETIME=[6F22A520:01C2BEFD] Reply-To: djgpp-workers AT delorie DOT com This is a multi-part message in MIME format. --------------070504000101040808090705 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Richard Dawe wrote: >>Well, maybe I'm on drugs, > > > Maybe the problem is that I'm not on drugs (caffeine)! ;) Nope, it's quite clear that the problems are on my side :) > I reverted my changes and I'm now running fileutils, sh-utils and textutils > built with DJGPP CVS HEAD. But the test program still fails for me: [...] > FAIL > > Do you expect this test to fail? No I don't, but I didn't see any failures, probably because we're running two different test programs - I've cleaned up the test program a bit. I'm attaching it. Could you tell if it fails with your testcases? > * symlinks between drives; Will see, the problem is that not everybody has multiple drives... > * paths using /dev/ instead of :; > * paths using environment variables in paths - e.g.: /dev/env/FOO. OK. Will add those tests too. > Basically tests that mix absolute and relative paths in every possible way. > Most of the tests are too "normal" at the moment IMHO. Not that I am > criticising. See that part about drugs above :) > It's just that you may not have thought of bizarre filenames as > much as I have, while testing fileutils, etc. ;) Right. I was trying to make up tests according to the problem description, and probably failed. >> /* FIXME below, but note that you're insane to run this test >>really that deep :) */ >> test_success("c:../../../../../../../../../../../../../../../link", >>"c:/autoexec.bat"); > Why is this marked as a FIXME? > > Why is it insane? Sure, having more than 8 levels of directories is > unsupported on DOS, but the code should cope with excess ".."s. No I mean, the test will not work as expected if there won't be enough '../' to try to get above c:/ from the cwd on C:. I tried to make this test to expose the bug you're talking about - isn't this the same situation? (relative path with drive letter and trying to get above the root). -- Laurynas --------------070504000101040808090705 Content-Type: text/plain; name="test.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="test.c" /* Testsuite for __solve_symlinks() * There are following success tests: * 1. Simple case with symlink in current directory * 2. Recursive symlinks * 3. Symlink in subdirectory * 4. Real file in symlinked directory * 5. Symlink in symlinked directory * 6. Symlink in symlinked directory to UNIX-style absolute path * 7. The same with DOS-style absolute path * 8. Real file in a symlink subdir in a symlink subdir * 9. Symlink in a subdir to file in an upper dir * 10. 3 with a double-slash * 11. 7 with a double-slash * 12. 8 with a double-slash * 13. Regular file * Any unhandled cases are more than welcome. * * There are some tests based on the current-working directory: * 14. An absolute path using '..' to navigate through the directories. * 15. A relative path using a drive letter and '..'. * 16. A relative path using a drive letter and one too many '..' over 2. * * And following are failure tests: * 17. Simple symlink loop. * 18. Symlink loop across directories * * These are absolute path tests: * 19. /../symlink should be expanded correctly * * TODO: device a test, where symlink expands to absolute path, which, * in turn, has symlinks somewhere inside. There was such a test once, but * it was broken - it depended on testsuite location. */ #include #include #include #include #include #include #include #include /* Global test index */ static unsigned test_num = 0; /* 1, if all testsuite passes */ static int full_pass = 1; typedef struct { const char *src; /* Source: includes symlink(s) */ const char *target; /* Target of the symlink */ } test_success_t; static const test_success_t tests_success[] = { { "test1", "file1" }, { "test4", "file2" }, { "dir1/test1", "dir1/file1" }, { "dirtest/file1", "dir1/file1" }, { "dirtest/test1", "dir1/file1" }, { "dirtest/test2", "/dev/env/DJDIR/bin/gcc.exe" }, { "dirtest/test3", "c:\\autoexec.bat" }, { "dirtest/test6/file", "dir1/dir2/file" }, { "dir1/test7", "file" }, /* Check that __solve_symlinks copes with double-slashes. */ { "dir1//test1", "dir1/file1" }, { "dirtest//test3", "c:\\autoexec.bat" }, { "dirtest//test6//file", "dir1/dir2/file" }, /* Non-symlinks */ { "makefile", "makefile" } }; static const int n_tests_success = sizeof(tests_success) / sizeof(tests_success[0]); static const char *tests_failure[] = { "fail1", "fail3" }; static const int n_tests_failure = sizeof(tests_failure) / sizeof(tests_failure[0]); static void test_success(const char * slink, const char * expect); static void test_failure(const char * slink); int main(void) { int i; if (!__file_exists("test1") || !__file_exists("test4") || !__file_exists("test5") || !__file_exists("dirtest") || !__file_exists("fail1") || !__file_exists("fail2") || !__file_exists("fail3") || !__file_exists("dir1/fail1") || !__file_exists("dir1/test1") || !__file_exists("dir1/test2") || !__file_exists("dir1/test3") || !__file_exists("dir1/test4") || !__file_exists("dir1/test5") || !__file_exists("dir1/test6") || !__file_exists("dir1/test7") || access("dir1/dir2", D_OK)) { fprintf(stderr, "Required data files not found"); exit(1); } puts("Running __solve_symlinks() and readlink() testsuite:"); puts("Tests that check __solve_symlinks() works:"); for (i = 0; i < n_tests_success; i++) test_success(tests_success[i].src, tests_success[i].target); /* Construct some tests with relative paths. */ puts("Tests that check __solve_symlinks() based on current directory:"); { char cwd[PATH_MAX], path[PATH_MAX + 1]; test_success_t test; int n_slashes = 0; char *cwd_without_drive = cwd; char *ptr = NULL; if (getcwd(cwd, sizeof(cwd) - 1) == NULL) { fprintf(stderr, "Error: Unable to get current working directory\n"); return(EXIT_FAILURE); } if (cwd[0] && (cwd[1] == ':')) cwd_without_drive = cwd + 2; /* Count the number of slashes in the current directory => number * of '..' we can use. */ for (ptr = strpbrk(cwd, "/\\"), n_slashes = 0; ptr != NULL; ptr = strpbrk(ptr + 1, "/\\"), n_slashes++) {;} /* Try tests_success[0] with an absolute path with '..'. */ test_num++; strcpy(path, cwd); for (i = 0; i < n_slashes; i++) { strcat(path, "/.."); } strcat(path, cwd_without_drive); strcat(path, "/"); strcat(path, tests_success[0].src); test.src = path; test.target = tests_success[0].target; printf("Test %d: Solving %s\n", test_num, test.src); test_success(test.src, test.target); /* Try tests_success[0] with a drive-letter and relative path * with '..'. */ test_num++; if (cwd[0] && (cwd[1] == ':')) { path[0] = cwd[0]; path[1] = cwd[1]; path[2] = '\0'; for (i = 0; i < n_slashes; i++) { if (i) strcat(path, "/.."); else strcat(path, ".."); } strcat(path, cwd_without_drive); strcat(path, "/"); strcat(path, tests_success[0].src); test.src = path; test.target = tests_success[0].target; printf("Test %d: Solving %s\n", test_num, test.src); test_success(test.src, test.target); } else { printf("Test %d: No drive letter - skipping\n", test_num); } /* Try tests_success[0] with a drive-letter and relative path * with too many '..'. */ test_num++; if (cwd[0] && (cwd[1] == ':')) { path[0] = cwd[0]; path[1] = cwd[1]; path[2] = '\0'; for (i = 0; i <= n_slashes; i++) { if (i) strcat(path, "/.."); else strcat(path, ".."); } strcat(path, cwd_without_drive); strcat(path, "/"); strcat(path, tests_success[0].src); test.src = path; test.target = tests_success[0].target; printf("Test %d: Solving %s\n", test_num, test.src); test_success(test.src, test.target); } else { printf("Test %d: No drive letter - skipping\n", test_num); } } puts("Tests that check __solve_symlinks() failure cases:"); for (i = 0; i < n_tests_failure; i++) test_failure(tests_failure[i]); puts("2003 tests:"); symlink("c:/autoexec.bat", "/link"); test_success("c:/link", "c:/autoexec.bat"); test_success("/link", "c:/autoexec.bat"); test_success("c://link", "c:/autoexec.bat"); test_success("//link", "c:/autoexec.bat"); test_success("c:/./link", "c:/autoexec.bat"); test_success("/../link", "c:/autoexec.bat"); test_success("c:/../link", "c:/autoexec.bat"); test_success("/../../link", "c:/autoexec.bat"); test_success("c:/../../link", "c:/autoexec.bat"); /* FIXME below, but note that you're insane to run this test really that deep :) */ test_success("c:../../../../../../../../../../../../../../../link", "c:/autoexec.bat"); mkdir("c:/tdir", S_IWUSR); symlink("c:/config.sys", "c:/tdir/link"); test_success("c:/tdir/link", "c:/config.sys"); test_success("/tdir/link", "c:/config.sys"); test_success("c:/tdir/../.././tdir/link", "c:/config.sys"); test_success("/tdir/..//../.././../../tdir/link", "c:/config.sys"); /* FIXME below, but note that you're insane to run this test really that deep :) */ test_success("c:../../../../../../../../../../../../../../../tdir/link", "c:/config.sys"); unlink("c:/tdir/link"); rmdir("c:/tdir"); unlink("/link"); symlink("/dev/env/DJDIR/djgpp.env", "/dev/env/DJDIR/link"); test_success("/dev/env/DJDIR/link", "/dev/env/DJDIR/djgpp.env"); test_success("/dev/env/DJDIR/../djgpp/link", "/dev/env/DJDIR/djgpp.env"); /* FIXME */ unlink("/dev/env/DJDIR/link"); if (full_pass) { puts("PASS"); return(EXIT_SUCCESS); } else { puts("FAIL"); return(EXIT_FAILURE); } } static void test_success(const char * slink, const char * expect) { char real_name[FILENAME_MAX + 1]; char real_fixed[FILENAME_MAX + 1]; char expect_fixed[FILENAME_MAX + 1]; char err_buf[50]; ++test_num; if (!__solve_symlinks(slink, real_name)) { sprintf(err_buf, "Test %d failed ", test_num); perror(err_buf); full_pass = 0; } _fixpath(expect, expect_fixed); _fixpath(real_name, real_fixed); if (strcmp(real_fixed, expect_fixed)) { fprintf(stderr, "Test %d failed - __solve_symlinks returns wrong resolved path\n", test_num); fprintf(stderr, "Returned path: %s\n", real_fixed); fprintf(stderr, "Expected path: %s\n", expect_fixed); full_pass = 0; } printf("Test %d passed\n", test_num); } static void test_failure(const char * slink) { char buf[FILENAME_MAX + 1]; char err_buf[50]; errno = 0; test_num++; if (__solve_symlinks(slink, buf)) { fprintf(stderr, "Test %d failed - __solve_symlinks suceeds when it should fail\n", test_num); } if (errno != ELOOP) { sprintf(err_buf, "Test %d failed - wrong errno returned ", test_num); perror(err_buf); } printf("Test %d passed\n", test_num); } --------------070504000101040808090705--