delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/01/18/09:26:58

Message-ID: <3E297258.50906@mif.vu.lt>
Date: Sat, 18 Jan 2003 16:27:20 +0100
From: Laurynas Biveinis <laurynas DOT biveinis AT mif DOT vu DOT lt>
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 <rich AT phekda DOT freeserve DOT co DOT uk>
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>
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/<drive> instead of <drive>:;
> * 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <libc/symlink.h>
#include <sys/stat.h>

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


- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019