[Parted-commits] GNU Parted Official Repository: Changes to 'master'

Jim Meyering meyering at alioth.debian.org
Wed May 23 20:56:19 UTC 2007


 .gitignore                     |    1 
 configure.ac                   |    2 
 libparted/tests/Makefile.am    |   16 ++
 libparted/tests/t1000-label.sh |   29 +++++
 m4/o-direct.m4                 |  235 +++++++++++++++++++++++++++++++++++++++++
 tests/Makefile.am              |   11 +
 tests/mkdtemp                  |  107 ++++++++++++++++++
 tests/test-lib.sh              |   28 ++--
 8 files changed, 407 insertions(+), 22 deletions(-)

New commits:
commit 873251291e04dec0f7ab0d5b7d65809981ed95b4
Author: Jim Meyering <jim at meyering.net>
Date:   Wed May 23 22:28:53 2007 +0200

    Don't fail all tests when "." lacks O_DIRECT support.
    
    I often build tools on a tmpfs file system (it's faster), and
    found that parted tests always failed there.  That's because it tries
    to open the "device" (a file) with O_DIRECT, and at least the linux tmpfs
    driver always fails with EINVAL in that case.
    
    So here's a patch that makes it work.
    Since the test may require writing in a directory like /tmp,
    to which others typically have write access, it is particularly
    careful about security (see the mkdtemp script below), in case
    "make check" is run by e.g., root.
    
    Don't fail all tests when "." lacks O_DIRECT support.
    Before, running "make check" on a file system that doesn't support
    O_DIRECT (e.g. tmpfs), would always fail.  Now, it works, as long as
    the test machinery can find a writable directory in which open with
    O_DIRECT *does* work.
    * m4/o-direct.m4: New file.  Find a directory/FS with O_DIRECT support.
    * configure.ac: Use the new macro.
    * libparted/tests/t1000-label.sh: New file.  Wrap the binary, so
    it can take advantage of the code that finds O_DIRECT supporting FS.
    * tests/mkdtemp: New file.  Required, since when running tests as
    root, we may have to create a temporary directory in a directory
    like /tmp that's writable by others.
    * tests/Makefile.am (EXTRA_DIST): Add mkdtemp.
    * tests/test-lib.sh: When creating test subdir, and setting up "trap",
    use the directory specified in $PARTED_USABLE_TEST_DIR.
    Don't set PATH here.  Now, that's done via the generated, and always-
    sourced, init.sh.  As a result, invoke parted via its full file name.

diff --git a/.gitignore b/.gitignore
index 28054a3..1bcbf96 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ configure
 doc/C/po/parted.8.pot
 doc/pt_BR/parted.8.pt_BR.po
 libparted.pc
+libparted/tests/init.sh
 libparted/tests/label
 libtool
 m4
diff --git a/configure.ac b/configure.ac
index 18a30d9..b21be56 100644
--- a/configure.ac
+++ b/configure.ac
@@ -171,6 +171,7 @@ AC_PROG_GCC_TRADITIONAL
 AM_PROG_CC_C_O
 
 gl_EARLY
+parted_FIND_USABLE_TEST_DIR
 
 dnl This test must come as early as possible after the compiler configuration
 dnl tests, because the choice of the file model can (in principle) affect
@@ -460,7 +461,6 @@ AC_C_INLINE
 AC_C_CONST
 AC_C_RESTRICT
 
-
 dnl Checks for library functions.
 AC_CHECK_FUNCS(sigaction)
 AC_CHECK_FUNCS(getuid)
diff --git a/libparted/tests/Makefile.am b/libparted/tests/Makefile.am
index 8c1e07b..12ad29f 100644
--- a/libparted/tests/Makefile.am
+++ b/libparted/tests/Makefile.am
@@ -1,12 +1,24 @@
 # This file is part of GNU Parted
-# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+# Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
 #
 # This file may be modified and/or distributed without restriction.
 
-TESTS = label
+TESTS = t1000-label.sh
+EXTRA_DIST = $(TESTS)
 bin_PROGRAMS     = label
 label_CFLAGS    = $(CHECK_CFLAGS) -I$(top_srcdir)/include
 label_LDADD     = $(CHECK_LIBS) $(top_builddir)/libparted/libparted.la
 label_SOURCES   = common.h common.c label.c
 
 MAINTAINERCLEANFILES = Makefile.in
+
+CLEANFILES = init.sh
+all: init.sh
+init.sh: Makefile.in
+	rm -f $@-t $@
+	echo 'PARTED_USABLE_TEST_DIR=$(PARTED_USABLE_TEST_DIR)' > $@-t
+	echo 'abs_top_srcdir=$(abs_top_srcdir)' >> $@-t
+	echo '. $(abs_top_srcdir)/tests/test-lib.sh' >> $@-t
+	echo 'PATH=$(abs_builddir)$(PATH_SEPARATOR)$$PATH; export PATH' >> $@-t
+	chmod a-w $@-t
+	mv $@-t $@
diff --git a/libparted/tests/t1000-label.sh b/libparted/tests/t1000-label.sh
new file mode 100755
index 0000000..34010b1
--- /dev/null
+++ b/libparted/tests/t1000-label.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Copyright (C) 2007 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 2 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+test_description='run the label unit tests in a directory supporting O_DIRECT'
+# This wrapper around the ./label binary is used to find a directory
+# in which one can open a file with the O_DIRECT flag.
+
+. ./init.sh
+
+test_expect_success \
+    'run the actual tests' 'label'
+
+test_done
diff --git a/m4/o-direct.m4 b/m4/o-direct.m4
new file mode 100644
index 0000000..ec84ef4
--- /dev/null
+++ b/m4/o-direct.m4
@@ -0,0 +1,235 @@
+#serial 1
+# Find a directory in which a disk-simulating file is usable by parted.
+# The problem is that on systems supporting O_DIRECT, open with O_DIRECT
+# fails for some file system types (e.g., tmpfs on linux-2.6.21).
+
+# Copyright (C) 2007 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# From Jim Meyering.
+
+# Set PARTED_USABLE_TEST_DIR to the name of the first usable directory
+# from the list below.  If none is usable, set it to the empty string.
+# Consider $TMPDIR only if it specifies an absolute name, and that
+# name contains no shell meta-character.  Likewise for $HOME.
+# The candidate directories:
+#   . $HOME $TMPDIR /tmp /var/tmp /dev/shm
+AC_DEFUN([parted_FIND_USABLE_TEST_DIR],
+[
+  AC_CACHE_CHECK([for a usable (O_DIRECT-supporting) temporary dir],
+    [parted_cv_func_open_O_DIRECT_temp_dir],
+    [
+      # First of all, if there is no O_DIRECT definition, use ".",
+      # and skip the run-test.
+      AC_EGREP_CPP([frobnozzle], [
+#include <fcntl.h>
+#ifdef O_DIRECT
+frobnozzle
+#endif
+		  ], pe_have_O_DIRECT=yes, pe_have_O_DIRECT=no)
+      if test $pe_have_O_DIRECT = no; then
+	  # With no O_DIRECT definition, "." is fine.
+	  pe_cand_dirs=.
+      else
+	  pe_cand_dirs=.
+	  for pe_dir in "$HOME" "$TMPDIR"; do
+	      case $pe_dir in
+	      /tmp) ;;
+	      /var/tmp) ;;
+	      /dev/shm) ;;
+	      /*) case $pe_dir in
+		  # Accept $HOME or $TMP only if the value is nice and boring.
+		  *[^/a-zA-Z0-9_.-]*) ;;
+		  *) pe_cand_dirs="$pe_cand_dirs $pe_dir";;
+		  esac
+	      esac
+	  done
+
+	  # This is the list of candidate directories.
+	  pe_cand_dirs="$pe_cand_dirs /tmp /var/tmp /dev/shm"
+
+	  PARTED_CANDIDATE_DIRS=$pe_cand_dirs
+	  export PARTED_CANDIDATE_DIRS
+
+	  AC_RUN_IFELSE(
+	    [AC_LANG_SOURCE(
+	      [[
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOGICAL_BLOCK_SIZE 4096
+static char g_buf[2 * LOGICAL_BLOCK_SIZE];
+
+static inline void *
+ptr_align (void const *ptr, size_t alignment)
+{
+  char const *p0 = ptr;
+  char const *p1 = p0 + alignment - 1;
+  return (void *) (p1 - (size_t) p1 % alignment);
+}
+
+static int
+create_input_file (char const *file, char const *buf, size_t n_bytes)
+{
+  int fd = open (file, O_CREAT | O_WRONLY, 0600);
+  if (fd < 0)
+    return 1;
+  if (write (fd, buf, n_bytes) != n_bytes)
+    {
+      close (fd);
+      return 1;
+    }
+  return !! close (fd);
+}
+
+static int
+try_o_direct (char const *file)
+{
+  char const *p = ptr_align (g_buf, LOGICAL_BLOCK_SIZE);
+  int fd;
+
+  if (!(p + LOGICAL_BLOCK_SIZE < g_buf + sizeof g_buf))
+    return 4;
+
+  fd = open (file, O_WRONLY | O_DIRECT);
+  if (fd < 0)
+    return 1;
+
+  if (write (fd, p, LOGICAL_BLOCK_SIZE) != LOGICAL_BLOCK_SIZE)
+    return 1;
+
+  return !! close (fd);
+}
+
+#undef stpcpy
+#define stpcpy(a, b) my_stpcpy (a, b)
+static char *
+my_stpcpy (char *dest, const char *src)
+{
+  char *d = dest;
+  const char *s = src;
+  do *d++ = *s; while (*s++ != '\0');
+  return d - 1;
+}
+
+/* The base name of the file we'll create in the mkdtemp-returned
+   temporary directory.  */
+#define BASENAME "x"
+
+/* Return 0 upon failure, else the 1-based index of the first
+   useful directory name from PARTED_CANDIDATE_DIRS.  */
+int
+main ()
+{
+  char const *env_dirs;
+  char *dirs;
+  char *dir;
+  unsigned int n;
+  int found = 0;
+  size_t dirs_len;
+
+  if ((env_dirs = getenv ("PARTED_CANDIDATE_DIRS")) == NULL)
+    return 0;
+
+  dirs_len = strlen (env_dirs);
+  if ((dirs = strndup (env_dirs, dirs_len)) == NULL)
+    return 0;
+  dir = dirs;
+
+  for (n = 1; ; n++)
+    {
+      size_t dirname_len;
+      char *space;
+
+      /* Skip any leading spaces.  */
+      while (*dir == ' ')
+	++dir;
+
+      space = strchr (dir, ' ');
+      if (space)
+	{
+	  *space = '\0';
+	  dirname_len = space - dir;
+	}
+      else
+	{
+	  dirname_len = strlen (dir);
+	}
+
+      if (dirname_len != 0)
+	{
+	  /* Create an mkdtemp template starting with dir.  */
+	  char *tmp;
+	  char *endp;
+	  char const *base = "partedOD.XXXXXX";
+	  /* Allocate enough space not just for the dir name, but
+	     also for the name of the file to create within it.  */
+	  char *template = malloc (dirname_len + 1 + strlen (base)
+				   + 1 + strlen (BASENAME) + 1);
+	  if (template != NULL
+	      && (endp = stpcpy (stpcpy (stpcpy (template, dir), "/"), base))
+	      && (tmp = mkdtemp (template)) != NULL)
+	    {
+	      /* Append "/BASENAME" to create the file name.  */
+	      stpcpy (stpcpy (endp, "/"), BASENAME);
+
+	      if (create_input_file (tmp, g_buf, sizeof g_buf) == 0
+		  && try_o_direct (tmp) == 0)
+		found = 1;
+
+	      unlink (tmp); /* ignore failure */
+	      *endp = '\0';
+	      rmdir (tmp); /* ignore failure */
+	    }
+	  if (template)
+	    free (template);
+	}
+
+      if (found)
+	break;
+
+      dir += dirname_len + 1;
+      if (dirs + dirs_len < dir)
+	{
+	  n = 0;
+	  break;
+	}
+    }
+  free (dirs);
+
+  return n;
+}
+	      ]])],
+	    # If the above program exits with status 0, then
+	    # there it found no useful directory.  Use ".".
+	    [parted_cv_func_open_O_DIRECT_temp_dir=.],
+
+	    # It found one.  The exit status is an index into the list.
+	    # We also run this code when the program fails to compile or
+	    # to link, as will happen on systems without a mkdtemp function.
+	    [pe_err=$?; set _ $pe_cand_dirs; shift
+	      eval parted_cv_func_open_O_DIRECT_temp_dir='$'$pe_err],
+
+	    # When cross-compiling, use ".".
+	    [parted_cv_func_open_O_DIRECT_temp_dir=.]
+	    )
+      fi
+    ])
+  PARTED_USABLE_TEST_DIR=$parted_cv_func_open_O_DIRECT_temp_dir
+  AC_SUBST([PARTED_USABLE_TEST_DIR])
+
+  # If the result is ".", don't cache it.  The next user of
+  # the cache may well be running from a different file system.
+  dnl Here, I'm using "$as_unset", which is a non-published (i.e., internal)
+  dnl part of autoconf, but we don't expect its name to change any time soon.
+  dnl and by then, it'll probably be ok to use "unset" all by itself.
+  if test "$parted_cv_func_open_O_DIRECT_temp_dir" = .; then
+    $as_unset parted_cv_func_open_O_DIRECT_temp_dir
+  fi
+])
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 93247a0..59d38e5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,16 +5,17 @@ TESTS = \
   t1500-small-ext2.sh \
   t2000-mkfs.sh
 
-TESTS_ENVIRONMENT = \
-  PATH="`pwd`/../parted$(PATH_SEPARATOR)$$PATH"
-
 EXTRA_DIST = \
-  $(TESTS) test-lib.sh
+  $(TESTS) test-lib.sh mkdtemp
 
 CLEANFILES = init.sh
 all: init.sh
 init.sh: Makefile.in
 	rm -f $@-t $@
-	( echo 'srcdir=$(srcdir)'; echo '. $$srcdir/test-lib.sh' ) > $@-t
+	echo 'PARTED_USABLE_TEST_DIR=$(PARTED_USABLE_TEST_DIR)' > $@-t
+	echo 'abs_top_srcdir=$(abs_top_srcdir)' >> $@-t
+	echo '. $(abs_top_srcdir)/tests/test-lib.sh' >> $@-t
+	echo 'PATH=$(abs_top_builddir)/parted$(PATH_SEPARATOR)$$PATH' >> $@-t
+	echo 'export PATH' >> $@-t
 	chmod a-w $@-t
 	mv $@-t $@
diff --git a/tests/mkdtemp b/tests/mkdtemp
new file mode 100755
index 0000000..48fe054
--- /dev/null
+++ b/tests/mkdtemp
@@ -0,0 +1,107 @@
+#!/bin/sh
+# Create a temporary directory, sort of like mktemp -d does.
+# Usage: mkdtemp /tmp phoey.XXXXXXXXXX
+
+# First, try to use the mktemp program.
+# Failing that, we'll roll our own mktemp-like function:
+#  - try to get random bytes from /dev/urandom
+#  - failing that, generate output from a combination of quickly-varying
+#      sources and gzip.  Ignore non-varying gzip header, and extract
+#      "random" bits from there.
+#  - given those bits, map to file-name bytes using tr, and try to create
+#      the desired directory.
+#  - make only $MAX_TRIES attempts
+
+ME=$(basename "$0")
+die() { echo >&2 "$ME: $@"; exit 1; }
+
+MAX_TRIES=4
+
+rand_bytes()
+{
+  n=$1
+
+  chars=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
+
+  dev_rand=/dev/urandom
+  if test -r "$dev_rand"; then
+    # Note: 256-length($chars) == 194; 3 copies of $chars is 186 + 8 = 194.
+    head -c$n "$dev_rand" | tr -c $chars 01234567$chars$chars$chars
+    return
+  fi
+
+  cmds='date; date +%N; free; who -a; w; ps auxww; ps ef; netstat -n'
+  data=$( (eval "$cmds") 2>&1 | gzip )
+
+  n_plus_50=$(expr $n + 50)
+
+  # Ensure that $data has length at least 50+$n
+  while :; do
+    len=$(echo "$data"|wc -c)
+    test $n_plus_50 -le $len && break;
+    data=$( (echo "$data"; eval "$cmds") 2>&1 | gzip )
+  done
+
+  echo "$data" \
+    | dd bs=1 skip=50 count=$n 2>/dev/null \
+    | tr -c $chars 01234567$chars$chars$chars
+}
+
+mkdtemp()
+{
+  case $# in
+  2);;
+  *) die "Usage: $ME DIR TEMPLATE";;
+  esac
+
+  destdir=$1
+  template=$2
+
+  case $template in
+  *XXXX) ;;
+  *) die "invalid template: $template (must have a suffix of at least 4 X's)";;
+  esac
+
+  fail=0
+
+  # First, try to use mktemp.
+  d=$(env -u TMPDIR mktemp -d -t -p "$destdir" "$template" 2>/dev/null) \
+    || fail=1
+
+  # The resulting name must be in the specified directory.
+  case $d in "$destdir"*);; *) fail=1;; esac
+
+  # It must have created the directory.
+  test -d "$d" || fail=1
+
+  # It must have 0700 permissions.
+  perms=$(ls -dgo "$d" 2>/dev/null) || fail=1
+  case $perms in drwx------*) ;; *) fail=1;; esac
+
+  test $fail = 0 && {
+    echo "$d"
+    return
+  }
+
+  # If we reach this point, we'll have to create a directory manually.
+
+  # Get a copy of the template without its suffix of X's.
+  base_template=$(echo "$template"|sed 's/XX*$//')
+
+  # Calculate how many X's we've just removed.
+  nx=$(expr length "$template" - length "$base_template")
+
+  err=
+  i=1
+  while :; do
+    X=$(rand_bytes $nx)
+    candidate_dir="$destdir/$base_template$X"
+    err=$(mkdir -m 0700 "$candidate_dir" 2>&1) \
+      && { echo "$candidate_dir"; return; }
+    test $MAX_TRIES -le $i && break;
+    i=$(expr $i + 1)
+  done
+  die "$err"
+}
+
+mkdtemp "$@"
diff --git a/tests/test-lib.sh b/tests/test-lib.sh
index 8370963..59f85a4 100644
--- a/tests/test-lib.sh
+++ b/tests/test-lib.sh
@@ -201,11 +201,12 @@ if test "$privileges_required_" != ''; then
     fi
 fi
 
+# Test the binaries we have just built.
 pwd_=`pwd`
+parted_="$pwd_/../parted/parted"
 
-# Test the binaries we have just built.
-PATH=$pwd_/../parted:$PATH
-export PATH
+test_dir_=$PARTED_USABLE_TEST_DIR
+test $test_dir_ = . && test_dir_=$pwd_
 
 fail=
 # Some tests require an actual hardware device, e.g., a real disk with a
@@ -222,8 +223,8 @@ if test $skip_ = 0 && test "$erasable_device_required_" != ''; then
   if test "$DEVICE_TO_ERASE" != '' && test "$DEVICE_TO_ERASE_SIZE" != ''; then
     dev_=$DEVICE_TO_ERASE
     sz=$DEVICE_TO_ERASE_SIZE
-    parted_output=$(parted -s $dev_ print) || fail="no such device: $dev_"
-    parted -s $dev_ print|grep "^Disk $dev_: $sz$" \
+    parted_output=$($parted -s $dev_ print) || fail="no such device: $dev_"
+    $parted -s $dev_ print|grep "^Disk $dev_: $sz$" \
 	> /dev/null || fail="actual device size is not $sz"
     # Try to see if $dev_ or any of its partitions is mounted.
     # This is not reliable.  FIXME: find a better way.
@@ -232,7 +233,7 @@ if test $skip_ = 0 && test "$erasable_device_required_" != ''; then
     # contains no "//" or "/./" components.
 
     # Prefer df --local, if it works, so we don't waste time
-    # enumerating with lots of automounted file systems.
+    # enumerating lots of automounted file systems.
     ( df --local / > /dev/null 2>&1 ) && df='df --local' || df=df
     $df | grep "^$dev_" && fail="$dev_ is already mounted"
     $df | grep "^$dev_[0-9]" && fail="a partition of $dev_ is already mounted"
@@ -274,17 +275,16 @@ do
 	esac
 done
 
+t0=$($abs_top_srcdir/tests/mkdtemp $test_dir_ parted-$this_test.XXXXXXXXXX) \
+    || error "failed to create temporary directory in $test_dir_"
+
 # Run each test from within a temporary sub-directory named after the
-# test itself, and arrange to remove it upon exception and upon normal exit.
-t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp_=$t0/$$
-trap 'st=$?; cleanup_; cd "$pwd_" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $st' 0
+# test itself, and arrange to remove it upon exception or normal exit.
+trap 'st=$?; cleanup_; d='"$t0"';
+    cd '"$test_dir_"' && chmod -R u+rwx "$d" && rm -rf "$d" && exit $st' 0
 trap '(exit $?); exit $?' 1 2 13 15
 
-framework_failure=0
-mkdir -p $tmp_ || framework_failure=1
-cd $tmp_ || framework_failure=1
-test $framework_failure = 0 \
-     || error 'failed to create temporary directory'
+cd $t0 || error "failed to cd to $t0"
 
 if ( diff --version < /dev/null 2>&1 | grep GNU ) 2>&1 > /dev/null; then
   compare='diff -u'



More information about the Parted-commits mailing list