bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 3/3] getcwd: enhance tests


From: Eric Blake
Subject: [PATCH 3/3] getcwd: enhance tests
Date: Mon, 25 Apr 2011 16:28:27 -0600

* tests/test-getcwd-lgpl.c: New file, taken from...
* tests/test-getcwd.c: ...old contents.  Rewrite this file to
repeat m4 long path stress tests.
* modules/getcwd-lgpl-tests: New module.
* modules/getcwd-tests (Depends-on): Depend on lgpl tests.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                 |    7 ++
 modules/getcwd-lgpl-tests |   12 +++
 modules/getcwd-tests      |    6 +-
 tests/test-getcwd-lgpl.c  |   76 ++++++++++++++++
 tests/test-getcwd.c       |  220 +++++++++++++++++++++++++++++++++++++--------
 5 files changed, 280 insertions(+), 41 deletions(-)
 create mode 100644 modules/getcwd-lgpl-tests
 create mode 100644 tests/test-getcwd-lgpl.c

diff --git a/ChangeLog b/ChangeLog
index 1105b88..c145ebc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2011-04-25  Eric Blake  <address@hidden>

+       getcwd: enhance tests
+       * tests/test-getcwd-lgpl.c: New file, taken from...
+       * tests/test-getcwd.c: ...old contents.  Rewrite this file to
+       repeat m4 long path stress tests.
+       * modules/getcwd-lgpl-tests: New module.
+       * modules/getcwd-tests (Depends-on): Depend on lgpl tests.
+
        getcwd-lgpl: new module
        * modules/getcwd-lgpl: New module.
        * doc/posix-functions/getcwd.texi (getcwd): Document it.
diff --git a/modules/getcwd-lgpl-tests b/modules/getcwd-lgpl-tests
new file mode 100644
index 0000000..07fdf73
--- /dev/null
+++ b/modules/getcwd-lgpl-tests
@@ -0,0 +1,12 @@
+Files:
+tests/test-getcwd-lgpl.c
+tests/signature.h
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-getcwd-lgpl
+check_PROGRAMS += test-getcwd-lgpl
diff --git a/modules/getcwd-tests b/modules/getcwd-tests
index 016961f..2187acc 100644
--- a/modules/getcwd-tests
+++ b/modules/getcwd-tests
@@ -1,9 +1,11 @@
 Files:
 tests/test-getcwd.c
-tests/signature.h
-tests/macros.h

 Depends-on:
+errno
+fcntl-h
+getcwd-lgpl
+sys_stat

 configure.ac:

diff --git a/tests/test-getcwd-lgpl.c b/tests/test-getcwd-lgpl.c
new file mode 100644
index 0000000..18fc74f
--- /dev/null
+++ b/tests/test-getcwd-lgpl.c
@@ -0,0 +1,76 @@
+/* Test of getcwd() function.
+   Copyright (C) 2009-2011 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (getcwd, char *, (char *, size_t));
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macros.h"
+
+int
+main (int argc, char **argv)
+{
+  char *pwd1;
+  char *pwd2;
+  /* If the user provides an argument, attempt to chdir there first.  */
+  if (1 < argc)
+    {
+      if (chdir (argv[1]) == 0)
+        printf ("changed to directory %s\n", argv[1]);
+    }
+
+  pwd1 = getcwd (NULL, 0);
+  ASSERT (pwd1 && *pwd1);
+  if (1 < argc)
+    printf ("cwd=%s\n", pwd1);
+
+  /* Make sure the result is usable.  */
+  ASSERT (chdir (pwd1) == 0);
+  ASSERT (chdir ("././.") == 0);
+
+  /* Make sure that result is normalized.  */
+  pwd2 = getcwd (NULL, 0);
+  ASSERT (pwd2);
+  ASSERT (strcmp (pwd1, pwd2) == 0);
+  free (pwd2);
+  {
+    size_t len = strlen (pwd1);
+    ssize_t i = len - 10;
+    if (i < 0)
+      i = 0;
+    pwd2 = malloc (len + 2);
+    for ( ; i < len; i++)
+      ASSERT (getcwd (pwd2, i) == NULL);
+    pwd2 = getcwd (pwd2, len + 1);
+    ASSERT (pwd2);
+    pwd2[len] = '/';
+    pwd2[len + 1] = '\0';
+  }
+  ASSERT (strstr (pwd2, "/./") == NULL);
+  ASSERT (strstr (pwd2, "/../") == NULL);
+
+  free (pwd1);
+  free (pwd2);
+
+  return 0;
+}
diff --git a/tests/test-getcwd.c b/tests/test-getcwd.c
index 18fc74f..613c281 100644
--- a/tests/test-getcwd.c
+++ b/tests/test-getcwd.c
@@ -18,59 +18,201 @@

 #include <unistd.h>

-#include "signature.h"
-SIGNATURE_CHECK (getcwd, char *, (char *, size_t));
-
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>

 #include "macros.h"

-int
-main (int argc, char **argv)
+#if ! HAVE_GETPAGESIZE
+# define getpagesize() 0
+#endif
+
+/* This size is chosen to be larger than PATH_MAX (4k), yet smaller than
+   the 16kB pagesize on ia64 linux.  Those conditions make the code below
+   trigger a bug in glibc's getcwd implementation before 2.4.90-10.  */
+#define TARGET_LEN (5 * 1024)
+
+static int
+test_abort_bug (void)
 {
-  char *pwd1;
-  char *pwd2;
-  /* If the user provides an argument, attempt to chdir there first.  */
-  if (1 < argc)
+  char const *dir_name = "confdir-14B---";
+  char *cwd;
+  size_t initial_cwd_len;
+  int fail = 0;
+  size_t desired_depth;
+  size_t d;
+
+  /* The bug is triggered when PATH_MAX < getpagesize (), so skip
+     this relative expensive and invasive test if that's not true.  */
+  if (getpagesize () <= PATH_MAX)
+    return 0;
+
+  cwd = getcwd (NULL, 0);
+  if (cwd == NULL)
+    return 0;
+
+  initial_cwd_len = strlen (cwd);
+  free (cwd);
+  desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
+                   / (1 + strlen (dir_name)));
+  for (d = 0; d < desired_depth; d++)
+    {
+      if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
+        {
+          fail = 3; /* Unable to construct deep hierarchy.  */
+          break;
+        }
+    }
+
+  /* If libc has the bug in question, this invocation of getcwd
+     results in a failed assertion.  */
+  cwd = getcwd (NULL, 0);
+  if (cwd == NULL)
+    fail = 4; /* getcwd failed.  This is ok, and expected.  */
+  free (cwd);
+
+  /* Call rmdir first, in case the above chdir failed.  */
+  rmdir (dir_name);
+  while (0 < d--)
     {
-      if (chdir (argv[1]) == 0)
-        printf ("changed to directory %s\n", argv[1]);
+      if (chdir ("..") < 0)
+        break;
+      rmdir (dir_name);
     }

-  pwd1 = getcwd (NULL, 0);
-  ASSERT (pwd1 && *pwd1);
-  if (1 < argc)
-    printf ("cwd=%s\n", pwd1);
+  return 0;
+}

-  /* Make sure the result is usable.  */
-  ASSERT (chdir (pwd1) == 0);
-  ASSERT (chdir ("././.") == 0);
+/* The length of this name must be 8.  */
+#define DIR_NAME "confdir3"
+#define DIR_NAME_LEN 8
+#define DIR_NAME_SIZE (DIR_NAME_LEN + 1)

-  /* Make sure that result is normalized.  */
-  pwd2 = getcwd (NULL, 0);
-  ASSERT (pwd2);
-  ASSERT (strcmp (pwd1, pwd2) == 0);
-  free (pwd2);
+/* The length of "../".  */
+#define DOTDOTSLASH_LEN 3
+
+/* Leftover bytes in the buffer, to work around library or OS bugs.  */
+#define BUF_SLOP 20
+
+static int
+test_long_name (void)
+{
+#ifndef PATH_MAX
+  /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
+     at least not on a local file system.  And if we were to start worrying
+     about remote file systems, we'd have to enable the wrapper function
+     all of the time, just to be safe.  That's not worth the cost.  */
+  return 0;
+#elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
+        - DIR_NAME_SIZE - BUF_SLOP) \
+       <= PATH_MAX)
+  /* FIXME: Assuming there's a system for which this is true,
+     this should be done in a compile test.  */
+  return 0;
+#else
+  char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
+           + DIR_NAME_SIZE + BUF_SLOP];
+  char *cwd = getcwd (buf, PATH_MAX);
+  size_t initial_cwd_len;
+  size_t cwd_len;
+  int fail = 0;
+  size_t n_chdirs = 0;
+
+  if (cwd == NULL)
+    return 10;
+
+  cwd_len = initial_cwd_len = strlen (cwd);
+
+  while (1)
+    {
+      size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
+      char *c = NULL;
+
+      cwd_len += DIR_NAME_SIZE;
+      /* If mkdir or chdir fails, it could be that this system cannot create
+         any file with an absolute name longer than PATH_MAX, such as cygwin.
+         If so, leave fail as 0, because the current working directory can't
+         be too long for getcwd if it can't even be created.  For other
+         errors, be pessimistic and consider that as a failure, too.  */
+      if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
+        {
+          if (! (errno == ERANGE || errno == ENAMETOOLONG))
+            fail = 20;
+          break;
+        }
+
+      if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
+        {
+          c = getcwd (buf, PATH_MAX);
+          if (!c && errno == ENOENT)
+            {
+              fail = 11;
+              break;
+            }
+          if (c || ! (errno == ERANGE || errno == ENAMETOOLONG))
+            {
+              fail = 21;
+              break;
+            }
+        }
+
+      if (dotdot_max <= cwd_len - initial_cwd_len)
+        {
+          if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
+            break;
+          c = getcwd (buf, cwd_len + 1);
+          if (!c)
+            {
+              if (! (errno == ERANGE || errno == ENOENT
+                     || errno == ENAMETOOLONG))
+                {
+                  fail = 22;
+                  break;
+                }
+              if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
+                {
+                  fail = 12;
+                  break;
+                }
+            }
+        }
+
+      if (c && strlen (c) != cwd_len)
+        {
+          fail = 23;
+          break;
+        }
+      ++n_chdirs;
+    }
+
+  /* Leaving behind such a deep directory is not polite.
+     So clean up here, right away, even though the driving
+     shell script would also clean up.  */
   {
-    size_t len = strlen (pwd1);
-    ssize_t i = len - 10;
-    if (i < 0)
-      i = 0;
-    pwd2 = malloc (len + 2);
-    for ( ; i < len; i++)
-      ASSERT (getcwd (pwd2, i) == NULL);
-    pwd2 = getcwd (pwd2, len + 1);
-    ASSERT (pwd2);
-    pwd2[len] = '/';
-    pwd2[len + 1] = '\0';
+    size_t i;
+
+    /* Try rmdir first, in case the chdir failed.  */
+    rmdir (DIR_NAME);
+    for (i = 0; i <= n_chdirs; i++)
+      {
+        if (chdir ("..") < 0)
+          break;
+        if (rmdir (DIR_NAME) != 0)
+          break;
+      }
   }
-  ASSERT (strstr (pwd2, "/./") == NULL);
-  ASSERT (strstr (pwd2, "/../") == NULL);

-  free (pwd1);
-  free (pwd2);
+  return fail;
+#endif
+}

-  return 0;
+int
+main (int argc, char **argv)
+{
+  return test_abort_bug () + test_long_name ();
 }
-- 
1.7.4.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]