Date: Thu, 19 Dec 2002 17:31:59 +0000 From: "Richard Dawe" Sender: rich AT phekda DOT freeserve DOT co DOT uk To: djgpp-workers AT delorie DOT com X-Mailer: Emacs 21.3.50 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6 Subject: __solve_symlinks and multiple slashes [PATCH] Message-Id: Reply-To: djgpp-workers AT delorie DOT com Hello. Below is a patch that fixes the issues with __solve_symlinks and multiple consecutive slashes. I've also heavily updated the test program to test these cases and also some cases involving '..' and relative/absolute paths. The last dynamic test case fails, because it uses one '..' too many. I've tested these patches by running the fileutils test suite with rebuilt fileutils, sh-utils and textutils. There were no regressions in the fileutils test suite - in fact, it worked much better, since the {file,sh-,text}utils could cope with paths like c://tmp/foo. OK to commit? Bye, Rich =] Index: src/libc/compat/unistd/xsymlink.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/compat/unistd/xsymlink.c,v retrieving revision 1.5 diff -p -c -3 -r1.5 xsymlink.c *** src/libc/compat/unistd/xsymlink.c 26 Aug 2000 18:46:09 -0000 1.5 --- src/libc/compat/unistd/xsymlink.c 19 Dec 2002 17:27:56 -0000 *************** *** 1,3 **** --- 1,4 ---- + /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Written by Laurynas Biveinis */ *************** int __solve_symlinks(const char * __syml *** 163,172 **** return 1; } static void advance(char ** s, char ** e) { *s = strpbrk(*s + 1, "/\\"); if (*s) ! (*s)++; ! *e = strpbrk(*e + 1, "/\\"); } --- 164,180 ---- return 1; } + /* Advance to the next portion of the path. Cope with multiple slashes. */ static void advance(char ** s, char ** e) { *s = strpbrk(*s + 1, "/\\"); if (*s) ! { ! while ((**s == '/') || (**s == '\\')) ! (*s)++; ! } ! ! while ((**e == '/') || (**e == '\\')) ! (*e)++; ! *e = strpbrk(*e, "/\\"); } Index: tests/libc/compat/unistd/xsymlink.c =================================================================== RCS file: /cvs/djgpp/djgpp/tests/libc/compat/unistd/xsymlink.c,v retrieving revision 1.4 diff -p -c -3 -r1.4 xsymlink.c *** tests/libc/compat/unistd/xsymlink.c 26 May 2002 16:12:19 -0000 1.4 --- tests/libc/compat/unistd/xsymlink.c 19 Dec 2002 17:27:57 -0000 *************** *** 9,19 **** * 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 * Any unhandled cases are more than welcome. * * And following are failure tests: ! * 11. Simple symlink loop. ! * 12. Symlink loop across directories * * TODO: device a test, where symlink expands to absolute path, which, * in turn, has symlinks somewhere inside. There was such a test once, but --- 9,28 ---- * 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: + * 1. An absolute path using '..' to navigate through the directories. + * 2. A relative path using a drive letter and '..'. + * 3. A relative path using a drive letter and one too many '..' over 2. + * * And following are failure tests: ! * 1. Simple symlink loop. ! * 2. Symlink loop across directories * * TODO: device a test, where symlink expands to absolute path, which, * in turn, has symlinks somewhere inside. There was such a test once, but *************** *** 24,39 **** #include #include #include #include #include ! static void test_success(const char * slink, const char * expect); ! static void test_failure(const char * slink); ! static int test_num = 0; int main(void) { if (!__file_exists("test1") || !__file_exists("test4") || !__file_exists("test5") || !__file_exists("dirtest") || !__file_exists("fail1") || !__file_exists("fail2") || --- 33,89 ---- #include #include #include + #include #include #include ! 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 int test_success(const int test_num, ! const char * slink, ! const char * expect); ! ! static int test_failure(const int test_num, const char * slink); int main(void) { + int ok = 1; + int ret, i, j; + if (!__file_exists("test1") || !__file_exists("test4") || !__file_exists("test5") || !__file_exists("dirtest") || !__file_exists("fail1") || !__file_exists("fail2") || *************** int main(void) *** 46,68 **** fprintf(stderr, "Required data files not found"); exit(1); } ! printf("Running readlink() testsuite:\n"); ! test_success("test1", "file1"); ! test_success("test4", "file2"); ! test_success("dir1/test1", "dir1/file1"); ! test_success("dirtest/file1", "dir1/file1"); ! test_success("dirtest/test1", "dir1/file1"); ! test_success("dirtest/test2", "/dev/env/DJDIR/bin/gcc.exe"); ! test_success("dirtest/test3", "c:\\autoexec.bat"); ! test_success("dirtest/test6/file", "dir1/dir2/file"); ! test_success("dir1/test7", "file"); ! test_failure("fail1"); ! test_failure("fail3"); ! printf("Done.\n"); ! return 0; } ! static void test_success(const char * slink, const char * expect) { char real_name[FILENAME_MAX + 1]; char real_fixed[FILENAME_MAX + 1]; --- 96,242 ---- 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++) { ! ret = test_success(i + 1, tests_success[i].src, tests_success[i].target); ! if (!ret) ! ok = 0; ! } ! ! /* 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++) {;} ! ! j = 0; ! ! /* Try tests_success[0] with an absolute path with '..'. */ ! j++; ! ! 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", j, test.src); ! ret = test_success(j, test.src, test.target); ! if (!ret) ! ok = 0; ! ! /* Try tests_success[0] with a drive-letter and relative path ! * with '..'. */ ! j++; ! ! 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", j, test.src); ! ret = test_success(j, test.src, test.target); ! if (!ret) ! ok = 0; ! } ! else ! { ! printf("Test %d: No drive letter - skipping\n", j); ! } ! ! /* Try tests_success[0] with a drive-letter and relative path ! * with too many '..'. */ ! j++; ! ! 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", j, test.src); ! ret = test_success(j, test.src, test.target); ! if (!ret) ! ok = 0; ! } ! else ! { ! printf("Test %d: No drive letter - skipping\n", j); ! } ! } ! ! puts("Tests that check __solve_symlinks() failure cases:"); ! for (i = 0; i < n_tests_failure; i++) { ! ret = test_failure(i + 1, tests_failure[i]); ! if (!ret) ! ok = 0; ! } ! ! if (ok) { ! puts("PASS"); ! return(EXIT_SUCCESS); ! } else { ! puts("FAIL"); ! return(EXIT_FAILURE); ! } } ! static int test_success(const int test_num, ! const char * slink, ! const char * expect) { char real_name[FILENAME_MAX + 1]; char real_fixed[FILENAME_MAX + 1]; *************** static void test_success(const char * sl *** 70,76 **** char err_buf[50]; if (!__solve_symlinks(slink, real_name)) { ! sprintf(err_buf, "Test %d failed ", ++test_num); perror(err_buf); exit(1); } --- 244,250 ---- char err_buf[50]; if (!__solve_symlinks(slink, real_name)) { ! sprintf(err_buf, "Test %d failed ", test_num); perror(err_buf); exit(1); } *************** static void test_success(const char * sl *** 83,94 **** test_num); fprintf(stderr, "Returned path: %s\n", real_fixed); fprintf(stderr, "Expected path: %s\n", expect_fixed); ! exit(1); } printf("Test %d passed\n", test_num); } ! static void test_failure(const char * slink) { char buf[FILENAME_MAX + 1]; char err_buf[50]; --- 257,269 ---- test_num); fprintf(stderr, "Returned path: %s\n", real_fixed); fprintf(stderr, "Expected path: %s\n", expect_fixed); ! return(0); } printf("Test %d passed\n", test_num); + return(1); } ! static int test_failure(const int test_num, const char * slink) { char buf[FILENAME_MAX + 1]; char err_buf[50]; *************** static void test_failure(const char * sl *** 97,110 **** { fprintf(stderr, "Test %d failed - __solve_symlinks suceeds when it should fail\n", ! ++test_num); exit(1); } if (errno != ELOOP) { sprintf(err_buf, "Test %d failed - wrong errno returned ", test_num); perror(err_buf); ! exit(1); } printf("Test %d passed\n", test_num); } --- 272,286 ---- { fprintf(stderr, "Test %d failed - __solve_symlinks suceeds when it should fail\n", ! test_num); exit(1); } if (errno != ELOOP) { sprintf(err_buf, "Test %d failed - wrong errno returned ", test_num); perror(err_buf); ! return(0); } printf("Test %d passed\n", test_num); + return(1); }