Add the ZFS Test Suite

Add the ZFS Test Suite and test-runner framework from illumos.
This is a continuation of the work done by Turbo Fredriksson to
port the ZFS Test Suite to Linux.  While this work was originally
conceived as a stand alone project integrating it directly with
the ZoL source tree has several advantages:

  * Allows the ZFS Test Suite to be packaged in zfs-test package.
    * Facilitates easy integration with the CI testing.
    * Users can locally run the ZFS Test Suite to validate ZFS.
      This testing should ONLY be done on a dedicated test system
      because the ZFS Test Suite in its current form is destructive.
  * Allows the ZFS Test Suite to be run directly in the ZoL source
    tree enabled developers to iterate quickly during development.
  * Developers can easily add/modify tests in the framework as
    features are added or functionality is changed.  The tests
    will then always be in sync with the implementation.

Full documentation for how to run the ZFS Test Suite is available
in the tests/README.md file.

Warning: This test suite is designed to be run on a dedicated test
system.  It will make modifications to the system including, but
not limited to, the following.

  * Adding new users
  * Adding new groups
  * Modifying the following /proc files:
    * /proc/sys/kernel/core_pattern
    * /proc/sys/kernel/core_uses_pid
  * Creating directories under /

Notes:
  * Not all of the test cases are expected to pass and by default
    these test cases are disabled.  The failures are primarily due
    to assumption made for illumos which are invalid under Linux.
  * When updating these test cases it should be done in as generic
    a way as possible so the patch can be submitted back upstream.
    Most existing library functions have been updated to be Linux
    aware, and the following functions and variables have been added.
    * Functions:
      * is_linux          - Used to wrap a Linux specific section.
      * block_device_wait - Waits for block devices to be added to /dev/.
    * Variables:            Linux          Illumos
      * ZVOL_DEVDIR         "/dev/zvol"    "/dev/zvol/dsk"
      * ZVOL_RDEVDIR        "/dev/zvol"    "/dev/zvol/rdsk"
      * DEV_DSKDIR          "/dev"         "/dev/dsk"
      * DEV_RDSKDIR         "/dev"         "/dev/rdsk"
      * NEWFS_DEFAULT_FS    "ext2"         "ufs"
  * Many of the disabled test cases fail because 'zfs/zpool destroy'
    returns EBUSY.  This is largely causes by the asynchronous nature
    of device handling on Linux and is expected, the impacted test
    cases will need to be updated to handle this.
  * There are several test cases which have been disabled because
    they can trigger a deadlock.  A primary example of this is to
    recursively create zpools within zpools.  These tests have been
    disabled until the root issue can be addressed.
  * Illumos specific utilities such as (mkfile) should be added to
    the tests/zfs-tests/cmd/ directory.  Custom programs required by
    the test scripts can also be added here.
  * SELinux should be either is permissive mode or disabled when
    running the tests.  The test cases should be updated to conform
    to a standard policy.
  * Redundant test functionality has been removed (zfault.sh).
  * Existing test scripts (zconfig.sh) should be migrated to use
    the framework for consistency and ease of testing.
  * The DISKS environment variable currently only supports loopback
    devices because of how the ZFS Test Suite expects partitions to
    be named (p1, p2, etc).  Support must be added to generate the
    correct partition name based on the device location and name.
  * The ZFS Test Suite is part of the illumos code base at:
    https://github.com/illumos/illumos-gate/tree/master/usr/src/test

Original-patch-by: Turbo Fredriksson <turbo@bayour.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Olaf Faaland <faaland1@llnl.gov>
Closes #6
Closes #1534
This commit is contained in:
Brian Behlendorf 2015-07-01 15:23:09 -07:00
parent 887d1e60ef
commit 6bb24f4dc7
1243 changed files with 89497 additions and 1042 deletions

View File

@ -6,7 +6,7 @@ include config/tgz.am
SUBDIRS = include rpm
if CONFIG_USER
SUBDIRS += udev etc man scripts lib cmd contrib
SUBDIRS += udev etc man scripts tests lib cmd contrib
endif
if CONFIG_KERNEL
SUBDIRS += module

6
TEST
View File

@ -37,6 +37,12 @@ TEST_ZCONFIG_OPTIONS="-c -s10"
#TEST_XFSTESTS_VDEV="/var/tmp/vdev"
#TEST_XFSTESTS_OPTIONS=""
### zfs-tests.sh
#TEST_ZFSTESTS_SKIP="yes"
#TEST_ZFSTESTS_DISKS="vdb vdc vdd"
#TEST_ZFSTESTS_DISKSIZE="8G"
#TEST_ZFSTESTS_RUNFILE="linux.run"
### filebench
#TEST_FILEBENCH_SKIP="yes"
#TEST_FILEBENCH_URL="http://build.zfsonlinux.org/"

171
config/user-commands.m4 Normal file
View File

@ -0,0 +1,171 @@
dnl #
dnl # Commands common to multiple platforms. They generally behave
dnl # in the same way and take similar options.
dnl #
AC_DEFUN([ZFS_AC_CONFIG_USER_COMMANDS_COMMON], [
AC_PATH_TOOL(AWK, awk, "")
AC_PATH_TOOL(BASENAME, basename, "")
AC_PATH_TOOL(BC, bc, "")
AC_PATH_TOOL(BUNZIP2, bunzip2, "")
AC_PATH_TOOL(BZCAT, bzcat, "")
AC_PATH_TOOL(CAT, cat, "")
AC_PATH_TOOL(CD, cd, "cd") dnl # Builtin in bash
AC_PATH_TOOL(CHGRP, chgrp, "")
AC_PATH_TOOL(CHMOD, chmod, "")
AC_PATH_TOOL(CHOWN, chown, "")
AC_PATH_TOOL(CKSUM, cksum, "")
AC_PATH_TOOL(CMP, cmp, "")
AC_PATH_TOOL(CP, cp, "")
AC_PATH_TOOL(CPIO, cpio, "")
AC_PATH_TOOL(CUT, cut, "")
AC_PATH_TOOL(DATE, date, "")
AC_PATH_TOOL(DD, dd, "")
AC_PATH_TOOL(DF, df, "")
AC_PATH_TOOL(DIFF, diff, "")
AC_PATH_TOOL(DIRNAME, dirname, "")
AC_PATH_TOOL(DU, du, "")
AC_PATH_TOOL(ECHO, echo, "")
AC_PATH_TOOL(EGREP, egrep, "")
AC_PATH_TOOL(FDISK, fdisk, "")
AC_PATH_TOOL(FGREP, fgrep, "")
AC_PATH_TOOL(FILE, file, "")
AC_PATH_TOOL(FIND, find, "")
AC_PATH_TOOL(FSCK, fsck, "")
AC_PATH_TOOL(GNUDD, dd, "")
AC_PATH_TOOL(GETCONF, getconf, "")
AC_PATH_TOOL(GETENT, getent, "")
AC_PATH_TOOL(GREP, grep, "")
dnl # Due to permissions unpriviledged users may not detect group*.
AC_PATH_TOOL(GROUPADD, groupadd, "/usr/sbin/groupadd")
AC_PATH_TOOL(GROUPDEL, groupdel, "/usr/sbin/groupdel")
AC_PATH_TOOL(GROUPMOD, groupmod, "/usr/sbin/groupmod")
AC_PATH_TOOL(HEAD, head, "")
AC_PATH_TOOL(HOSTNAME, hostname, "")
AC_PATH_TOOL(ID, id, "")
AC_PATH_TOOL(KILL, kill, "")
AC_PATH_TOOL(KSH, ksh, "")
AC_PATH_TOOL(LOGNAME, logname, "")
AC_PATH_TOOL(LS, ls, "")
AC_PATH_TOOL(MD5SUM, md5sum, "")
AC_PATH_TOOL(MKDIR, mkdir, "")
AC_PATH_TOOL(MKNOD, mknod, "")
AC_PATH_TOOL(MKTEMP, mktemp, "")
AC_PATH_TOOL(MODINFO, modinfo, "")
AC_PATH_TOOL(MOUNT, mount, "")
AC_PATH_TOOL(MV, mv, "")
AC_PATH_TOOL(NAWK, nawk, "")
AC_PATH_TOOL(PGREP, pgrep, "")
AC_PATH_TOOL(PING, ping, "")
AC_PATH_TOOL(PKILL, pkill, "")
AC_PATH_TOOL(PRINTF, printf, "")
AC_PATH_TOOL(PS, ps, "")
AC_PATH_TOOL(PYTHON, python, "")
AC_PATH_TOOL(REBOOT, reboot, "")
AC_PATH_TOOL(RMDIR, rmdir, "")
AC_PATH_TOOL(RSH, rsh, "")
AC_PATH_TOOL(SED, sed, "")
AC_PATH_TOOL(SHUF, shuf, "")
AC_PATH_TOOL(SLEEP, sleep, "")
AC_PATH_TOOL(SORT, sort, "")
AC_PATH_TOOL(STRINGS, strings, "")
AC_PATH_TOOL(SU, su, "")
AC_PATH_TOOL(SUM, sum, "")
AC_PATH_TOOL(SYNC, sync, "")
AC_PATH_TOOL(TAIL, tail, "")
AC_PATH_TOOL(TAR, tar, "")
AC_PATH_TOOL(TOUCH, touch, "")
AC_PATH_TOOL(TR, tr, "")
AC_PATH_TOOL(TRUE, true, "")
AC_PATH_TOOL(UMASK, umask, "")
AC_PATH_TOOL(UMOUNT, umount, "")
AC_PATH_TOOL(UNAME, uname, "")
AC_PATH_TOOL(UNIQ, uniq, "")
dnl # Due to permissions unpriviledged users may not detect user*.
AC_PATH_TOOL(USERADD, useradd, "/usr/sbin/useradd")
AC_PATH_TOOL(USERDEL, userdel, "/usr/sbin/userdel")
AC_PATH_TOOL(USERMOD, usermod, "/usr/sbin/usermod")
AC_PATH_TOOL(WAIT, wait, "wait") dnl # Builtin in bash
AC_PATH_TOOL(WC, wc, "")
])
dnl #
dnl # Linux commands, used withing 'is_linux' blocks of test scripts.
dnl # These commands may take different command line arguments.
dnl #
AC_DEFUN([ZFS_AC_CONFIG_USER_COMMANDS_LINUX], [
AC_PATH_TOOL(BLOCKDEV, blockdev, "")
AC_PATH_TOOL(COMPRESS, gzip, "")
AC_PATH_TOOL(FORMAT, parted, "")
AC_PATH_TOOL(LOCKFS, lsof, "")
AC_PATH_TOOL(MODUNLOAD, rmmod, "")
AC_PATH_TOOL(NEWFS, mke2fs, "")
AC_PATH_TOOL(PFEXEC, sudo, "")
AC_PATH_TOOL(SHARE, exportfs, "")
AC_PATH_TOOL(SWAP, swapon, "")
AC_PATH_TOOL(SWAPADD, swapon, "")
AC_PATH_TOOL(TRUNCATE, truncate, "")
AC_PATH_TOOL(UDEVADM, udevadm, "")
AC_PATH_TOOL(UFSDUMP, dump, "")
AC_PATH_TOOL(UFSRESTORE, restore, "")
AC_PATH_TOOL(UNCOMPRESS, gunzip, "")
AC_PATH_TOOL(UNSHARE, exportfs, "")
AC_PATH_TOOL(GETFACL, getfacl, "")
AC_PATH_TOOL(SETFACL, setfacl, "")
AC_PATH_TOOL(CHACL, chacl, "")
AC_PATH_TOOL(NPROC, nproc, "")
PAGESIZE=$($GETCONF PAGESIZE)
AC_SUBST(PAGESIZE)
MNTTAB=/etc/mtab
AC_SUBST(MNTTAB)
])
dnl #
dnl # BSD style commands, these have been kept in case at some point
dnl # we want to build these packages on a BSD style systems. Otherwise
dnl # they are unused and should be treated as such.
dnl #
AC_DEFUN([ZFS_AC_CONFIG_USER_COMMANDS_BSD], [
AC_PATH_TOOL(COMPRESS, compress, "")
AC_PATH_TOOL(COREADM, coreadm, "")
AC_PATH_TOOL(DIRCMP, dircmp, "")
AC_PATH_TOOL(DUMPADM, dumpadm, "")
AC_PATH_TOOL(FORMAT, format, "")
AC_PATH_TOOL(GETMAJOR, getmajor, "")
AC_PATH_TOOL(ISAINFO, isainfo, "")
AC_PATH_TOOL(KSTAT, kstat, "")
AC_PATH_TOOL(LOCKFS, lockfs, "")
AC_PATH_TOOL(LOFIADM, lofiadm, "")
AC_PATH_TOOL(MODUNLOAD, modunload, "")
AC_PATH_TOOL(NEWFS, newfs, "")
AC_PATH_TOOL(PAGESIZE, pagesize, "")
AC_PATH_TOOL(PFEXEC, pfexec, "")
AC_PATH_TOOL(PKGINFO, pkginfo, "")
AC_PATH_TOOL(PRTVTOC, prtvtoc, "")
AC_PATH_TOOL(PSRINFO, psrinfo, "")
AC_PATH_TOOL(SHARE, share, "")
AC_PATH_TOOL(SVCADM, svcadm, "")
AC_PATH_TOOL(SVCS, svcs, "")
AC_PATH_TOOL(SWAP, swap, "")
AC_PATH_TOOL(SWAPADD, swapadd, "")
AC_PATH_TOOL(UFSDUMP, ufsdump, "")
AC_PATH_TOOL(UFSRESTORE, ufsrestore, "")
AC_PATH_TOOL(UMOUNTALL, umountall, "")
AC_PATH_TOOL(UNCOMPRESS, uncompress, "")
AC_PATH_TOOL(UNSHARE, unshare, "")
AC_PATH_TOOL(ZONEADM, zoneadm, "")
AC_PATH_TOOL(ZONECFG, zonecfg, "")
AC_PATH_TOOL(ZONENAME, zonename, "")
])
AC_DEFUN([ZFS_AC_CONFIG_USER_COMMANDS], [
ZFS_AC_CONFIG_USER_COMMANDS_COMMON
OS=$($UNAME -o)
AS_IF([test "$OS" == "GNU/Linux"], [
ZFS_AC_CONFIG_USER_COMMANDS_LINUX
], [
ZFS_AC_CONFIG_USER_COMMANDS_BSD
])
])

12
config/user-libattr.m4 Normal file
View File

@ -0,0 +1,12 @@
dnl #
dnl # Check for libattr
dnl #
AC_DEFUN([ZFS_AC_CONFIG_USER_LIBATTR], [
LIBATTR=
AC_CHECK_HEADER([attr/xattr.h], [], [AC_MSG_FAILURE([
*** attr/xattr.h missing, libattr-devel package required])])
AC_SUBST([LIBATTR], ["-lattr"])
AC_DEFINE([HAVE_LIBATTR], 1, [Define if you have libattr])
])

View File

@ -11,9 +11,24 @@ AC_DEFUN([ZFS_AC_CONFIG_USER], [
ZFS_AC_CONFIG_USER_ZLIB
ZFS_AC_CONFIG_USER_LIBUUID
ZFS_AC_CONFIG_USER_LIBBLKID
ZFS_AC_CONFIG_USER_LIBATTR
ZFS_AC_CONFIG_USER_FRAME_LARGER_THAN
ZFS_AC_CONFIG_USER_RUNSTATEDIR
dnl #
dnl # Checks for library functions
ZFS_AC_CONFIG_USER_COMMANDS
ZFS_AC_TEST_FRAMEWORK
AC_CHECK_FUNCS([mlockall])
])
dnl #
dnl # Setup the environment for the ZFS Test Suite. Currently only
dnl # Linux sytle systems are supported but this infrastructure can
dnl # be extended to support other platforms if needed.
dnl #
AC_DEFUN([ZFS_AC_TEST_FRAMEWORK], [
ZONENAME="echo global"
AC_SUBST(ZONENAME)
AC_SUBST(RM)
])

View File

@ -89,8 +89,8 @@ AC_DEFUN([ZFS_AC_CONFIG], [
case "$ZFS_CONFIG" in
kernel) ZFS_AC_CONFIG_KERNEL ;;
user) ZFS_AC_CONFIG_USER ;;
all) ZFS_AC_CONFIG_KERNEL
ZFS_AC_CONFIG_USER ;;
all) ZFS_AC_CONFIG_USER
ZFS_AC_CONFIG_KERNEL ;;
srpm) ;;
*)
AC_MSG_RESULT([Error!])

View File

@ -56,7 +56,7 @@ ZFS_AC_CONFIG
ZFS_AC_DEBUG
ZFS_AC_DEBUG_DMU_TX
AC_CONFIG_FILES([
AC_CONFIG_FILES([
Makefile
udev/Makefile
udev/rules.d/Makefile
@ -134,6 +134,145 @@ AC_CONFIG_FILES([
scripts/zpios-test/Makefile
scripts/zpool-config/Makefile
scripts/common.sh
tests/Makefile
tests/test-runner/Makefile
tests/test-runner/cmd/Makefile
tests/test-runner/include/Makefile
tests/test-runner/man/Makefile
tests/runfiles/Makefile
tests/zfs-tests/Makefile
tests/zfs-tests/cmd/Makefile
tests/zfs-tests/cmd/chg_usr_exec/Makefile
tests/zfs-tests/cmd/devname2devid/Makefile
tests/zfs-tests/cmd/dir_rd_update/Makefile
tests/zfs-tests/cmd/file_check/Makefile
tests/zfs-tests/cmd/file_trunc/Makefile
tests/zfs-tests/cmd/file_write/Makefile
tests/zfs-tests/cmd/largest_file/Makefile
tests/zfs-tests/cmd/mkbusy/Makefile
tests/zfs-tests/cmd/mkfile/Makefile
tests/zfs-tests/cmd/mkfiles/Makefile
tests/zfs-tests/cmd/mktree/Makefile
tests/zfs-tests/cmd/mmap_exec/Makefile
tests/zfs-tests/cmd/mmapwrite/Makefile
tests/zfs-tests/cmd/randfree_file/Makefile
tests/zfs-tests/cmd/readmmap/Makefile
tests/zfs-tests/cmd/rename_dir/Makefile
tests/zfs-tests/cmd/rm_lnkcnt_zero_file/Makefile
tests/zfs-tests/cmd/threadsappend/Makefile
tests/zfs-tests/cmd/xattrtest/Makefile
tests/zfs-tests/include/Makefile
tests/zfs-tests/include/commands.cfg
tests/zfs-tests/include/default.cfg
tests/zfs-tests/tests/Makefile
tests/zfs-tests/tests/functional/Makefile
tests/zfs-tests/tests/functional/acl/Makefile
tests/zfs-tests/tests/functional/acl/posix/Makefile
tests/zfs-tests/tests/functional/atime/Makefile
tests/zfs-tests/tests/functional/bootfs/Makefile
tests/zfs-tests/tests/functional/cache/Makefile
tests/zfs-tests/tests/functional/cachefile/Makefile
tests/zfs-tests/tests/functional/casenorm/Makefile
tests/zfs-tests/tests/functional/clean_mirror/Makefile
tests/zfs-tests/tests/functional/cli_root/Makefile
tests/zfs-tests/tests/functional/cli_root/zdb/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_clone/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_copies/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_destroy/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_get/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_inherit/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_mount/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_promote/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_property/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_rename/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_reservation/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_rollback/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_send/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_set/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_share/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_snapshot/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_unshare/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_upgrade/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_add/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_attach/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_clear/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_create/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_destroy/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_detach/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_expand/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_export/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_get/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_history/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_import/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_offline/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_online/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_remove/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_replace/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_scrub/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_set/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_status/Makefile
tests/zfs-tests/tests/functional/cli_root/zpool_upgrade/Makefile
tests/zfs-tests/tests/functional/cli_user/Makefile
tests/zfs-tests/tests/functional/cli_user/misc/Makefile
tests/zfs-tests/tests/functional/cli_user/zfs_list/Makefile
tests/zfs-tests/tests/functional/cli_user/zpool_iostat/Makefile
tests/zfs-tests/tests/functional/cli_user/zpool_list/Makefile
tests/zfs-tests/tests/functional/compression/Makefile
tests/zfs-tests/tests/functional/ctime/Makefile
tests/zfs-tests/tests/functional/delegate/Makefile
tests/zfs-tests/tests/functional/devices/Makefile
tests/zfs-tests/tests/functional/exec/Makefile
tests/zfs-tests/tests/functional/features/async_destroy/Makefile
tests/zfs-tests/tests/functional/features/Makefile
tests/zfs-tests/tests/functional/grow_pool/Makefile
tests/zfs-tests/tests/functional/grow_replicas/Makefile
tests/zfs-tests/tests/functional/history/Makefile
tests/zfs-tests/tests/functional/inheritance/Makefile
tests/zfs-tests/tests/functional/inuse/Makefile
tests/zfs-tests/tests/functional/large_files/Makefile
tests/zfs-tests/tests/functional/largest_pool/Makefile
tests/zfs-tests/tests/functional/link_count/Makefile
tests/zfs-tests/tests/functional/migration/Makefile
tests/zfs-tests/tests/functional/mmap/Makefile
tests/zfs-tests/tests/functional/mount/Makefile
tests/zfs-tests/tests/functional/mv_files/Makefile
tests/zfs-tests/tests/functional/nestedfs/Makefile
tests/zfs-tests/tests/functional/no_space/Makefile
tests/zfs-tests/tests/functional/nopwrite/Makefile
tests/zfs-tests/tests/functional/online_offline/Makefile
tests/zfs-tests/tests/functional/pool_names/Makefile
tests/zfs-tests/tests/functional/poolversion/Makefile
tests/zfs-tests/tests/functional/privilege/Makefile
tests/zfs-tests/tests/functional/quota/Makefile
tests/zfs-tests/tests/functional/redundancy/Makefile
tests/zfs-tests/tests/functional/refquota/Makefile
tests/zfs-tests/tests/functional/refreserv/Makefile
tests/zfs-tests/tests/functional/rename_dirs/Makefile
tests/zfs-tests/tests/functional/replacement/Makefile
tests/zfs-tests/tests/functional/reservation/Makefile
tests/zfs-tests/tests/functional/rootpool/Makefile
tests/zfs-tests/tests/functional/rsend/Makefile
tests/zfs-tests/tests/functional/scrub_mirror/Makefile
tests/zfs-tests/tests/functional/slog/Makefile
tests/zfs-tests/tests/functional/snapshot/Makefile
tests/zfs-tests/tests/functional/snapused/Makefile
tests/zfs-tests/tests/functional/sparse/Makefile
tests/zfs-tests/tests/functional/threadsappend/Makefile
tests/zfs-tests/tests/functional/truncate/Makefile
tests/zfs-tests/tests/functional/userquota/Makefile
tests/zfs-tests/tests/functional/write_dirs/Makefile
tests/zfs-tests/tests/functional/xattr/Makefile
tests/zfs-tests/tests/functional/zvol/Makefile
tests/zfs-tests/tests/functional/zvol/zvol_cli/Makefile
tests/zfs-tests/tests/functional/zvol/zvol_ENOSPC/Makefile
tests/zfs-tests/tests/functional/zvol/zvol_misc/Makefile
tests/zfs-tests/tests/functional/zvol/zvol_swap/Makefile
tests/zfs-tests/tests/stress/Makefile
rpm/Makefile
rpm/redhat/Makefile
rpm/redhat/zfs.spec

View File

@ -58,6 +58,7 @@ extern "C" {
*/
#define DISK_ROOT "/dev"
#define UDISK_ROOT "/dev/disk"
#define ZVOL_ROOT "/dev/zvol"
/*
* Default wait time for a device name to be created.

View File

@ -908,7 +908,8 @@ zfs_append_partition(char *path, size_t max_len)
{
int len = strlen(path);
if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) {
if ((strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) ||
(strncmp(path, ZVOL_ROOT, strlen(ZVOL_ROOT)) == 0)) {
if (len + 6 >= max_len)
return (-1);

View File

@ -171,6 +171,7 @@ Requires: parted
Requires: lsscsi
Requires: mdadm
Requires: bc
Requires: ksh
%description test
This package contains test infrastructure and support scripts for

View File

@ -6,52 +6,12 @@ pkgdatadir = $(datadir)/@PACKAGE@
dist_pkgdata_SCRIPTS = \
$(top_builddir)/scripts/common.sh \
$(top_srcdir)/scripts/zconfig.sh \
$(top_srcdir)/scripts/zfault.sh \
$(top_srcdir)/scripts/ziltest.sh \
$(top_srcdir)/scripts/zimport.sh \
$(top_srcdir)/scripts/zfs.sh \
$(top_srcdir)/scripts/zfs-tests.sh \
$(top_srcdir)/scripts/zpool-create.sh \
$(top_srcdir)/scripts/zpios.sh \
$(top_srcdir)/scripts/zpios-sanity.sh \
$(top_srcdir)/scripts/zpios-survey.sh \
$(top_srcdir)/scripts/smb.sh
ZFS=$(top_builddir)/scripts/zfs.sh
ZCONFIG=$(top_builddir)/scripts/zconfig.sh
ZFAULT=$(top_builddir)/scripts/zfault.sh
ZIMPORT=$(top_builddir)/scripts/zimport.sh
ZTEST=$(top_builddir)/cmd/ztest/ztest
ZPIOS_SANITY=$(top_builddir)/scripts/zpios-sanity.sh
check:
@$(ZFS) -u
@echo
@echo -n "===================================="
@echo -n " ZTEST "
@echo "===================================="
@echo
@$(ZFS)
@$(ZTEST) -V
@$(ZFS) -u
@echo
@echo
@echo -n "==================================="
@echo -n " ZCONFIG "
@echo "==================================="
@echo
@$(ZCONFIG) -c
@echo
@echo -n "==================================="
@echo -n " ZFAULT "
@echo "==================================="
@echo
@$(ZFAULT) -c
@echo
@echo -n "===================================="
@echo -n " ZPIOS "
@echo "===================================="
@echo
@$(ZFS)
@$(ZPIOS_SANITY)
@$(ZFS) -u
@echo

View File

@ -45,10 +45,13 @@ DEVDIR=${DEVDIR:-/dev/disk/by-vdev}
ZPOOLDIR=${ZPOOLDIR:-${pkgdatadir}/zpool-config}
ZPIOSDIR=${ZPIOSDIR:-${pkgdatadir}/zpios-test}
ZPIOSPROFILEDIR=${ZPIOSPROFILEDIR:-${pkgdatadir}/zpios-profile}
TESTSDIR=${TESTSDIR:-${pkgdatadir}/zfs-tests}
RUNFILEDIR=${RUNFILEDIR:-${pkgdatadir}/runfiles}
ZDB=${ZDB:-${sbindir}/zdb}
ZFS=${ZFS:-${sbindir}/zfs}
ZINJECT=${ZINJECT:-${sbindir}/zinject}
ZHACK=${ZHACK:-${sbindir}/zhack}
ZPOOL=${ZPOOL:-${sbindir}/zpool}
ZTEST=${ZTEST:-${sbindir}/ztest}
ZPIOS=${ZPIOS:-${sbindir}/zpios}
@ -58,6 +61,9 @@ ZFS_SH=${ZFS_SH:-${pkgdatadir}/zfs.sh}
ZPOOL_CREATE_SH=${ZPOOL_CREATE_SH:-${pkgdatadir}/zpool-create.sh}
ZPIOS_SH=${ZPIOS_SH:-${pkgdatadir}/zpios.sh}
ZPIOS_SURVEY_SH=${ZPIOS_SURVEY_SH:-${pkgdatadir}/zpios-survey.sh}
TEST_RUNNER=${TEST_RUNNER:-${pkgdatadir}/test-runner/bin/test-runner.py}
STF_TOOLS=${STF_TOOLS:-${pkgdatadir}/test-runner}
STF_SUITE=${STF_SUITE:-${pkgdatadir}/zfs-tests}
LDMOD=${LDMOD:-/sbin/modprobe}
LSMOD=${LSMOD:-/sbin/lsmod}
@ -65,6 +71,7 @@ RMMOD=${RMMOD:-/sbin/rmmod}
INFOMOD=${INFOMOD:-/sbin/modinfo}
LOSETUP=${LOSETUP:-/sbin/losetup}
MDADM=${MDADM:-/sbin/mdadm}
DMSETUP=${DMSETUP:-/sbin/dmsetup}
PARTED=${PARTED:-/sbin/parted}
BLOCKDEV=${BLOCKDEV:-/sbin/blockdev}
LSSCSI=${LSSCSI:-/usr/bin/lsscsi}
@ -297,7 +304,7 @@ check_loop_utils() {
# the minor as long as it's less than /sys/module/loop/parameters/max_loop.
#
unused_loop_device() {
local DEVICE=`${LOSETUP} -f`
local DEVICE=$(${LOSETUP} -f)
local MAX_LOOP_PATH="/sys/module/loop/parameters/max_loop"
local MAX_LOOP;
@ -359,7 +366,7 @@ destroy_loop_devices() {
local LODEVICES="$1"
msg "Destroying ${LODEVICES}"
${LOSETUP} -d ${LODEVICES} || \
${LOSETUP} -d ${LODEVICES} || \
die "Error $? destroying ${FILE} -> ${DEVICE} loopback"
rm -f ${FILES}

View File

@ -1,955 +0,0 @@
#!/bin/bash
#
# ZPOOL fault verification test script.
#
# The current suite of fault tests should not be thought of an exhaustive
# list of failure modes. Rather it is simply an starting point which trys
# to cover the bulk the of the 'easy' and hopefully common, failure modes.
#
# Additional tests should be added but the current suite as new interesting
# failures modes are observed. Additional failure modes I'd like to see
# tests for include, but are not limited too:
#
# * Slow but successful IO.
# * SCSI sense codes generated as zevents.
# * 4k sectors
# * noise
# * medium error
# * recovered error
#
# The current infrastructure using the 'mdadm' faulty device and the
# 'scsi_debug' simulated scsi devices. The idea is to inject the error
# below the zfs stack to validate all the error paths. More targeted
# failure testing should be added using the 'zinject' command line util.
#
# Requires the following packages:
# * mdadm
# * lsscsi
# * sg3-utils
#
basedir="$(dirname $0)"
SCRIPT_COMMON=common.sh
if [ -f "${basedir}/${SCRIPT_COMMON}" ]; then
. "${basedir}/${SCRIPT_COMMON}"
else
echo "Missing helper script ${SCRIPT_COMMON}" && exit 1
fi
PROG=zfault.sh
usage() {
cat << EOF
USAGE:
$0 [hvcts]
DESCRIPTION:
ZPOOL fault verification tests
OPTIONS:
-h Show this message
-v Verbose
-c Cleanup md+lo+file devices at start
-t <#> Run listed tests
-s <#> Skip listed tests
EOF
}
while getopts 'hvct:s:?' OPTION; do
case $OPTION in
h)
usage
exit 1
;;
v)
VERBOSE=1
;;
c)
CLEANUP=1
;;
t)
TESTS_RUN=($OPTARG)
;;
s)
TESTS_SKIP=($OPTARG)
;;
?)
usage
exit
;;
esac
done
if [ $(id -u) != 0 ]; then
die "Must run as root"
fi
# Initialize the test suite
init
# Perform pre-cleanup is requested
if [ ${CLEANUP} ]; then
${ZFS_SH} -u
cleanup_md_devices
cleanup_loop_devices
rm -f /tmp/zpool.cache.*
fi
# Check if we need to skip all md based tests.
MD_PARTITIONABLE=0
check_md_partitionable && MD_PARTITIONABLE=1
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
echo "Skipping tests 1-7 which require partitionable md devices"
fi
# Check if we need to skip all the scsi_debug tests.
SCSI_DEBUG=0
${INFOMOD} scsi_debug &>/dev/null && SCSI_DEBUG=1
if [ ${SCSI_DEBUG} -eq 0 ]; then
echo "Skipping tests 8-9 which require the scsi_debug module"
fi
if [ ${MD_PARTITIONABLE} -eq 0 ] || [ ${SCSI_DEBUG} -eq 0 ]; then
echo
fi
printf "%40s%s\t%s\t%s\t%s\t%s\n" "" "raid0" "raid10" "raidz" "raidz2" "raidz3"
pass_nonewline() {
echo -n -e "${COLOR_GREEN}Pass${COLOR_RESET}\t"
}
skip_nonewline() {
echo -n -e "${COLOR_BROWN}Skip${COLOR_RESET}\t"
}
nth_zpool_vdev() {
local POOL_NAME=$1
local DEVICE_TYPE=$2
local DEVICE_NTH=$3
${ZPOOL} status ${POOL_NAME} | grep ${DEVICE_TYPE} ${TMP_STATUS} | \
head -n${DEVICE_NTH} | tail -n1 | ${AWK} "{ print \$1 }"
}
vdev_status() {
local POOL_NAME=$1
local VDEV_NAME=$2
${ZPOOL} status ${POOL_NAME} | ${AWK} "/${VDEV_NAME}/ { print \$2 }"
}
# Required format is x.yz[KMGTP]
expand_numeric_suffix() {
local VALUE=$1
VALUE=`echo "${VALUE/%K/*1000}"`
VALUE=`echo "${VALUE/%M/*1000000}"`
VALUE=`echo "${VALUE/%G/*1000000000}"`
VALUE=`echo "${VALUE/%T/*1000000000000}"`
VALUE=`echo "${VALUE/%P/*1000000000000000}"`
VALUE=`echo "${VALUE}" | bc | cut -d'.' -f1`
echo "${VALUE}"
}
vdev_read_errors() {
local POOL_NAME=$1
local VDEV_NAME=$2
local VDEV_ERRORS=`${ZPOOL} status ${POOL_NAME} |
${AWK} "/${VDEV_NAME}/ { print \\$3 }"`
expand_numeric_suffix ${VDEV_ERRORS}
}
vdev_write_errors() {
local POOL_NAME=$1
local VDEV_NAME=$2
local VDEV_ERRORS=`${ZPOOL} status ${POOL_NAME} |
${AWK} "/${VDEV_NAME}/ { print \\$4 }"`
expand_numeric_suffix ${VDEV_ERRORS}
}
vdev_cksum_errors() {
local POOL_NAME=$1
local VDEV_NAME=$2
local VDEV_ERRORS=`${ZPOOL} status ${POOL_NAME} |
${AWK} "/${VDEV_NAME}/ { print \\$5 }"`
expand_numeric_suffix ${VDEV_ERRORS}
}
zpool_state() {
local POOL_NAME=$1
${ZPOOL} status ${POOL_NAME} | ${AWK} "/state/ { print \$2; exit }"
}
zpool_event() {
local EVENT_NAME=$1
local EVENT_KEY=$2
SCRIPT1="BEGIN {RS=\"\"; FS=\"\n\"} /${EVENT_NAME}/ { print \$0; exit }"
SCRIPT2="BEGIN {FS=\"=\"} /${EVENT_KEY}/ { print \$2; exit }"
${ZPOOL} events -vH | ${AWK} "${SCRIPT1}" | ${AWK} "${SCRIPT2}"
}
zpool_scan_errors() {
local POOL_NAME=$1
${ZPOOL} status ${POOL_NAME} | ${AWK} "/scan: scrub/ { print \$8 }"
${ZPOOL} status ${POOL_NAME} | ${AWK} "/scan: resilver/ { print \$7 }"
}
pattern_create() {
local PATTERN_BLOCK_SIZE=$1
local PATTERN_BLOCK_COUNT=$2
local PATTERN_NAME=`mktemp -p /tmp zpool.pattern.XXXXXXXX`
echo ${PATTERN_NAME}
dd if=/dev/urandom of=${PATTERN_NAME} bs=${PATTERN_BLOCK_SIZE} \
count=${PATTERN_BLOCK_COUNT} &>/dev/null
return $?
}
pattern_write() {
local PATTERN_NAME=$1
local PATTERN_BLOCK_SIZE=$2
local PATTERN_BLOCK_COUNT=$3
local DEVICE_NAME=$4
dd if=${PATTERN_NAME} of=${DEVICE_NAME} bs=${PATTERN_BLOCK_SIZE} \
count=${PATTERN_BLOCK_COUNT} oflag=direct &>/dev/null
return $?
}
pattern_write_bg() {
local PATTERN_NAME=$1
local PATTERN_BLOCK_SIZE=$2
local PATTERN_BLOCK_COUNT=$3
local DEVICE_NAME=$4
dd if=${PATTERN_NAME} of=${DEVICE_NAME} bs=${PATTERN_BLOCK_SIZE} \
count=${PATTERN_BLOCK_COUNT} oflag=direct &>/dev/null &
return $?
}
pattern_verify() {
local PATTERN_NAME=$1
local PATTERN_BLOCK_SIZE=$2
local PATTERN_BLOCK_COUNT=$3
local DEVICE_NAME=$4
local DEVICE_FILE=`mktemp -p /tmp zpool.pattern.XXXXXXXX`
dd if=${DEVICE_NAME} of=${DEVICE_FILE} bs=${PATTERN_BLOCK_SIZE} \
count=${PATTERN_BLOCK_COUNT} iflag=direct &>/dev/null
cmp -s ${PATTERN_NAME} ${DEVICE_FILE}
RC=$?
rm -f ${DEVICE_FILE}
return ${RC}
}
pattern_remove() {
local PATTERN_NAME=$1
rm -f ${PATTERN_NAME}
return $?
}
fault_set_md() {
local VDEV_FAULTY=$1
local FAULT_TYPE=$2
${MDADM} /dev/${VDEV_FAULTY} --grow --level=faulty \
--layout=${FAULT_TYPE} >/dev/null
return $?
}
fault_clear_md() {
local VDEV_FAULTY=$1
# Clear all failure injection.
${MDADM} /dev/${VDEV_FAULTY} --grow --level=faulty \
--layout=clear >/dev/null || return $?
${MDADM} /dev/${VDEV_FAULTY} --grow --level=faulty \
--layout=flush >/dev/null || return $?
return $?
}
fault_set_sd() {
local OPTS=$1
local NTH=$2
echo ${OPTS} >/sys/bus/pseudo/drivers/scsi_debug/opts
echo ${NTH} >/sys/bus/pseudo/drivers/scsi_debug/every_nth
}
fault_clear_sd() {
echo 0 >/sys/bus/pseudo/drivers/scsi_debug/every_nth
echo 0 >/sys/bus/pseudo/drivers/scsi_debug/opts
}
test_setup() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local ZVOL_NAME=$3
local TMP_CACHE=$4
${ZFS_SH} zfs="spa_config_path=${TMP_CACHE}" || fail 1
${ZPOOL_CREATE_SH} -p ${POOL_NAME} -c ${POOL_CONFIG} || fail 2
${ZFS} create -V 64M ${POOL_NAME}/${ZVOL_NAME} || fail 3
# Trigger udev and re-read the partition table to ensure all of
# this IO is out of the way before we begin injecting failures.
udev_trigger || fail 4
${BLOCKDEV} --rereadpt /dev/${POOL_NAME}/${ZVOL_NAME} || fail 5
}
test_cleanup() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local ZVOL_NAME=$3
local TMP_CACHE=$4
${ZFS} destroy ${POOL_NAME}/${ZVOL_NAME} || fail 101
${ZPOOL_CREATE_SH} -p ${POOL_NAME} -c ${POOL_CONFIG} -d || fail 102
${ZFS_SH} -u || fail 103
rm -f ${TMP_CACHE} || fail 104
}
test_write_soft() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Set soft write failure for first vdev device.
local VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md 1`
fault_set_md ${VDEV_FAULTY} write-transient
# The application must not observe an error.
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
fault_clear_md ${VDEV_FAULTY}
# Soft errors will not be logged to 'zpool status'
local WRITE_ERRORS=`vdev_write_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${WRITE_ERRORS} -eq 0 || fail 13
# Soft errors will still generate an EIO (5) event.
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 14
# Verify the known pattern.
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 15
pattern_remove ${TMP_PATTERN} || fail 16
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
# Soft write error.
test_1() {
test_write_soft tank lo-faulty-raid0 0
test_write_soft tank lo-faulty-raid10 1
test_write_soft tank lo-faulty-raidz 1
test_write_soft tank lo-faulty-raidz2 1
test_write_soft tank lo-faulty-raidz3 1
echo
}
run_test 1 "soft write error"
test_write_hard() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Set hard write failure for first vdev device.
local VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md 1`
fault_set_md ${VDEV_FAULTY} write-persistent
# The application must not observe an error.
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
fault_clear_md ${VDEV_FAULTY}
local WRITE_ERRORS=`vdev_write_errors ${POOL_NAME} ${VDEV_FAULTY}`
if [ ${POOL_REDUNDANT} -eq 1 ]; then
# For redundant configurations hard errors will not be
# logged to 'zpool status' but will generate EIO events.
test ${WRITE_ERRORS} -eq 0 || fail 21
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 22
else
# For non-redundant configurations hard errors will be
# logged to 'zpool status' and generate EIO events. They
# will also trigger a scrub of the impacted sectors.
sleep 10
test ${WRITE_ERRORS} -gt 0 || fail 31
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 32
test `zpool_event "zfs.resilver.start" "ena"` != "" || fail 33
test `zpool_event "zfs.resilver.finish" "ena"` != "" || fail 34
test `zpool_scan_errors ${POOL_NAME}` -eq 0 || fail 35
fi
# Verify the known pattern.
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 41
pattern_remove ${TMP_PATTERN} || fail 42
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
# Hard write error.
test_2() {
test_write_hard tank lo-faulty-raid0 0
test_write_hard tank lo-faulty-raid10 1
test_write_hard tank lo-faulty-raidz 1
test_write_hard tank lo-faulty-raidz2 1
test_write_hard tank lo-faulty-raidz3 1
echo
}
run_test 2 "hard write error"
test_write_all() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Set all write failures for first vdev device.
local VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md 1`
fault_set_md ${VDEV_FAULTY} write-all
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
if [ ${POOL_REDUNDANT} -eq 1 ]; then
# The application must not observe an error.
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
else
# The application is expected to hang in the background until
# the faulty device is repaired and 'zpool clear' is run.
pattern_write_bg ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 13
sleep 10
fi
fault_clear_md ${VDEV_FAULTY}
local WRITE_ERRORS=`vdev_write_errors ${POOL_NAME} ${VDEV_FAULTY}`
local VDEV_STATUS=`vdev_status ${POOL_NAME} ${VDEV_FAULTY}`
local POOL_STATE=`zpool_state ${POOL_NAME}`
# For all configurations write errors are logged to 'zpool status',
# and EIO events are generated. However, only a redundant config
# will cause the vdev to be FAULTED and pool DEGRADED. In a non-
# redundant config the IO will hang until 'zpool clear' is run.
test ${WRITE_ERRORS} -gt 0 || fail 14
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 15
if [ ${POOL_REDUNDANT} -eq 1 ]; then
test "${VDEV_STATUS}" = "FAULTED" || fail 21
test "${POOL_STATE}" = "DEGRADED" || fail 22
else
BLOCKED=`ps a | grep "${ZVOL_DEVICE}" | grep -c -v "grep"`
${ZPOOL} clear ${POOL_NAME} || fail 31
test ${BLOCKED} -eq 1 || fail 32
wait
fi
# Verify the known pattern.
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 41
pattern_remove ${TMP_PATTERN} || fail 42
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
# All write errors.
test_3() {
test_write_all tank lo-faulty-raid0 0
test_write_all tank lo-faulty-raid10 1
test_write_all tank lo-faulty-raidz 1
test_write_all tank lo-faulty-raidz2 1
test_write_all tank lo-faulty-raidz3 1
echo
}
run_test 3 "all write errors"
test_read_soft() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
local READ_ERRORS=0
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Create a pattern to be verified during a read error.
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
# Set soft read failure for all the vdevs to ensure we hit it.
for (( i=1; i<=4; i++ )); do
fault_set_md `nth_zpool_vdev ${POOL_NAME} md $i` read-transient
done
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 13
pattern_remove ${TMP_PATTERN} || fail 14
# Clear all failure injection and sum read errors.
for (( i=1; i<=4; i++ )); do
local VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md $i`
local VDEV_ERRORS=`vdev_read_errors ${POOL_NAME} ${VDEV_FAULTY}`
let READ_ERRORS=${READ_ERRORS}+${VDEV_ERRORS}
fault_clear_md ${VDEV_FAULTY}
done
# Soft errors will not be logged to 'zpool status'.
test ${READ_ERRORS} -eq 0 || fail 15
# Soft errors will still generate an EIO (5) event.
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 16
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
# Soft read error.
test_4() {
test_read_soft tank lo-faulty-raid0 0
test_read_soft tank lo-faulty-raid10 1
test_read_soft tank lo-faulty-raidz 1
test_read_soft tank lo-faulty-raidz2 1
test_read_soft tank lo-faulty-raidz3 1
echo
}
run_test 4 "soft read error"
test_read_hard() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
local READ_ERRORS=0
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Create a pattern to be verified during a read error.
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
# Set hard read failure for the fourth vdev.
local VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md 4`
fault_set_md ${VDEV_FAULTY} read-persistent
# For a redundant pool there must be no IO error, for a non-redundant
# pool we expect permanent damage and an IO error during verify, unless
# we get exceptionally lucky and have just damaged redundant metadata.
if [ ${POOL_REDUNDANT} -eq 1 ]; then
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 21
local READ_ERRORS=`vdev_read_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${READ_ERRORS} -eq 0 || fail 22
else
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE}
${ZPOOL} scrub ${POOL_NAME} || fail 32
local READ_ERRORS=`vdev_read_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${READ_ERRORS} -gt 0 || fail 33
${ZPOOL} status -v ${POOL_NAME} | \
grep -A8 "Permanent errors" | \
grep -q "${POOL_NAME}" || fail 34
fi
pattern_remove ${TMP_PATTERN} || fail 41
# Clear all failure injection and sum read errors.
fault_clear_md ${VDEV_FAULTY}
# Hard errors will generate an EIO (5) event.
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 42
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
# Hard read error.
test_5() {
test_read_hard tank lo-faulty-raid0 0
test_read_hard tank lo-faulty-raid10 1
test_read_hard tank lo-faulty-raidz 1
test_read_hard tank lo-faulty-raidz2 1
test_read_hard tank lo-faulty-raidz3 1
echo
}
run_test 5 "hard read error"
# Fixable read error.
test_read_fixable() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
local READ_ERRORS=0
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Create a pattern to be verified during a read error.
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
# Set hard read failure for the fourth vdev.
local VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md 4`
fault_set_md ${VDEV_FAULTY} read-fixable
# For a redundant pool there must be no IO error, for a non-redundant
# pool we expect permanent damage and an IO error during verify, unless
# we get exceptionally lucky and have just damaged redundant metadata.
if [ ${POOL_REDUNDANT} -eq 1 ]; then
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 21
local READ_ERRORS=`vdev_read_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${READ_ERRORS} -eq 0 || fail 22
else
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE}
${ZPOOL} scrub ${POOL_NAME} || fail 32
local READ_ERRORS=`vdev_read_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${READ_ERRORS} -gt 0 || fail 33
${ZPOOL} status -v ${POOL_NAME} | \
grep -A8 "Permanent errors" | \
grep -q "${POOL_NAME}" || fail 34
fi
pattern_remove ${TMP_PATTERN} || fail 41
# Clear all failure injection and sum read errors.
fault_clear_md ${VDEV_FAULTY}
# Hard errors will generate an EIO (5) event.
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 42
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
# Read errors fixable with a write.
test_6() {
test_read_fixable tank lo-faulty-raid0 0
test_read_fixable tank lo-faulty-raid10 1
test_read_fixable tank lo-faulty-raidz 1
test_read_fixable tank lo-faulty-raidz2 1
test_read_fixable tank lo-faulty-raidz3 1
echo
}
run_test 6 "fixable read error"
test_cksum() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local VDEV_DAMAGE="$4"
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
if [ ${MD_PARTITIONABLE} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Create a pattern to be verified.
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
# Verify the pattern and that no vdev has cksum errors.
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 13
for (( i=1; i<4; i++ )); do
VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md ${i}`
CKSUM_ERRORS=`vdev_cksum_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${CKSUM_ERRORS} -eq 0 || fail 14
done
# Corrupt the bulk of a vdev with random garbage, we damage as many
# vdevs as we have levels of redundancy. For example for a raidz3
# configuration we can trash 3 vdevs and still expect correct data.
# This improves the odds that we read one of the damaged vdevs.
for VDEV in ${VDEV_DAMAGE}; do
VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} md $VDEV`
pattern_write /dev/urandom 1M 64 /dev/${VDEV_FAULTY}p1
done
# Verify the pattern is still correct. For non-redundant pools
# expect failure and for redundant pools success due to resilvering.
if [ ${POOL_REDUNDANT} -eq 1 ]; then
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 16
else
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} && fail 17
fi
CKSUM_ERRORS=`vdev_cksum_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${CKSUM_ERRORS} -gt 0 || fail 18
STATUS=`vdev_status ${POOL_NAME} ${VDEV_FAULTY}`
test "${STATUS}" = "ONLINE" || fail 19
# The checksum errors must be logged as an event.
local CKSUM_ERRORS=`zpool_event "zfs.checksum" "zio_err"`
test ${CKSUM_ERRORS} = "0x34" || test ${CKSUM_ERRORS} = "0x0" || fail 20
# Verify permant errors for non-redundant pools, and for redundant
# pools trigger a scrub and check that all checksums have been fixed.
if [ ${POOL_REDUNDANT} -eq 1 ]; then
# Scrub the checksum errors and clear the faults.
${ZPOOL} scrub ${POOL_NAME} || fail 21
sleep 3
${ZPOOL} clear ${POOL_NAME} || fail 22
# Re-verify the pattern for fixed checksums.
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 23
CKSUM_ERRORS=`vdev_cksum_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${CKSUM_ERRORS} -eq 0 || fail 24
# Re-verify the entire pool for fixed checksums.
${ZPOOL} scrub ${POOL_NAME} || fail 25
CKSUM_ERRORS=`vdev_cksum_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${CKSUM_ERRORS} -eq 0 || fail 26
else
${ZPOOL} status -v ${POOL_NAME} | \
grep -A8 "Permanent errors" | \
grep -q "${POOL_NAME}/${ZVOL_NAME}" || fail 31
${ZPOOL} clear ${POOL_NAME} || fail 32
fi
pattern_remove ${TMP_PATTERN} || fail 41
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
# Silent data corruption
test_7() {
test_cksum tank lo-faulty-raid0 0 "1"
test_cksum tank lo-faulty-raid10 1 "1 3"
test_cksum tank lo-faulty-raidz 1 "4"
test_cksum tank lo-faulty-raidz2 1 "3 4"
test_cksum tank lo-faulty-raidz3 1 "2 3 4"
echo
}
run_test 7 "silent data corruption"
# Soft write timeout at the scsi device layer.
test_write_timeout_soft() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local POOL_NTH=$4
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
if [ ${SCSI_DEBUG} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
# Set timeout(0x4) for every nth command.
fault_set_sd 4 ${POOL_NTH}
# The application must not observe an error.
local TMP_PATTERN=`pattern_create 1M 8` || fail 11
pattern_write ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 12
fault_clear_sd
# Intermittent write timeouts even with FAILFAST set may not cause
# an EIO (5) event. This is because how FAILFAST is handled depends
# a log on the low level driver and the exact nature of the failure.
# We will however see a 'zfs.delay' event logged due to the timeout.
VDEV_DELAY=`zpool_event "zfs.delay" "zio_delay"`
test `printf "%d" ${VDEV_DELAY}` -ge 30000 || fail 13
# Verify the known pattern.
pattern_verify ${TMP_PATTERN} 1M 8 ${ZVOL_DEVICE} || fail 14
pattern_remove ${TMP_PATTERN} || fail 15
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
test_8() {
test_write_timeout_soft tank scsi_debug-raid0 0 50
test_write_timeout_soft tank scsi_debug-raid10 1 100
test_write_timeout_soft tank scsi_debug-raidz 1 75
test_write_timeout_soft tank scsi_debug-raidz2 1 150
test_write_timeout_soft tank scsi_debug-raidz3 1 300
echo
}
run_test 8 "soft write timeout"
# Persistent write timeout at the scsi device layer.
test_write_timeout_hard() {
local POOL_NAME=$1
local POOL_CONFIG=$2
local POOL_REDUNDANT=$3
local POOL_NTH=$4
local ZVOL_NAME="zvol"
local ZVOL_DEVICE="/dev/${POOL_NAME}/${ZVOL_NAME}"
local RESCAN=1
if [ ${SCSI_DEBUG} -eq 0 ]; then
skip_nonewline
return
fi
local TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX`
test_setup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
local TMP_PATTERN1=`pattern_create 1M 8`
local TMP_PATTERN2=`pattern_create 1M 8`
local TMP_PATTERN3=`pattern_create 1M 8`
# Create three partitions each one gets a unique pattern. The first
# pattern is written before the failure, the second pattern during
# the failure, and the third pattern while the vdev is degraded.
# All three patterns are verified while the vdev is degraded and
# then again once it is brought back online.
${PARTED} -s ${ZVOL_DEVICE} mklabel gpt || fail 11
${PARTED} -s ${ZVOL_DEVICE} mkpart primary 1M 16M || fail 12
${PARTED} -s ${ZVOL_DEVICE} mkpart primary 16M 32M || fail 13
${PARTED} -s ${ZVOL_DEVICE} mkpart primary 32M 48M || fail 14
wait_udev ${ZVOL_DEVICE}1 30
wait_udev ${ZVOL_DEVICE}2 30
wait_udev ${ZVOL_DEVICE}3 30
# Before the failure.
pattern_write ${TMP_PATTERN1} 1M 8 ${ZVOL_DEVICE}1 || fail 15
# Get the faulty vdev name.
local VDEV_FAULTY=`nth_zpool_vdev ${POOL_NAME} sd 1`
# Set timeout(0x4) for every nth command.
fault_set_sd 4 ${POOL_NTH}
# During the failure.
pattern_write ${TMP_PATTERN2} 1M 8 ${ZVOL_DEVICE}2 || fail 21
# Expect write errors to be logged to 'zpool status'
local WRITE_ERRORS=`vdev_write_errors ${POOL_NAME} ${VDEV_FAULTY}`
test ${WRITE_ERRORS} -gt 0 || fail 22
local VDEV_STATUS=`vdev_status ${POOL_NAME} ${VDEV_FAULTY}`
test "${VDEV_STATUS}" = "UNAVAIL" || fail 23
# Clear the error and remove it from /dev/.
fault_clear_sd
rm -f /dev/${VDEV_FAULTY}[0-9]
# Verify the first two patterns and write out the third.
pattern_write ${TMP_PATTERN3} 1M 8 ${ZVOL_DEVICE}3 || fail 31
pattern_verify ${TMP_PATTERN1} 1M 8 ${ZVOL_DEVICE}1 || fail 32
pattern_verify ${TMP_PATTERN2} 1M 8 ${ZVOL_DEVICE}2 || fail 33
pattern_verify ${TMP_PATTERN3} 1M 8 ${ZVOL_DEVICE}3 || fail 34
# Bring the device back online by rescanning for it. It must appear
# in lsscsi and be available to dd before allowing ZFS to bring it
# online. This is not required but provides additional sanity.
while [ ${RESCAN} -eq 1 ]; do
scsi_rescan
wait_udev /dev/${VDEV_FAULTY} 30
if [ `${LSSCSI} | grep -c "/dev/${VDEV_FAULTY}"` -eq 0 ]; then
continue
fi
dd if=/dev/${VDEV_FAULTY} of=/dev/null bs=8M count=1 &>/dev/null
if [ $? -ne 0 ]; then
continue
fi
RESCAN=0
done
# Bring the device back online. We expect it to be automatically
# resilvered without error and we should see minimally the zfs.io,
# zfs.statechange (VDEV_STATE_HEALTHY (0x7)), and zfs.resilver.*
# events posted.
${ZPOOL} online ${POOL_NAME} ${VDEV_FAULTY} || fail 51
sleep 3
test `zpool_event "zfs.io" "zio_err"` = "0x5" || fail 52
test `zpool_event "zfs.statechange" "vdev_state"` = "0x7" || fail 53
test `zpool_event "zfs.resilver.start" "ena"` != "" || fail 54
test `zpool_event "zfs.resilver.finish" "ena"` != "" || fail 55
test `zpool_scan_errors ${POOL_NAME}` -eq 0 || fail 56
local VDEV_STATUS=`vdev_status ${POOL_NAME} ${VDEV_FAULTY}`
test "${VDEV_STATUS}" = "ONLINE" || fail 57
# Verify the known pattern.
pattern_verify ${TMP_PATTERN1} 1M 8 ${ZVOL_DEVICE}1 || fail 61
pattern_verify ${TMP_PATTERN2} 1M 8 ${ZVOL_DEVICE}2 || fail 62
pattern_verify ${TMP_PATTERN3} 1M 8 ${ZVOL_DEVICE}3 || fail 63
pattern_remove ${TMP_PATTERN1} || fail 64
pattern_remove ${TMP_PATTERN2} || fail 65
pattern_remove ${TMP_PATTERN3} || fail 66
test_cleanup ${POOL_NAME} ${POOL_CONFIG} ${ZVOL_NAME} ${TMP_CACHE}
pass_nonewline
}
test_9() {
skip_nonewline # Skip non-redundant config
test_write_timeout_hard tank scsi_debug-raid10 1 -50
test_write_timeout_hard tank scsi_debug-raidz 1 -50
test_write_timeout_hard tank scsi_debug-raidz2 1 -50
test_write_timeout_hard tank scsi_debug-raidz3 1 -50
echo
}
run_test 9 "hard write timeout"
exit 0

343
scripts/zfs-tests.sh Executable file
View File

@ -0,0 +1,343 @@
#!/bin/bash
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License"). You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
basedir="$(dirname $0)"
SCRIPT_COMMON=common.sh
if [ -f "${basedir}/${SCRIPT_COMMON}" ]; then
. "${basedir}/${SCRIPT_COMMON}"
else
echo "Missing helper script ${SCRIPT_COMMON}" && exit 1
fi
. $STF_SUITE/include/default.cfg
PROG=zfs-tests.sh
SUDO=/usr/bin/sudo
SETENFORCE=/usr/sbin/setenforce
VERBOSE=
QUIET=
CLEANUP=1
CLEANUPALL=0
LOOPBACK=1
FILESIZE="2G"
RUNFILE=${RUNFILE:-"linux.run"}
FILEDIR=${FILEDIR:-/var/tmp}
DISKS=${DISKS:-""}
#
# Attempt to remove loopback devices and files which where created earlier
# by this script to run the test framework. The '-k' option may be passed
# to the script to suppress cleanup for debugging purposes.
#
cleanup() {
if [ $CLEANUP -eq 0 ]; then
return 0
fi
if [ $LOOPBACK -eq 1 ]; then
for TEST_LOOPBACK in ${LOOPBACKS}; do
LOOP_DEV=$(basename $TEST_LOOPBACK)
DM_DEV=$(${SUDO} ${DMSETUP} ls 2>/dev/null | \
grep ${LOOP_DEV} | cut -f1)
if [ -n "$DM_DEV" ]; then
${SUDO} ${DMSETUP} remove ${DM_DEV} ||
echo "Failed to remove: ${DM_DEV}"
fi
if [ -n "${TEST_LOOPBACK}" ]; then
${SUDO} ${LOSETUP} -d ${TEST_LOOPBACK} ||
echo "Failed to remove: ${TEST_LOOPBACK}"
fi
done
fi
for TEST_FILE in ${FILES}; do
rm -f ${TEST_FILE} &>/dev/null
done
}
trap cleanup EXIT
#
# Attempt to remove all testpools (testpool.XXX), unopened dm devices,
# loopback devices, and files. This is a useful way to cleanup a previous
# test run failure which has left the system in an unknown state. This can
# be dangerous and should only be used in a dedicated test environment.
#
cleanup_all() {
local TEST_POOLS=$(${SUDO} ${ZPOOL} list -H -o name | grep testpool)
local TEST_LOOPBACKS=$(${SUDO} ${LOSETUP} -a|grep file-vdev|cut -f1 -d:)
local TEST_FILES=$(ls /var/tmp/file-vdev* 2>/dev/null)
msg
msg "--- Cleanup ---"
msg "Removing pool(s): $(echo ${TEST_POOLS} | tr '\n' ' ')"
for TEST_POOL in $TEST_POOLS; do
${SUDO} ${ZPOOL} destroy ${TEST_POOL}
done
msg "Removing dm(s): $(${SUDO} ${DMSETUP} ls |
grep loop | tr '\n' ' ')"
${SUDO} ${DMSETUP} remove_all
msg "Removing loopback(s): $(echo ${TEST_LOOPBACKS} | tr '\n' ' ')"
for TEST_LOOPBACK in $TEST_LOOPBACKS; do
${SUDO} ${LOSETUP} -d ${TEST_LOOPBACK}
done
msg "Removing files(s): $(echo ${TEST_FILES} | tr '\n' ' ')"
for TEST_FILE in $TEST_FILES; do
${SUDO} rm -f ${TEST_FILE}
done
}
#
# Log a failure message, cleanup, and return an error.
#
fail() {
echo -e "${PROG}: $1" >&2
cleanup
exit 1
}
#
# Takes a name as the only arguments and looks for the following variations
# on that name. If one is found it is returned.
#
# $RUNFILEDIR/<name>
# $RUNFILEDIR/<name>.run
# <name>
# <name>.run
#
find_runfile() {
local NAME=$1
local RESULT=""
if [ -f "$RUNFILEDIR/$NAME" ]; then
RESULT="$RUNFILEDIR/$NAME"
elif [ -f "$RUNFILEDIR/$NAME.run" ]; then
RESULT="$RUNFILEDIR/$NAME.run"
elif [ -f "$NAME" ]; then
RESULT="$NAME"
elif [ -f "$NAME.run" ]; then
RESULT="$NAME.run"
fi
echo "$RESULT"
}
#
# Output a useful usage message.
#
usage() {
cat << EOF
USAGE:
$0 [hvqxkf] [-s SIZE] [-r RUNFILE]
DESCRIPTION:
ZFS Test Suite launch script
OPTIONS:
-h Show this message
-v Verbose zfs-tests.sh output
-q Quiet test-runner output
-x Remove all testpools, dm, lo, and files (unsafe)
-k Disable cleanup after test failure
-f Use files only, disables block device tests
-d DIR Use DIR for files and loopback devices
-s SIZE Use vdevs of SIZE (default: 4G)
-r RUNFILE Run tests in RUNFILE (default: linux.run)
EXAMPLES:
# Run the default (linux) suite of tests and output the configuration used.
$0 -v
# Run a smaller suite of tests designed to run more quickly.
$0 -r linux-fast
# Cleanup a previous run of the test suite prior to testing, run the
# default (linux) suite of tests and perform no cleanup on exit.
$0 -c
EOF
}
while getopts 'hvqxkfd:s:r:?' OPTION; do
case $OPTION in
h)
usage
exit 1
;;
v)
VERBOSE=1
;;
q)
QUIET="-q"
;;
x)
CLEANUPALL=1
;;
k)
CLEANUP=0
;;
f)
LOOPBACK=0
;;
d)
FILEDIR="$OPTARG"
;;
s)
FILESIZE="$OPTARG"
;;
r)
RUNFILE="$OPTARG"
;;
?)
usage
exit
;;
esac
done
shift $((OPTIND-1))
FILES=${FILES:-"$FILEDIR/file-vdev0 $FILEDIR/file-vdev1 $FILEDIR/file-vdev2"}
LOOPBACKS=${LOOPBACKS:-""}
#
# Attempt to locate the runfile describing the test workload.
#
if [ -n "$RUNFILE" ]; then
SAVED_RUNFILE="$RUNFILE"
RUNFILE=$(find_runfile "$RUNFILE")
[ -z "$RUNFILE" ] && fail "Cannot find runfile: $SAVED_RUNFILE"
fi
if [ ! -r "$RUNFILE" ]; then
fail "Cannot read runfile: $RUNFILE"
fi
#
# This script should not be run as root. Instead the test user, which may
# be a normal user account, needs to be configured such that it can
# run commands via sudo passwordlessly.
#
if [ $(id -u) = "0" ]; then
fail "This script must not be run as root."
fi
if [ $(sudo whoami) != "root" ]; then
fail "Passwordless sudo access required."
fi
#
# Verify the ZFS module stack if loaded.
#
${SUDO} ${ZFS_SH} &>/dev/null
#
# Attempt to cleanup all previous state for a new test run.
#
if [ $CLEANUPALL -ne 0 ]; then
cleanup_all
fi
#
# By default preserve any existing pools
#
if [ -z "${KEEP}" ]; then
KEEP=$(${SUDO} ${ZPOOL} list -H -o name)
if [ -z "${KEEP}" ]; then
KEEP="rpool"
fi
fi
msg
msg "--- Configuration ---"
msg "Runfile: $RUNFILE"
msg "STF_TOOLS: $STF_TOOLS"
msg "STF_SUITE: $STF_SUITE"
#
# No DISKS have been provided so a basic file or loopback based devices
# must be created for the test suite to use.
#
if [ -z "${DISKS}" ]; then
#
# Create sparse files for the test suite. These may be used
# directory or have loopback devices layered on them.
#
for TEST_FILE in ${FILES}; do
[ -f "$TEST_FILE" ] && fail "Failed file exists: ${TEST_FILE}"
truncate -s ${FILESIZE} ${TEST_FILE} ||
fail "Failed creating: ${TEST_FILE} ($?)"
DISKS="$DISKS$TEST_FILE "
done
#
# If requested setup loopback devices backed by the sparse files.
#
if [ $LOOPBACK -eq 1 ]; then
DISKS=""
check_loop_utils
for TEST_FILE in ${FILES}; do
TEST_LOOPBACK=$(${SUDO} ${LOSETUP} -f)
${SUDO} ${LOSETUP} ${TEST_LOOPBACK} ${TEST_FILE} ||
fail "Failed: ${TEST_FILE} -> ${TEST_LOOPBACK}"
LOOPBACKS="${LOOPBACKS}${TEST_LOOPBACK} "
DISKS="$DISKS$(basename $TEST_LOOPBACK) "
done
fi
fi
NUM_DISKS=$(echo ${DISKS} | $AWK '{print NF}')
[ $NUM_DISKS -lt 3 ] && fail "Not enough disks ($NUM_DISKS/3 minimum)"
#
# Disable SELinux until the ZFS Test Suite has been updated accordingly.
#
if [ -x ${SETENFORCE} ]; then
${SUDO} ${SETENFORCE} permissive &>/dev/null
fi
msg "FILEDIR: $FILEDIR"
msg "FILES: $FILES"
msg "LOOPBACKS: $LOOPBACKS"
msg "DISKS: $DISKS"
msg "NUM_DISKS: $NUM_DISKS"
msg "FILESIZE: $FILESIZE"
msg "Keep pool(s): $KEEP"
msg ""
export STF_TOOLS
export STF_SUITE
export DISKS
export KEEP
msg "${TEST_RUNNER} ${QUIET} -c ${RUNFILE} -i ${STF_SUITE}"
${TEST_RUNNER} ${QUIET} -c ${RUNFILE} -i ${STF_SUITE}
RESULT=$?
echo
exit ${RESULT}

1
tests/Makefile.am Normal file
View File

@ -0,0 +1 @@
SUBDIRS = runfiles test-runner zfs-tests

133
tests/README.md Normal file
View File

@ -0,0 +1,133 @@
# ZFS Test Suite README
1) Building and installing the ZFS Test Suite
The ZFS Test Suite runs under the test-runner framework. This framework
is built along side the standard ZFS utilities and is included as part of
zfs-test package. The zfs-test package can be built from source as follows:
$ ./configure
$ make pkg-utils
The resulting packages can be installed using the rpm or dpkg command as
appropriate for your distributions. Alternately, if you have installed
ZFS from a distributions repository (not from source) the zfs-test package
may be provided for your distribution.
- Installed from source
$ rpm -ivh ./zfs-test*.rpm, or
$ dpkg -i ./zfs-test*.deb,
- Installed from package repository
$ yum install zfs-test
$ apt-get install zfs-test
2) Running the ZFS Test Suite
The pre-requisites for running the ZFS Test Suite are:
* Three scratch disks
* Specify the disks you wish to use in the $DISKS variable, as a
space delimited list like this: DISKS='vdb vdc vdd'. By default
the zfs-tests.sh sciprt will construct three loopback devices to
be used for testing: DISKS='loop0 loop1 loop2'.
* A non-root user with a full set of basic privileges and the ability
to sudo(8) to root without a password to run the test.
* Specify any pools you wish to preserve as a space delimited list in
the $KEEP variable. All pools detected at the start of testing are
added automatically.
* The ZFS Test Suite will add users and groups to test machine to
verify functionality. Therefore it is strongly advised that a
dedicated test machine, which can be a VM, be used for testing.
Once the pre-requisites are satisfied simply run the zfs-tests.sh script:
$ /usr/share/zfs/zfs-tests.sh
Alternately, the zfs-tests.sh script can be run from the source tree to allow
developers to rapidly validate their work. In this mode the ZFS utilities and
modules from the source tree will be used (rather than those installed on the
system). In order to avoid certain types of failures you will need to ensure
the ZFS udev rules are installed. This can be done manually or by ensuring
some version of ZFS is installed on the system.
$ ./scripts/zfs-tests.sh
The following zfs-tests.sh options are supported:
-v Verbose zfs-tests.sh output When specified additional
information describing the test environment will be logged
prior to invoking test-runner. This includes the runfile
being used, the DISKS targeted, pools to keep, etc.
-q Quiet test-runner output. When specified it is passed to
test-runner(1) which causes output to be written to the
console only for tests that do not pass and the results
summary.
-x Remove all testpools, dm, lo, and files (unsafe). When
specified the script will attempt to remove any leftover
configuration from a previous test run. This includes
destroying any pools named testpool, unused DM devices,
and loopback devices backed by file-vdevs. This operation
can be DANGEROUS because it is possible that the script
will mistakenly remove a resource not related to the testing.
-k Disable cleanup after test failure. When specified the
zfs-tests.sh script will not perform any additional cleanup
when test-runner exists. This is useful when the results of
a specific test need to be preserved for further analysis.
-f Use sparse files directly instread of loopback devices for
the testing. When running in this mode certain tests will
be skipped which depend on real block devices.
-d DIR Create sparse files for vdevs in the DIR directory. By
default these files are created under /var/tmp/.
-s SIZE Use vdevs of SIZE (default: 2G)
-r RUNFILE Run tests in RUNFILE (default: linux.run)
The ZFS Test Suite allows the user to specify a subset of the tests via a
runfile. The format of the runfile is explained in test-runner(1), and
the files that zfs-tests.sh uses are available for reference under
/usr/share/zfs/runfiles. To specify a custom runfile, use the -r option:
$ /usr/share/zfs/zfs-tests.sh -r my_tests.run
3) Test results
While the ZFS Test Suite is running, one informational line is printed at the
end of each test, and a results summary is printed at the end of the run. The
results summary includes the location of the complete logs, which is logged in
the form /var/tmp/test_results/[ISO 8601 date]. A normal test run launched
with the `zfs-tests.sh` wrapper script will look something like this:
$ /usr/share/zfs/zfs-tests.sh -v -d /mnt
--- Configuration ---
Runfile: /usr/share/zfs/runfiles/linux.run
STF_TOOLS: /usr/share/zfs/test-runner
STF_SUITE: /usr/share/zfs/zfs-tests
FILEDIR: /mnt
FILES: /mnt/file-vdev0 /mnt/file-vdev1 /mnt/file-vdev2
LOOPBACKS: /dev/loop0 /dev/loop1 /dev/loop2
DISKS: loop0 loop1 loop2
NUM_DISKS: 3
FILESIZE: 2G
Keep pool(s): rpool
/usr/share/zfs/test-runner/bin/test-runner.py -c \
/usr/share/zfs/runfiles/linux.run -i /usr/share/zfs/zfs-tests
Test: .../tests/functional/acl/posix/setup (run as root) [00:00] [PASS]
...470 additional tests...
Test: .../tests/functional/zvol/zvol_cli/cleanup (run as root) [00:00] [PASS]
Results Summary
PASS 472
Running Time: 00:45:09
Percent passed: 100.0%
Log directory: /var/tmp/test_results/20160316T181651

View File

@ -0,0 +1,2 @@
pkgdatadir = $(datadir)/@PACKAGE@/runfiles
dist_pkgdata_SCRIPTS = *.run

647
tests/runfiles/linux.run Normal file
View File

@ -0,0 +1,647 @@
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
[DEFAULT]
pre = setup
quiet = False
pre_user = root
user = root
timeout = 600
post_user = root
post = cleanup
outputdir = /var/tmp/test_results
# DISABLED:
# posix_001_pos - needs investigation
[tests/functional/acl/posix]
tests = ['posix_002_pos']
[tests/functional/atime]
tests = ['atime_001_pos', 'atime_002_neg']
# DISABLED:
# bootfs_006_pos - needs investigation
# bootfs_008_neg - needs investigation
[tests/functional/bootfs]
tests = ['bootfs_001_pos', 'bootfs_002_neg', 'bootfs_003_pos',
'bootfs_004_neg', 'bootfs_005_neg', 'bootfs_007_neg']
# DISABLED:
# cache_001_pos - needs investigation
# cache_010_neg - needs investigation
[tests/functional/cache]
tests = ['cache_002_pos', 'cache_003_pos', 'cache_004_neg',
'cache_005_neg', 'cache_006_pos', 'cache_007_neg', 'cache_008_neg',
'cache_009_pos', 'cache_011_pos']
# DISABLED: needs investigation
#[tests/functional/cachefile]
#tests = ['cachefile_001_pos', 'cachefile_002_pos', 'cachefile_003_pos',
# 'cachefile_004_pos']
#pre =
#post =
# DISABLED: needs investigation
# 'sensitive_none_lookup', 'sensitive_none_delete',
# 'sensitive_formd_lookup', 'sensitive_formd_delete',
# 'insensitive_none_lookup', 'insensitive_none_delete',
# 'insensitive_formd_lookup', 'insensitive_formd_delete',
# 'mixed_none_lookup', 'mixed_none_lookup_ci', 'mixed_none_delete',
# 'mixed_formd_lookup', 'mixed_formd_lookup_ci', 'mixed_formd_delete']
[tests/functional/casenorm]
tests = ['case_all_values', 'norm_all_values']
[tests/functional/clean_mirror]
tests = [ 'clean_mirror_001_pos', 'clean_mirror_002_pos',
'clean_mirror_003_pos', 'clean_mirror_004_pos']
[tests/functional/cli_root/zdb]
tests = ['zdb_001_neg']
pre =
post =
[tests/functional/cli_root/zfs]
tests = ['zfs_001_neg', 'zfs_002_pos', 'zfs_003_neg']
# DISABLED:
# zfs_clone_005_pos - busy unmount
# zfs_clone_010_pos - needs investigation
[tests/functional/cli_root/zfs_clone]
tests = ['zfs_clone_001_neg', 'zfs_clone_002_pos', 'zfs_clone_003_pos',
'zfs_clone_004_pos', 'zfs_clone_006_pos',
'zfs_clone_007_pos', 'zfs_clone_008_neg', 'zfs_clone_009_neg']
# DISABLED:
# zfs_copies_002_pos - needs investigation
# zfs_copies_003_pos - zpool on zvol
# zfs_copies_005_neg - nested pools
[tests/functional/cli_root/zfs_copies]
tests = ['zfs_copies_001_pos', 'zfs_copies_004_neg', 'zfs_copies_006_pos']
# DISABLED:
# zfs_create_006_pos - needs investigation
# zfs_create_003_pos - needs investigation
[tests/functional/cli_root/zfs_create]
tests = ['zfs_create_001_pos', 'zfs_create_002_pos',
'zfs_create_004_pos', 'zfs_create_005_pos',
'zfs_create_007_pos', 'zfs_create_008_neg', 'zfs_create_009_neg',
'zfs_create_010_neg', 'zfs_create_011_pos', 'zfs_create_012_pos',
'zfs_create_013_pos']
# DISABLED:
# zfs_destroy_001_pos - busy mountpoint behavior
# zfs_destroy_004_pos - busy mountpoint behavior
# zfs_destroy_005_neg - busy mountpoint behavior
# zfs_destroy_008_pos - busy mountpoint behavior
# zfs_destroy_009_pos - busy mountpoint behavior
# zfs_destroy_010_pos - busy mountpoint behavior
# zfs_destroy_011_pos - busy mountpoint behavior
# zfs_destroy_012_pos - busy mountpoint behavior
# zfs_destroy_013_neg - busy mountpoint behavior
[tests/functional/cli_root/zfs_destroy]
tests = ['zfs_destroy_002_pos', 'zfs_destroy_003_pos', 'zfs_destroy_006_neg',
'zfs_destroy_007_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
'zfs_destroy_016_pos']
# DISABLED:
# zfs_get_004_pos - nested pools
# zfs_get_006_neg - needs investigation
[tests/functional/cli_root/zfs_get]
tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
'zfs_get_005_neg', 'zfs_get_007_neg', 'zfs_get_008_pos',
'zfs_get_009_pos', 'zfs_get_010_neg']
[tests/functional/cli_root/zfs_inherit]
tests = ['zfs_inherit_001_neg', 'zfs_inherit_002_neg', 'zfs_inherit_003_pos']
# DISABLED:
# zfs_mount_005_pos - needs investigation
# zfs_mount_006_pos - needs investigation
# zfs_mount_007_pos - needs investigation
# zfs_mount_009_neg - needs investigation
# zfs_mount_010_neg - needs investigation
# zfs_mount_all_001_pos - needs investigation
[tests/functional/cli_root/zfs_mount]
tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos',
'zfs_mount_004_pos', 'zfs_mount_008_pos',
'zfs_mount_011_neg']
[tests/functional/cli_root/zfs_promote]
tests = ['zfs_promote_001_pos', 'zfs_promote_002_pos', 'zfs_promote_003_pos',
'zfs_promote_004_pos', 'zfs_promote_005_pos', 'zfs_promote_006_neg',
'zfs_promote_007_neg', 'zfs_promote_008_pos']
# DISABLED:
# zfs_written_property_001_pos - sync(1) does not force txg under Linux
[tests/functional/cli_root/zfs_property]
tests = []
# DISABLED:
# zfs_receive_003_pos - needs investigation
# zfs_receive_010_pos - needs investigation
# zfs_receive_011_pos - needs investigation
# zfs_receive_012_pos - needs investigation
[tests/functional/cli_root/zfs_receive]
tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_005_neg',
'zfs_receive_006_pos', 'zfs_receive_007_neg', 'zfs_receive_008_pos',
'zfs_receive_009_neg']
# DISABLED:
# zfs_rename_002_pos - needs investigation
# zfs_rename_005_neg - nested pools
# zfs_rename_006_pos - needs investigation
# zfs_rename_007_pos - needs investigation
[tests/functional/cli_root/zfs_rename]
tests = ['zfs_rename_001_pos', 'zfs_rename_003_pos',
'zfs_rename_004_neg', 'zfs_rename_008_pos', 'zfs_rename_009_neg',
'zfs_rename_010_neg', 'zfs_rename_011_pos', 'zfs_rename_012_neg',
'zfs_rename_013_pos']
[tests/functional/cli_root/zfs_reservation]
tests = ['zfs_reservation_001_pos', 'zfs_reservation_002_pos']
# DISABLED:
# zfs_rollback_001_pos - busy mountpoint behavior
# zfs_rollback_002_pos - busy mountpoint behavior
[tests/functional/cli_root/zfs_rollback]
tests = ['zfs_rollback_003_neg', 'zfs_rollback_004_neg']
# DISABLED:
# zfs_send_007_pos - needs investigation
[tests/functional/cli_root/zfs_send]
tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos',
'zfs_send_004_neg', 'zfs_send_005_pos', 'zfs_send_006_pos']
# DISABLED:
# mountpoint_003_pos - needs investigation
# ro_props_001_pos - needs investigation
# onoffs_001_pos - needs investigation
# property_alias_001_pos - needs investigation
# readonly_001_pos - needs investigation
# user_property_002_pos - needs investigation
[tests/functional/cli_root/zfs_set]
tests = ['cache_001_pos', 'cache_002_neg', 'canmount_001_pos',
'canmount_002_pos', 'canmount_003_pos', 'canmount_004_pos',
'checksum_001_pos', 'compression_001_pos', 'mountpoint_001_pos',
'mountpoint_002_pos', 'reservation_001_neg',
'share_mount_001_neg', 'snapdir_001_pos',
'user_property_001_pos', 'user_property_003_neg',
'user_property_004_pos', 'version_001_neg', 'zfs_set_001_neg',
'zfs_set_002_neg', 'zfs_set_003_neg']
# DISABLED: Tests need to be updated for Linux share behavior
#[tests/functional/cli_root/zfs_share]
#tests = ['zfs_share_001_pos', 'zfs_share_002_pos', 'zfs_share_003_pos',
# 'zfs_share_004_pos', 'zfs_share_005_pos', 'zfs_share_006_pos',
# 'zfs_share_007_neg', 'zfs_share_008_neg', 'zfs_share_009_neg',
# 'zfs_share_010_neg', 'zfs_share_011_pos']
# DISABLED:
# zfs_snapshot_008_neg - nested pools
[tests/functional/cli_root/zfs_snapshot]
tests = ['zfs_snapshot_001_neg', 'zfs_snapshot_002_neg',
'zfs_snapshot_003_neg', 'zfs_snapshot_004_neg', 'zfs_snapshot_005_neg',
'zfs_snapshot_006_pos', 'zfs_snapshot_007_neg', 'zfs_snapshot_009_pos']
# DISABLED:
# zfs_unmount_005_pos - needs investigation
# zfs_unmount_009_pos - needs investigation
# zfs_unmount_all_001_pos - needs investigation
[tests/functional/cli_root/zfs_unmount]
tests = ['zfs_unmount_001_pos', 'zfs_unmount_002_pos', 'zfs_unmount_003_pos',
'zfs_unmount_004_pos', 'zfs_unmount_006_pos',
'zfs_unmount_007_neg', 'zfs_unmount_008_neg']
# DISABLED: Tests need to be updated for Linux unshare behavior
#[tests/functional/cli_root/zfs_unshare]
#tests = ['zfs_unshare_001_pos', 'zfs_unshare_002_pos', 'zfs_unshare_003_pos',
# 'zfs_unshare_004_neg', 'zfs_unshare_005_neg']
[tests/functional/cli_root/zfs_upgrade]
tests = ['zfs_upgrade_001_pos', 'zfs_upgrade_002_pos', 'zfs_upgrade_003_pos',
'zfs_upgrade_004_pos', 'zfs_upgrade_005_pos', 'zfs_upgrade_006_neg',
'zfs_upgrade_007_neg']
[tests/functional/cli_root/zpool]
tests = ['zpool_001_neg', 'zpool_002_pos', 'zpool_003_pos']
# DISABLED:
# zpool_add_005_pos - no 'dumpadm' command.
# zpool_add_006_pos - nested pools
[tests/functional/cli_root/zpool_add]
tests = ['zpool_add_001_pos', 'zpool_add_002_pos', 'zpool_add_003_pos',
'zpool_add_004_pos',
'zpool_add_007_neg', 'zpool_add_008_neg', 'zpool_add_009_neg']
[tests/functional/cli_root/zpool_attach]
tests = ['zpool_attach_001_neg']
[tests/functional/cli_root/zpool_clear]
tests = ['zpool_clear_001_pos', 'zpool_clear_002_neg', 'zpool_clear_003_neg']
# DISABLED:
# zpool_create_001_pos - needs investigation
# zpool_create_002_pos - needs investigation
# zpool_create_004_pos - needs investigation
# zpool_create_006_pos - nested pools
# zpool_create_008_pos - uses VTOC labels (?) and 'overlapping slices'
# zpool_create_011_neg - tries to access /etc/vfstab etc
# zpool_create_012_neg - swap devices
# zpool_create_014_neg - swap devices
# zpool_create_015_neg - swap devices
# zpool_create_016_pos - no dumadm command.
# zpool_create_020_pos - needs investigation
[tests/functional/cli_root/zpool_create]
tests = [
'zpool_create_003_pos', 'zpool_create_005_pos', 'zpool_create_007_neg',
'zpool_create_009_neg', 'zpool_create_010_neg', 'zpool_create_017_neg',
'zpool_create_018_pos', 'zpool_create_019_pos',
'zpool_create_021_pos', 'zpool_create_022_pos', 'zpool_create_023_neg',
'zpool_create_features_001_pos', 'zpool_create_features_002_pos',
'zpool_create_features_003_pos', 'zpool_create_features_004_neg']
# DISABLED:
# zpool_destroy_001_pos - failure should be investigated
# zpool_destroy_002_pos - update for Linux fource unmount behavior
[tests/functional/cli_root/zpool_destroy]
tests = [
'zpool_destroy_003_neg']
pre =
post =
[tests/functional/cli_root/zpool_detach]
tests = ['zpool_detach_001_neg']
# DISABLED: Requires full FMA support in ZED
#[tests/functional/cli_root/zpool_expand]
#tests = ['zpool_expand_001_pos', 'zpool_expand_002_pos',
# 'zpool_expand_003_neg']
# DISABLED:
# zpool_export_004_pos - nested pools
[tests/functional/cli_root/zpool_export]
tests = ['zpool_export_001_pos', 'zpool_export_002_pos',
'zpool_export_003_neg']
[tests/functional/cli_root/zpool_get]
tests = ['zpool_get_001_pos', 'zpool_get_002_pos', 'zpool_get_003_pos',
'zpool_get_004_neg']
[tests/functional/cli_root/zpool_history]
tests = ['zpool_history_001_neg', 'zpool_history_002_pos']
# DISABLED:
# zpool_import_012_pos - sharenfs issue
# zpool_import_all_001_pos - partition issue
# zpool_import_features_001_pos - zhack issue
# zpool_import_features_002_neg - zhack issue
# zpool_import_features_003_pos - zhack issue
# zpool_import_missing_001_pos - zhack_issue
# zpool_import_missing_002_pos - zhack_issue
# zpool_import_missing_003_pos - zhack_issue
# zpool_import_rename_001_pos - hack issue
[tests/functional/cli_root/zpool_import]
tests = ['zpool_import_001_pos', 'zpool_import_002_pos',
'zpool_import_003_pos', 'zpool_import_004_pos', 'zpool_import_005_pos',
'zpool_import_006_pos', 'zpool_import_007_pos', 'zpool_import_008_pos',
'zpool_import_009_neg', 'zpool_import_010_pos', 'zpool_import_011_neg',
'zpool_import_013_neg']
[tests/functional/cli_root/zpool_offline]
tests = ['zpool_offline_001_pos', 'zpool_offline_002_neg']
[tests/functional/cli_root/zpool_online]
tests = ['zpool_online_001_pos', 'zpool_online_002_neg']
# DISABLED:
# zpool_remove_003_pos - needs investigation
[tests/functional/cli_root/zpool_remove]
tests = ['zpool_remove_001_neg', 'zpool_remove_002_pos']
[tests/functional/cli_root/zpool_replace]
tests = ['zpool_replace_001_neg']
# DISABLED:
# zpool_scrub_004_pos - needs investigation
# zpool_scrub_005_pos - needs investigation
[tests/functional/cli_root/zpool_scrub]
tests = ['zpool_scrub_001_neg', 'zpool_scrub_002_pos', 'zpool_scrub_003_pos']
[tests/functional/cli_root/zpool_set]
tests = ['zpool_set_001_pos', 'zpool_set_002_neg', 'zpool_set_003_neg']
pre =
post =
[tests/functional/cli_root/zpool_status]
tests = ['zpool_status_001_pos', 'zpool_status_002_pos']
# DISABLED: ENOSPC failure
#[tests/functional/cli_root/zpool_upgrade]
#tests = ['zpool_upgrade_001_pos', 'zpool_upgrade_002_pos',
# 'zpool_upgrade_003_pos', 'zpool_upgrade_004_pos', 'zpool_upgrade_005_neg',
# 'zpool_upgrade_006_neg', 'zpool_upgrade_007_pos', 'zpool_upgrade_008_pos',
# 'zpool_upgrade_009_neg']
# DISABLED: nested pools
#[tests/functional/cli_user/misc]
#tests = ['zdb_001_neg', 'zfs_001_neg', 'zfs_allow_001_neg',
# 'zfs_clone_001_neg', 'zfs_create_001_neg', 'zfs_destroy_001_neg',
# 'zfs_get_001_neg', 'zfs_inherit_001_neg', 'zfs_mount_001_neg',
# 'zfs_promote_001_neg', 'zfs_receive_001_neg', 'zfs_rename_001_neg',
# 'zfs_rollback_001_neg', 'zfs_send_001_neg', 'zfs_set_001_neg',
# 'zfs_share_001_neg', 'zfs_snapshot_001_neg', 'zfs_unallow_001_neg',
# 'zfs_unmount_001_neg', 'zfs_unshare_001_neg', 'zfs_upgrade_001_neg',
# 'zpool_001_neg', 'zpool_add_001_neg', 'zpool_attach_001_neg',
# 'zpool_clear_001_neg', 'zpool_create_001_neg', 'zpool_destroy_001_neg',
# 'zpool_detach_001_neg', 'zpool_export_001_neg', 'zpool_get_001_neg',
# 'zpool_history_001_neg', 'zpool_import_001_neg', 'zpool_import_002_neg',
# 'zpool_offline_001_neg', 'zpool_online_001_neg', 'zpool_remove_001_neg',
# 'zpool_replace_001_neg', 'zpool_scrub_001_neg', 'zpool_set_001_neg',
# 'zpool_status_001_neg', 'zpool_upgrade_001_neg']
#user = zfs-tests
[tests/functional/cli_user/zfs_list]
tests = ['zfs_list_001_pos', 'zfs_list_002_pos', 'zfs_list_003_pos',
'zfs_list_004_neg', 'zfs_list_007_pos', 'zfs_list_008_neg']
[tests/functional/cli_user/zpool_iostat]
tests = ['zpool_iostat_001_neg', 'zpool_iostat_002_pos',
'zpool_iostat_003_neg']
[tests/functional/cli_user/zpool_list]
tests = ['zpool_list_001_pos', 'zpool_list_002_neg']
[tests/functional/compression]
tests = ['compress_001_pos', 'compress_002_pos', 'compress_003_pos',
'compress_004_pos']
[tests/functional/ctime]
tests = ['ctime_001_pos' ]
# DISABLED: Linux does not yet support delegations.
#[tests/functional/delegate]
#tests = ['zfs_allow_001_pos', 'zfs_allow_002_pos',
# 'zfs_allow_004_pos', 'zfs_allow_005_pos', 'zfs_allow_006_pos',
# 'zfs_allow_007_pos', 'zfs_allow_008_pos', 'zfs_allow_009_neg',
# 'zfs_allow_010_pos', 'zfs_allow_011_neg', 'zfs_allow_012_neg',
# 'zfs_unallow_001_pos', 'zfs_unallow_002_pos', 'zfs_unallow_003_pos',
# 'zfs_unallow_004_pos', 'zfs_unallow_005_pos', 'zfs_unallow_006_pos',
# 'zfs_unallow_007_neg', 'zfs_unallow_008_neg']
# DISABLED:
# devices_001_pos - needs investigation
# devices_002_neg - needs investigation
[tests/functional/devices]
tests = ['devices_003_pos']
# DISABLED:
# exec_002_neg - needs investigation
[tests/functional/exec]
tests = ['exec_001_pos']
[tests/functional/features/async_destroy]
tests = ['async_destroy_001_pos']
# DISABLED: needs investigation
#[tests/functional/grow_pool]
#tests = ['grow_pool_001_pos']
#pre =
#post =
# DISABLED: needs investigation
#[tests/functional/grow_replicas]
#tests = ['grow_replicas_001_pos']
#pre =
#post =
# DISABLED:
# history_001_pos - export commands missing from history
# history_003_pos - nested pool
# history_006_neg - needs investigation
# history_007_pos - needs investigation
# history_008_pos - needs investigation
# history_010_pos - needs investigation
[tests/functional/history]
tests = ['history_002_pos', 'history_004_pos', 'history_005_neg',
'history_009_pos']
[tests/functional/inheritance]
tests = ['inherit_001_pos']
pre =
# DISABLED:
# inuse_001_pos, inuse_007_pos - no dumpadm command
# inuse_005_pos - partition issue
# inuse_006_pos - partition issue
# inuse_008_pos - partition issue
# inuse_009_pos - partition issue
[tests/functional/inuse]
tests = ['inuse_004_pos']
post =
# DISABLED: needs investigation
#[tests/functional/large_files]
#tests = ['large_files_001_pos']
# DISABLED: needs investigation
#[tests/functional/largest_pool]
#tests = ['largest_pool_001_pos']
#pre =
#post =
# DISABLED: needs investigation
#[tests/functional/link_count]
#tests = ['link_count_001']
[tests/functional/migration]
tests = ['migration_001_pos', 'migration_002_pos', 'migration_003_pos',
'migration_004_pos', 'migration_005_pos', 'migration_006_pos',
'migration_007_pos', 'migration_008_pos', 'migration_009_pos',
'migration_010_pos', 'migration_011_pos', 'migration_012_pos']
# DISABLED:
# mmap_write_001_pos: needs investigation
[tests/functional/mmap]
tests = ['mmap_read_001_pos']
# DISABLED:
# umountall_001 - Doesn't make sence in Linux - no umountall command.
[tests/functional/mount]
tests = ['umount_001']
[tests/functional/mv_files]
tests = ['mv_files_001_pos', 'mv_files_002_pos']
[tests/functional/nestedfs]
tests = ['nestedfs_001_pos']
[tests/functional/no_space]
tests = ['enospc_001_pos']
# DISABLED: needs investigation (CentOS 7 only)
#[tests/functional/nopwrite]
#tests = ['nopwrite_copies', 'nopwrite_mtime', 'nopwrite_negative',
# 'nopwrite_promoted_clone', 'nopwrite_recsize', 'nopwrite_sync',
# 'nopwrite_volume', 'nopwrite_varying_compression']
# DISABLED: needs investigation
#[tests/functional/online_offline]
#tests = ['online_offline_001_pos', 'online_offline_002_neg',
# 'online_offline_003_neg']
[tests/functional/pool_names]
tests = ['pool_names_001_pos', 'pool_names_002_neg']
pre =
post =
[tests/functional/poolversion]
tests = ['poolversion_001_pos', 'poolversion_002_pos']
# DISABLED: Doesn't make sense on Linux - no pfexec command or 'RBAC profile'
#[tests/functional/privilege]
#tests = ['privilege_001_pos', 'privilege_002_pos']
# DISABLED:
# quota_002_pos - size is less than current used or reserved space
# quota_004_pos - size is less than current used or reserved space
# quota_005_pos - size is less than current used or reserved space
[tests/functional/quota]
tests = ['quota_001_pos', 'quota_003_pos', 'quota_006_neg']
[tests/functional/redundancy]
tests = ['redundancy_001_pos', 'redundancy_002_pos', 'redundancy_003_pos']
# DISABLED:
# refquota_002_pos - size is less than current used or reserved space
# refquota_004_pos - needs investigation
[tests/functional/refquota]
tests = ['refquota_001_pos', 'refquota_003_pos',
'refquota_005_pos', 'refquota_006_neg']
# DISABLED:
# refreserv_004_pos - needs investigation
[tests/functional/refreserv]
tests = ['refreserv_001_pos', 'refreserv_002_pos', 'refreserv_003_pos',
'refreserv_005_pos']
# DISABLED: nested pool
#[tests/functional/rename_dirs]
#tests = ['rename_dirs_001_pos']
# DISABLED: nested pool
#[tests/functional/replacement]
#tests = ['replacement_001_pos', 'replacement_002_pos']
# DISABLED:
# reservation_012_pos - needs investigation
# reservation_015_pos - needs investigation
# reservation_016_pos - needs investigation
[tests/functional/reservation]
tests = ['reservation_001_pos', 'reservation_002_pos', 'reservation_003_pos',
'reservation_004_pos', 'reservation_005_pos', 'reservation_006_pos',
'reservation_007_pos', 'reservation_008_pos', 'reservation_009_pos',
'reservation_010_pos', 'reservation_011_pos',
'reservation_013_pos', 'reservation_014_pos',
'reservation_017_pos', 'reservation_018_pos']
# DISABLED: Root pools must be handled differently under Linux
#[tests/functional/rootpool]
#tests = ['rootpool_002_neg', 'rootpool_003_neg', 'rootpool_007_neg']
# DISABLED: Hangs on I/O for unclear reason.
#[tests/functional/rsend]
#tests = ['rsend_002_pos', 'rsend_003_pos', 'rsend_004_pos',
# 'rsend_005_pos', 'rsend_006_pos', 'rsend_007_pos', 'rsend_008_pos',
# 'rsend_009_pos', 'rsend_010_pos', 'rsend_011_pos', 'rsend_012_pos',
# 'rsend_013_pos']
[tests/functional/scrub_mirror]
tests = ['scrub_mirror_001_pos', 'scrub_mirror_002_pos',
'scrub_mirror_003_pos', 'scrub_mirror_004_pos']
# DISABLED: Scripts need to be updated.
# slog_012_neg - needs investigation
# slog_013_pos - Linux doesn't have a 'lofiadm' command.
# slog_014_pos - needs investigation
[tests/functional/slog]
tests = ['slog_001_pos', 'slog_002_pos', 'slog_003_pos', 'slog_004_pos',
'slog_005_pos', 'slog_006_pos', 'slog_007_pos', 'slog_008_neg',
'slog_009_neg', 'slog_010_neg', 'slog_011_neg']
# DISABLED:
# rollback_003_pos - Hangs in unmount and spins.
# snapshot_013_pos - Hangs on I/O for unclear reason.
# snapshot_016_pos - .zfs mv/rmdir/mkdir disabled by default.
#[tests/functional/snapshot]
#tests = ['clone_001_pos', 'rollback_001_pos', 'rollback_002_pos',
# 'snapshot_001_pos', 'snapshot_002_pos',
# 'snapshot_003_pos', 'snapshot_004_pos', 'snapshot_005_pos',
# 'snapshot_006_pos', 'snapshot_007_pos', 'snapshot_008_pos',
# 'snapshot_009_pos', 'snapshot_010_pos', 'snapshot_011_pos',
# 'snapshot_012_pos', 'snapshot_014_pos',
# 'snapshot_015_pos', 'snapshot_017_pos']
[tests/functional/snapused]
tests = ['snapused_001_pos', 'snapused_002_pos', 'snapused_003_pos',
'snapused_004_pos', 'snapused_005_pos']
[tests/functional/sparse]
tests = ['sparse_001_pos']
# DISABLED: needs investigation
#[tests/functional/threadsappend]
#tests = ['threadsappend_001_pos']
[tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos']
# DISABLED:
# groupspace_001_pos
# groupspace_002_pos
# userquota_001_pos
# userquota_004_pos
# userquota_007_pos
# userquota_010_pos
# userspace_001_pos
# userspace_002_pos
[tests/functional/userquota]
tests = [
'userquota_002_pos', 'userquota_003_pos',
'userquota_005_neg', 'userquota_006_pos',
'userquota_008_pos', 'userquota_009_pos',
'userquota_011_pos', 'userquota_012_neg']
# DISABLED:
# write_dirs_002_pos - needs investigation
[tests/functional/write_dirs]
tests = ['write_dirs_001_pos']
# DISABLED: No 'runat' command, replace the Linux equivilant and add xattrtest
#[tests/functional/xattr]
#tests = ['xattr_001_pos', 'xattr_002_neg', 'xattr_003_neg', 'xattr_004_pos',
# 'xattr_005_pos', 'xattr_006_pos', 'xattr_007_neg', 'xattr_008_pos',
# 'xattr_009_neg', 'xattr_010_neg', 'xattr_011_pos', 'xattr_012_pos',
# 'xattr_013_pos']
[tests/functional/zvol/zvol_ENOSPC]
tests = ['zvol_ENOSPC_001_pos']
[tests/functional/zvol/zvol_cli]
tests = ['zvol_cli_001_pos', 'zvol_cli_002_pos', 'zvol_cli_003_neg']
# DISABLED: requires dumpadm
#[tests/functional/zvol/zvol_misc]
#tests = ['zvol_misc_001_neg', 'zvol_misc_002_pos', 'zvol_misc_003_neg',
# 'zvol_misc_004_pos', 'zvol_misc_005_neg', 'zvol_misc_006_pos']
# DISABLED: requires updated for Linux
#[tests/functional/zvol/zvol_swap]
#tests = ['zvol_swap_001_pos', 'zvol_swap_002_pos', 'zvol_swap_003_pos',
# 'zvol_swap_004_pos', 'zvol_swap_005_pos', 'zvol_swap_006_pos']

View File

@ -0,0 +1 @@
SUBDIRS = cmd include man

View File

@ -0,0 +1,3 @@
pkgdatadir = $(datadir)/@PACKAGE@/test-runner/bin
dist_pkgdata_SCRIPTS = \
test-runner.py

View File

@ -0,0 +1,862 @@
#!/usr/bin/python
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 by Delphix. All rights reserved.
#
import ConfigParser
import os
import logging
from datetime import datetime
from optparse import OptionParser
from pwd import getpwnam
from pwd import getpwuid
from select import select
from subprocess import PIPE
from subprocess import Popen
from sys import argv
from sys import exit
from threading import Timer
from time import time
BASEDIR = '/var/tmp/test_results'
TESTDIR = '/usr/share/zfs/'
KILL = 'kill'
TRUE = 'true'
SUDO = 'sudo'
class Result(object):
total = 0
runresults = {'PASS': 0, 'FAIL': 0, 'SKIP': 0, 'KILLED': 0}
def __init__(self):
self.starttime = None
self.returncode = None
self.runtime = ''
self.stdout = []
self.stderr = []
self.result = ''
def done(self, proc, killed):
"""
Finalize the results of this Cmd.
"""
Result.total += 1
m, s = divmod(time() - self.starttime, 60)
self.runtime = '%02d:%02d' % (m, s)
self.returncode = proc.returncode
if killed:
self.result = 'KILLED'
Result.runresults['KILLED'] += 1
elif self.returncode is 0:
self.result = 'PASS'
Result.runresults['PASS'] += 1
elif self.returncode is 4:
self.result = 'SKIP'
Result.runresults['SKIP'] += 1
elif self.returncode is not 0:
self.result = 'FAIL'
Result.runresults['FAIL'] += 1
class Output(object):
"""
This class is a slightly modified version of the 'Stream' class found
here: http://goo.gl/aSGfv
"""
def __init__(self, stream):
self.stream = stream
self._buf = ''
self.lines = []
def fileno(self):
return self.stream.fileno()
def read(self, drain=0):
"""
Read from the file descriptor. If 'drain' set, read until EOF.
"""
while self._read() is not None:
if not drain:
break
def _read(self):
"""
Read up to 4k of data from this output stream. Collect the output
up to the last newline, and append it to any leftover data from a
previous call. The lines are stored as a (timestamp, data) tuple
for easy sorting/merging later.
"""
fd = self.fileno()
buf = os.read(fd, 4096)
if not buf:
return None
if '\n' not in buf:
self._buf += buf
return []
buf = self._buf + buf
tmp, rest = buf.rsplit('\n', 1)
self._buf = rest
now = datetime.now()
rows = tmp.split('\n')
self.lines += [(now, r) for r in rows]
class Cmd(object):
verified_users = []
def __init__(self, pathname, outputdir=None, timeout=None, user=None):
self.pathname = pathname
self.outputdir = outputdir or 'BASEDIR'
self.timeout = timeout or 60
self.user = user or ''
self.killed = False
self.result = Result()
def __str__(self):
return "Pathname: %s\nOutputdir: %s\nTimeout: %s\nUser: %s\n" % (
self.pathname, self.outputdir, self.timeout, self.user)
def kill_cmd(self, proc):
"""
Kill a running command due to timeout, or ^C from the keyboard. If
sudo is required, this user was verified previously.
"""
self.killed = True
do_sudo = len(self.user) != 0
signal = '-TERM'
cmd = [SUDO, KILL, signal, str(proc.pid)]
if not do_sudo:
del cmd[0]
try:
kp = Popen(cmd)
kp.wait()
except:
pass
def update_cmd_privs(self, cmd, user):
"""
If a user has been specified to run this Cmd and we're not already
running as that user, prepend the appropriate sudo command to run
as that user.
"""
me = getpwuid(os.getuid())
if not user or user is me:
return cmd
if not os.path.isfile(cmd):
if os.path.isfile(cmd+'.ksh') and os.access(cmd+'.ksh', os.X_OK):
cmd += '.ksh'
if os.path.isfile(cmd+'.sh') and os.access(cmd+'.sh', os.X_OK):
cmd += '.sh'
ret = '%s -E -u %s %s' % (SUDO, user, cmd)
return ret.split(' ')
def collect_output(self, proc):
"""
Read from stdout/stderr as data becomes available, until the
process is no longer running. Return the lines from the stdout and
stderr Output objects.
"""
out = Output(proc.stdout)
err = Output(proc.stderr)
res = []
while proc.returncode is None:
proc.poll()
res = select([out, err], [], [], .1)
for fd in res[0]:
fd.read()
for fd in res[0]:
fd.read(drain=1)
return out.lines, err.lines
def run(self, options):
"""
This is the main function that runs each individual test.
Determine whether or not the command requires sudo, and modify it
if needed. Run the command, and update the result object.
"""
if options.dryrun is True:
print self
return
privcmd = self.update_cmd_privs(self.pathname, self.user)
try:
old = os.umask(0)
if not os.path.isdir(self.outputdir):
os.makedirs(self.outputdir, mode=0777)
os.umask(old)
except OSError, e:
fail('%s' % e)
try:
self.result.starttime = time()
proc = Popen(privcmd, stdout=PIPE, stderr=PIPE)
t = Timer(int(self.timeout), self.kill_cmd, [proc])
t.start()
self.result.stdout, self.result.stderr = self.collect_output(proc)
except KeyboardInterrupt:
self.kill_cmd(proc)
fail('\nRun terminated at user request.')
finally:
t.cancel()
self.result.done(proc, self.killed)
def skip(self):
"""
Initialize enough of the test result that we can log a skipped
command.
"""
Result.total += 1
Result.runresults['SKIP'] += 1
self.result.stdout = self.result.stderr = []
self.result.starttime = time()
m, s = divmod(time() - self.result.starttime, 60)
self.result.runtime = '%02d:%02d' % (m, s)
self.result.result = 'SKIP'
def log(self, logger, options):
"""
This function is responsible for writing all output. This includes
the console output, the logfile of all results (with timestamped
merged stdout and stderr), and for each test, the unmodified
stdout/stderr/merged in it's own file.
"""
if logger is None:
return
logname = getpwuid(os.getuid()).pw_name
user = ' (run as %s)' % (self.user if len(self.user) else logname)
msga = 'Test: %s%s ' % (self.pathname, user)
msgb = '[%s] [%s]' % (self.result.runtime, self.result.result)
pad = ' ' * (80 - (len(msga) + len(msgb)))
# If -q is specified, only print a line for tests that didn't pass.
# This means passing tests need to be logged as DEBUG, or the one
# line summary will only be printed in the logfile for failures.
if not options.quiet:
logger.info('%s%s%s' % (msga, pad, msgb))
elif self.result.result is not 'PASS':
logger.info('%s%s%s' % (msga, pad, msgb))
else:
logger.debug('%s%s%s' % (msga, pad, msgb))
lines = self.result.stdout + self.result.stderr
for dt, line in sorted(lines):
logger.debug('%s %s' % (dt.strftime("%H:%M:%S.%f ")[:11], line))
if len(self.result.stdout):
with open(os.path.join(self.outputdir, 'stdout'), 'w') as out:
for _, line in self.result.stdout:
os.write(out.fileno(), '%s\n' % line)
if len(self.result.stderr):
with open(os.path.join(self.outputdir, 'stderr'), 'w') as err:
for _, line in self.result.stderr:
os.write(err.fileno(), '%s\n' % line)
if len(self.result.stdout) and len(self.result.stderr):
with open(os.path.join(self.outputdir, 'merged'), 'w') as merged:
for _, line in sorted(lines):
os.write(merged.fileno(), '%s\n' % line)
class Test(Cmd):
props = ['outputdir', 'timeout', 'user', 'pre', 'pre_user', 'post',
'post_user']
def __init__(self, pathname, outputdir=None, timeout=None, user=None,
pre=None, pre_user=None, post=None, post_user=None):
super(Test, self).__init__(pathname, outputdir, timeout, user)
self.pre = pre or ''
self.pre_user = pre_user or ''
self.post = post or ''
self.post_user = post_user or ''
def __str__(self):
post_user = pre_user = ''
if len(self.pre_user):
pre_user = ' (as %s)' % (self.pre_user)
if len(self.post_user):
post_user = ' (as %s)' % (self.post_user)
return "Pathname: %s\nOutputdir: %s\nTimeout: %s\nPre: %s%s\nPost: " \
"%s%s\nUser: %s\n" % (self.pathname, self.outputdir,
self.timeout, self.pre, pre_user, self.post, post_user,
self.user)
def verify(self, logger):
"""
Check the pre/post scripts, user and Test. Omit the Test from this
run if there are any problems.
"""
files = [self.pre, self.pathname, self.post]
users = [self.pre_user, self.user, self.post_user]
for f in [f for f in files if len(f)]:
if not verify_file(f):
logger.info("Warning: Test '%s' not added to this run because"
" it failed verification." % f)
return False
for user in [user for user in users if len(user)]:
if not verify_user(user, logger):
logger.info("Not adding Test '%s' to this run." %
self.pathname)
return False
return True
def run(self, logger, options):
"""
Create Cmd instances for the pre/post scripts. If the pre script
doesn't pass, skip this Test. Run the post script regardless.
"""
pretest = Cmd(self.pre, outputdir=os.path.join(self.outputdir,
os.path.basename(self.pre)), timeout=self.timeout,
user=self.pre_user)
test = Cmd(self.pathname, outputdir=self.outputdir,
timeout=self.timeout, user=self.user)
posttest = Cmd(self.post, outputdir=os.path.join(self.outputdir,
os.path.basename(self.post)), timeout=self.timeout,
user=self.post_user)
cont = True
if len(pretest.pathname):
pretest.run(options)
cont = pretest.result.result is 'PASS'
pretest.log(logger, options)
if cont:
test.run(options)
else:
test.skip()
test.log(logger, options)
if len(posttest.pathname):
posttest.run(options)
posttest.log(logger, options)
class TestGroup(Test):
props = Test.props + ['tests']
def __init__(self, pathname, outputdir=None, timeout=None, user=None,
pre=None, pre_user=None, post=None, post_user=None,
tests=None):
super(TestGroup, self).__init__(pathname, outputdir, timeout, user,
pre, pre_user, post, post_user)
self.tests = tests or []
def __str__(self):
post_user = pre_user = ''
if len(self.pre_user):
pre_user = ' (as %s)' % (self.pre_user)
if len(self.post_user):
post_user = ' (as %s)' % (self.post_user)
return "Pathname: %s\nOutputdir: %s\nTests: %s\nTimeout: %s\n" \
"Pre: %s%s\nPost: %s%s\nUser: %s\n" % (self.pathname,
self.outputdir, self.tests, self.timeout, self.pre, pre_user,
self.post, post_user, self.user)
def verify(self, logger):
"""
Check the pre/post scripts, user and tests in this TestGroup. Omit
the TestGroup entirely, or simply delete the relevant tests in the
group, if that's all that's required.
"""
# If the pre or post scripts are relative pathnames, convert to
# absolute, so they stand a chance of passing verification.
if len(self.pre) and not os.path.isabs(self.pre):
self.pre = os.path.join(self.pathname, self.pre)
if len(self.post) and not os.path.isabs(self.post):
self.post = os.path.join(self.pathname, self.post)
auxfiles = [self.pre, self.post]
users = [self.pre_user, self.user, self.post_user]
for f in [f for f in auxfiles if len(f)]:
if self.pathname != os.path.dirname(f):
logger.info("Warning: TestGroup '%s' not added to this run. "
"Auxiliary script '%s' exists in a different "
"directory." % (self.pathname, f))
return False
if not verify_file(f):
logger.info("Warning: TestGroup '%s' not added to this run. "
"Auxiliary script '%s' failed verification." %
(self.pathname, f))
return False
for user in [user for user in users if len(user)]:
if not verify_user(user, logger):
logger.info("Not adding TestGroup '%s' to this run." %
self.pathname)
return False
# If one of the tests is invalid, delete it, log it, and drive on.
for test in self.tests:
if not verify_file(os.path.join(self.pathname, test)):
del self.tests[self.tests.index(test)]
logger.info("Warning: Test '%s' removed from TestGroup '%s' "
"because it failed verification." % (test,
self.pathname))
return len(self.tests) is not 0
def run(self, logger, options):
"""
Create Cmd instances for the pre/post scripts. If the pre script
doesn't pass, skip all the tests in this TestGroup. Run the post
script regardless.
"""
pretest = Cmd(self.pre, outputdir=os.path.join(self.outputdir,
os.path.basename(self.pre)), timeout=self.timeout,
user=self.pre_user)
posttest = Cmd(self.post, outputdir=os.path.join(self.outputdir,
os.path.basename(self.post)), timeout=self.timeout,
user=self.post_user)
cont = True
if len(pretest.pathname):
pretest.run(options)
cont = pretest.result.result is 'PASS'
pretest.log(logger, options)
for fname in self.tests:
test = Cmd(os.path.join(self.pathname, fname),
outputdir=os.path.join(self.outputdir, fname),
timeout=self.timeout, user=self.user)
if cont:
test.run(options)
else:
test.skip()
test.log(logger, options)
if len(posttest.pathname):
posttest.run(options)
posttest.log(logger, options)
class TestRun(object):
props = ['quiet', 'outputdir']
def __init__(self, options):
self.tests = {}
self.testgroups = {}
self.starttime = time()
self.timestamp = datetime.now().strftime('%Y%m%dT%H%M%S')
self.outputdir = os.path.join(options.outputdir, self.timestamp)
self.logger = self.setup_logging(options)
self.defaults = [
('outputdir', BASEDIR),
('quiet', False),
('timeout', 60),
('user', ''),
('pre', ''),
('pre_user', ''),
('post', ''),
('post_user', '')
]
def __str__(self):
s = 'TestRun:\n outputdir: %s\n' % self.outputdir
s += 'TESTS:\n'
for key in sorted(self.tests.keys()):
s += '%s%s' % (self.tests[key].__str__(), '\n')
s += 'TESTGROUPS:\n'
for key in sorted(self.testgroups.keys()):
s += '%s%s' % (self.testgroups[key].__str__(), '\n')
return s
def addtest(self, pathname, options):
"""
Create a new Test, and apply any properties that were passed in
from the command line. If it passes verification, add it to the
TestRun.
"""
test = Test(pathname)
for prop in Test.props:
setattr(test, prop, getattr(options, prop))
if test.verify(self.logger):
self.tests[pathname] = test
def addtestgroup(self, dirname, filenames, options):
"""
Create a new TestGroup, and apply any properties that were passed
in from the command line. If it passes verification, add it to the
TestRun.
"""
if dirname not in self.testgroups:
testgroup = TestGroup(dirname)
for prop in Test.props:
setattr(testgroup, prop, getattr(options, prop))
# Prevent pre/post scripts from running as regular tests
for f in [testgroup.pre, testgroup.post]:
if f in filenames:
del filenames[filenames.index(f)]
self.testgroups[dirname] = testgroup
self.testgroups[dirname].tests = sorted(filenames)
testgroup.verify(self.logger)
def read(self, logger, options):
"""
Read in the specified runfile, and apply the TestRun properties
listed in the 'DEFAULT' section to our TestRun. Then read each
section, and apply the appropriate properties to the Test or
TestGroup. Properties from individual sections override those set
in the 'DEFAULT' section. If the Test or TestGroup passes
verification, add it to the TestRun.
"""
config = ConfigParser.RawConfigParser()
if not len(config.read(options.runfile)):
fail("Coulnd't read config file %s" % options.runfile)
for opt in TestRun.props:
if config.has_option('DEFAULT', opt):
setattr(self, opt, config.get('DEFAULT', opt))
self.outputdir = os.path.join(self.outputdir, self.timestamp)
for section in config.sections():
if 'tests' in config.options(section):
if os.path.isdir(section):
pathname = section
elif os.path.isdir(os.path.join(options.testdir, section)):
pathname = os.path.join(options.testdir, section)
else:
pathname = section
testgroup = TestGroup(os.path.abspath(pathname))
for prop in TestGroup.props:
try:
setattr(testgroup, prop, config.get('DEFAULT', prop))
setattr(testgroup, prop, config.get(section, prop))
except ConfigParser.NoOptionError:
pass
# Repopulate tests using eval to convert the string to a list
testgroup.tests = eval(config.get(section, 'tests'))
if testgroup.verify(logger):
self.testgroups[section] = testgroup
else:
test = Test(section)
for prop in Test.props:
try:
setattr(test, prop, config.get('DEFAULT', prop))
setattr(test, prop, config.get(section, prop))
except ConfigParser.NoOptionError:
pass
if test.verify(logger):
self.tests[section] = test
def write(self, options):
"""
Create a configuration file for editing and later use. The
'DEFAULT' section of the config file is created from the
properties that were specified on the command line. Tests are
simply added as sections that inherit everything from the
'DEFAULT' section. TestGroups are the same, except they get an
option including all the tests to run in that directory.
"""
defaults = dict([(prop, getattr(options, prop)) for prop, _ in
self.defaults])
config = ConfigParser.RawConfigParser(defaults)
for test in sorted(self.tests.keys()):
config.add_section(test)
for testgroup in sorted(self.testgroups.keys()):
config.add_section(testgroup)
config.set(testgroup, 'tests', self.testgroups[testgroup].tests)
try:
with open(options.template, 'w') as f:
return config.write(f)
except IOError:
fail('Could not open \'%s\' for writing.' % options.template)
def complete_outputdirs(self, options):
"""
Collect all the pathnames for Tests, and TestGroups. Work
backwards one pathname component at a time, to create a unique
directory name in which to deposit test output. Tests will be able
to write output files directly in the newly modified outputdir.
TestGroups will be able to create one subdirectory per test in the
outputdir, and are guaranteed uniqueness because a group can only
contain files in one directory. Pre and post tests will create a
directory rooted at the outputdir of the Test or TestGroup in
question for their output.
"""
done = False
components = 0
tmp_dict = dict(self.tests.items() + self.testgroups.items())
total = len(tmp_dict)
base = self.outputdir
while not done:
l = []
components -= 1
for testfile in tmp_dict.keys():
uniq = '/'.join(testfile.split('/')[components:]).lstrip('/')
if not uniq in l:
l.append(uniq)
tmp_dict[testfile].outputdir = os.path.join(base, uniq)
else:
break
done = total == len(l)
def setup_logging(self, options):
"""
Two loggers are set up here. The first is for the logfile which
will contain one line summarizing the test, including the test
name, result, and running time. This logger will also capture the
timestamped combined stdout and stderr of each run. The second
logger is optional console output, which will contain only the one
line summary. The loggers are initialized at two different levels
to facilitate segregating the output.
"""
if options.dryrun is True:
return
testlogger = logging.getLogger(__name__)
testlogger.setLevel(logging.DEBUG)
if options.cmd is not 'wrconfig':
try:
old = os.umask(0)
os.makedirs(self.outputdir, mode=0777)
os.umask(old)
except OSError, e:
fail('%s' % e)
filename = os.path.join(self.outputdir, 'log')
logfile = logging.FileHandler(filename)
logfile.setLevel(logging.DEBUG)
logfilefmt = logging.Formatter('%(message)s')
logfile.setFormatter(logfilefmt)
testlogger.addHandler(logfile)
cons = logging.StreamHandler()
cons.setLevel(logging.INFO)
consfmt = logging.Formatter('%(message)s')
cons.setFormatter(consfmt)
testlogger.addHandler(cons)
return testlogger
def run(self, options):
"""
Walk through all the Tests and TestGroups, calling run().
"""
try:
os.chdir(self.outputdir)
except OSError:
fail('Could not change to directory %s' % self.outputdir)
for test in sorted(self.tests.keys()):
self.tests[test].run(self.logger, options)
for testgroup in sorted(self.testgroups.keys()):
self.testgroups[testgroup].run(self.logger, options)
def summary(self):
if Result.total is 0:
return
print '\nResults Summary'
for key in Result.runresults.keys():
if Result.runresults[key] is not 0:
print '%s\t% 4d' % (key, Result.runresults[key])
m, s = divmod(time() - self.starttime, 60)
h, m = divmod(m, 60)
print '\nRunning Time:\t%02d:%02d:%02d' % (h, m, s)
print 'Percent passed:\t%.1f%%' % ((float(Result.runresults['PASS']) /
float(Result.total)) * 100)
print 'Log directory:\t%s' % self.outputdir
def verify_file(pathname):
"""
Verify that the supplied pathname is an executable regular file.
"""
if os.path.isdir(pathname) or os.path.islink(pathname):
return False
if (os.path.isfile(pathname) and os.access(pathname, os.X_OK)) or \
(os.path.isfile(pathname+'.ksh') and os.access(pathname+'.ksh', os.X_OK)) or \
(os.path.isfile(pathname+'.sh') and os.access(pathname+'.sh', os.X_OK)):
return True
return False
def verify_user(user, logger):
"""
Verify that the specified user exists on this system, and can execute
sudo without being prompted for a password.
"""
testcmd = [SUDO, '-n', '-u', user, TRUE]
can_sudo = exists = True
if user in Cmd.verified_users:
return True
try:
_ = getpwnam(user)
except KeyError:
exists = False
logger.info("Warning: user '%s' does not exist.", user)
return False
p = Popen(testcmd)
p.wait()
if p.returncode is not 0:
logger.info("Warning: user '%s' cannot use passwordless sudo.", user)
return False
else:
Cmd.verified_users.append(user)
return True
def find_tests(testrun, options):
"""
For the given list of pathnames, add files as Tests. For directories,
if do_groups is True, add the directory as a TestGroup. If False,
recursively search for executable files.
"""
for p in sorted(options.pathnames):
if os.path.isdir(p):
for dirname, _, filenames in os.walk(p):
if options.do_groups:
testrun.addtestgroup(dirname, filenames, options)
else:
for f in sorted(filenames):
testrun.addtest(os.path.join(dirname, f), options)
else:
testrun.addtest(p, options)
def fail(retstr, ret=1):
print '%s: %s' % (argv[0], retstr)
exit(ret)
def options_cb(option, opt_str, value, parser):
path_options = ['runfile', 'outputdir', 'template', 'testdir']
if option.dest is 'runfile' and '-w' in parser.rargs or \
option.dest is 'template' and '-c' in parser.rargs:
fail('-c and -w are mutually exclusive.')
if opt_str in parser.rargs:
fail('%s may only be specified once.' % opt_str)
if option.dest is 'runfile':
parser.values.cmd = 'rdconfig'
if option.dest is 'template':
parser.values.cmd = 'wrconfig'
setattr(parser.values, option.dest, value)
if option.dest in path_options:
setattr(parser.values, option.dest, os.path.abspath(value))
def parse_args():
parser = OptionParser()
parser.add_option('-c', action='callback', callback=options_cb,
type='string', dest='runfile', metavar='runfile',
help='Specify tests to run via config file.')
parser.add_option('-d', action='store_true', default=False, dest='dryrun',
help='Dry run. Print tests, but take no other action.')
parser.add_option('-g', action='store_true', default=False,
dest='do_groups', help='Make directories TestGroups.')
parser.add_option('-o', action='callback', callback=options_cb,
default=BASEDIR, dest='outputdir', type='string',
metavar='outputdir', help='Specify an output directory.')
parser.add_option('-i', action='callback', callback=options_cb,
default=TESTDIR, dest='testdir', type='string',
metavar='testdir', help='Specify a test directory.')
parser.add_option('-p', action='callback', callback=options_cb,
default='', dest='pre', metavar='script',
type='string', help='Specify a pre script.')
parser.add_option('-P', action='callback', callback=options_cb,
default='', dest='post', metavar='script',
type='string', help='Specify a post script.')
parser.add_option('-q', action='store_true', default=False, dest='quiet',
help='Silence on the console during a test run.')
parser.add_option('-t', action='callback', callback=options_cb, default=60,
dest='timeout', metavar='seconds', type='int',
help='Timeout (in seconds) for an individual test.')
parser.add_option('-u', action='callback', callback=options_cb,
default='', dest='user', metavar='user', type='string',
help='Specify a different user name to run as.')
parser.add_option('-w', action='callback', callback=options_cb,
default=None, dest='template', metavar='template',
type='string', help='Create a new config file.')
parser.add_option('-x', action='callback', callback=options_cb, default='',
dest='pre_user', metavar='pre_user', type='string',
help='Specify a user to execute the pre script.')
parser.add_option('-X', action='callback', callback=options_cb, default='',
dest='post_user', metavar='post_user', type='string',
help='Specify a user to execute the post script.')
(options, pathnames) = parser.parse_args()
if not options.runfile and not options.template:
options.cmd = 'runtests'
if options.runfile and len(pathnames):
fail('Extraneous arguments.')
options.pathnames = [os.path.abspath(path) for path in pathnames]
return options
def main(args):
options = parse_args()
testrun = TestRun(options)
if options.cmd is 'runtests':
find_tests(testrun, options)
elif options.cmd is 'rdconfig':
testrun.read(testrun.logger, options)
elif options.cmd is 'wrconfig':
find_tests(testrun, options)
testrun.write(options)
exit(0)
else:
fail('Unknown command specified')
testrun.complete_outputdirs(options)
testrun.run(options)
testrun.summary()
exit(0)
if __name__ == '__main__':
main(argv[1:])

View File

@ -0,0 +1,4 @@
pkgdatadir = $(datadir)/@PACKAGE@/test-runner/include
dist_pkgdata_SCRIPTS = \
logapi.shlib \
stf.shlib

View File

@ -0,0 +1,385 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Copyright (c) 2012 by Delphix. All rights reserved.
#
. ${STF_TOOLS}/include/stf.shlib
# Output an assertion
#
# $@ - assertion text
function log_assert
{
_printline ASSERTION: "$@"
}
# Output a comment
#
# $@ - comment text
function log_note
{
_printline NOTE: "$@"
}
# Execute and print command with status where success equals non-zero result
#
# $@ - command to execute
#
# return 0 if command fails, otherwise return 1
function log_neg
{
log_neg_expect "" "$@"
return $?
}
# Execute a positive test and exit $STF_FAIL is test fails
#
# $@ - command to execute
function log_must
{
log_pos "$@"
(( $? != 0 )) && log_fail
}
# Execute a negative test and exit $STF_FAIL if test passes
#
# $@ - command to execute
function log_mustnot
{
log_neg "$@"
(( $? != 0 )) && log_fail
}
# Execute a negative test with keyword expected, and exit
# $STF_FAIL if test passes
#
# $1 - keyword expected
# $2-$@ - command to execute
function log_mustnot_expect
{
log_neg_expect "$@"
(( $? != 0 )) && log_fail
}
# Execute and print command with status where success equals non-zero result
# or output includes expected keyword
#
# $1 - keyword expected
# $2-$@ - command to execute
#
# return 0 if command fails, or the output contains the keyword expected,
# return 1 otherwise
function log_neg_expect
{
typeset out=""
typeset logfile="/tmp/log.$$"
typeset ret=1
typeset expect=$1
shift
while [[ -e $logfile ]]; do
logfile="$logfile.$$"
done
"$@" 2>$logfile
typeset status=$?
out="$CAT $logfile"
# unexpected status
if (( $status == 0 )); then
print -u2 $($out)
_printerror "$@" "unexpectedly exited $status"
# missing binary
elif (( $status == 127 )); then
print -u2 $($out)
_printerror "$@" "unexpectedly exited $status (File not found)"
# bus error - core dump
elif (( $status == 138 )); then
print -u2 $($out)
_printerror "$@" "unexpectedly exited $status (Bus Error)"
# segmentation violation - core dump
elif (( $status == 139 )); then
print -u2 $($out)
_printerror "$@" "unexpectedly exited $status (SEGV)"
else
$out | $EGREP -i "internal error|assertion failed" \
> /dev/null 2>&1
# internal error or assertion failed
if (( $? == 0 )); then
print -u2 $($out)
_printerror "$@" "internal error or assertion failure" \
" exited $status"
elif [[ -n $expect ]] ; then
$out | $GREP -i "$expect" > /dev/null 2>&1
if (( $? == 0 )); then
ret=0
else
print -u2 $($out)
_printerror "$@" "unexpectedly exited $status"
fi
else
ret=0
fi
if (( $ret == 0 )); then
[[ -n $LOGAPI_DEBUG ]] && print $($out)
_printsuccess "$@" "exited $status"
fi
fi
_recursive_output $logfile "false"
return $ret
}
# Execute and print command with status where success equals zero result
#
# $@ command to execute
#
# return command exit status
function log_pos
{
typeset out=""
typeset logfile="/tmp/log.$$"
while [[ -e $logfile ]]; do
logfile="$logfile.$$"
done
"$@" 2>$logfile
typeset status=$?
out="$CAT $logfile"
if (( $status != 0 )) ; then
print -u2 $($out)
_printerror "$@" "exited $status"
else
$out | $EGREP -i "internal error|assertion failed" \
> /dev/null 2>&1
# internal error or assertion failed
if [[ $? -eq 0 ]]; then
print -u2 $($out)
_printerror "$@" "internal error or assertion failure" \
" exited $status"
status=1
else
[[ -n $LOGAPI_DEBUG ]] && print $($out)
_printsuccess "$@"
fi
fi
_recursive_output $logfile "false"
return $status
}
# Set an exit handler
#
# $@ - function(s) to perform on exit
function log_onexit
{
_CLEANUP="$@"
}
#
# Exit functions
#
# Perform cleanup and exit $STF_PASS
#
# $@ - message text
function log_pass
{
_endlog $STF_PASS "$@"
}
# Perform cleanup and exit $STF_FAIL
#
# $@ - message text
function log_fail
{
_endlog $STF_FAIL "$@"
}
# Perform cleanup and exit $STF_UNRESOLVED
#
# $@ - message text
function log_unresolved
{
_endlog $STF_UNRESOLVED "$@"
}
# Perform cleanup and exit $STF_NOTINUSE
#
# $@ - message text
function log_notinuse
{
_endlog $STF_NOTINUSE "$@"
}
# Perform cleanup and exit $STF_UNSUPPORTED
#
# $@ - message text
function log_unsupported
{
_endlog $STF_UNSUPPORTED "$@"
}
# Perform cleanup and exit $STF_UNTESTED
#
# $@ - message text
function log_untested
{
_endlog $STF_UNTESTED "$@"
}
# Perform cleanup and exit $STF_UNINITIATED
#
# $@ - message text
function log_uninitiated
{
_endlog $STF_UNINITIATED "$@"
}
# Perform cleanup and exit $STF_NORESULT
#
# $@ - message text
function log_noresult
{
_endlog $STF_NORESULT "$@"
}
# Perform cleanup and exit $STF_WARNING
#
# $@ - message text
function log_warning
{
_endlog $STF_WARNING "$@"
}
# Perform cleanup and exit $STF_TIMED_OUT
#
# $@ - message text
function log_timed_out
{
_endlog $STF_TIMED_OUT "$@"
}
# Perform cleanup and exit $STF_OTHER
#
# $@ - message text
function log_other
{
_endlog $STF_OTHER "$@"
}
#
# Internal functions
#
# Perform cleanup and exit
#
# $1 - stf exit code
# $2-$n - message text
function _endlog
{
typeset logfile="/tmp/log.$$"
_recursive_output $logfile
if [[ -n $_CLEANUP ]] ; then
typeset cleanup=$_CLEANUP
log_onexit ""
log_note "Performing local cleanup via log_onexit ($cleanup)"
$cleanup
fi
typeset exitcode=$1
shift
(( ${#@} > 0 )) && _printline "$@"
exit $exitcode
}
# Output a formatted line
#
# $@ - message text
function _printline
{
print "$@"
}
# Output an error message
#
# $@ - message text
function _printerror
{
_printline ERROR: "$@"
}
# Output a success message
#
# $@ - message text
function _printsuccess
{
_printline SUCCESS: "$@"
}
# Output logfiles recursively
#
# $1 - start file
# $2 - indicate whether output the start file itself, default as yes.
function _recursive_output #logfile
{
typeset logfile=$1
while [[ -e $logfile ]]; do
if [[ -z $2 || $logfile != $1 ]]; then
$CAT $logfile
fi
$RM -f $logfile
logfile="$logfile.$$"
done
}

View File

@ -0,0 +1,57 @@
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Copyright (c) 2012 by Delphix. All rights reserved.
#
STF_PASS=0
STF_FAIL=1
STF_UNRESOLVED=2
STF_NOTINUSE=3
STF_UNSUPPORTED=4
STF_UNTESTED=5
STF_UNINITIATED=6
STF_NORESULT=7
STF_WARNING=8
STF_TIMED_OUT=9
STF_OTHER=10
# do this to use the names: eval echo \$STF_RESULT_NAME_${result}
STF_RESULT_NAME_0="PASS"
STF_RESULT_NAME_1="FAIL"
STF_RESULT_NAME_2="UNRESOLVED"
STF_RESULT_NAME_3="NOTINUSE"
STF_RESULT_NAME_4="UNSUPPORTED"
STF_RESULT_NAME_5="UNTESTED"
STF_RESULT_NAME_6="UNINITIATED"
STF_RESULT_NAME_7="NORESULT"
STF_RESULT_NAME_8="WARNING"
STF_RESULT_NAME_9="TIMED_OUT"
STF_RESULT_NAME_10="OTHER"
# do this to use the array: ${STF_RESULT_NAMES[$result]}
STF_RESULT_NAMES=( "PASS" "FAIL" "UNRESOLVED" "NOTINUSE" "UNSUPPORTED" \
"UNTESTED" "UNINITIATED" "NORESULT" "WARNING" "TIMED_OUT" "OTHER" )

View File

@ -0,0 +1,4 @@
dist_man_MANS = test-runner.1
install-data-local:
$(INSTALL) -d -m 0755 "$(DESTDIR)$(mandir)/man1"

View File

@ -0,0 +1,370 @@
.\"
.\" This file and its contents are supplied under the terms of the
.\" Common Development and Distribution License ("CDDL"), version 1.0.
.\" You may only use this file in accordance with the terms of version
.\" 1.0 of the CDDL.
.\"
.\" A full copy of the text of the CDDL should have accompanied this
.\" source. A copy of the CDDL is also available via the Internet at
.\" http://www.illumos.org/license/CDDL.
.\"
.\"
.\" Copyright (c) 2012 by Delphix. All rights reserved.
.\"
.TH run 1 "23 Sep 2012"
.SH NAME
run \- find, execute, and log the results of tests
.SH SYNOPSIS
.LP
.nf
\fBrun\fR [\fB-dgq] [\fB-o\fR \fIoutputdir\fR] [\fB-pP\fR \fIscript\fR] [\fB-t\fR \fIseconds\fR] [\fB-uxX\fR \fIusername\fR]
\fIpathname\fR ...
.fi
.LP
.nf
\fBrun\fR \fB-w\fR \fIrunfile\fR [\fB-gq\fR] [\fB-o\fR \fIoutputdir\fR] [\fB-pP\fR \fIscript\fR] [\fB-t\fR \fIseconds\fR]
[\fB-uxX\fR \fIusername\fR] \fIpathname\fR ...
.fi
.LP
.nf
\fBrun\fR \fB-c\fR \fIrunfile\fR [\fB-dq\fR]
.fi
.LP
.nf
\fBrun\fR [\fB-h\fR]
.fi
.SH DESCRIPTION
.sp
.LP
The \fBrun\fR command has three basic modes of operation. With neither the
\fB-c\fR nor the \fB-w\fR option, \fBrun\fR processes the arguments provided on
the command line, adding them to the list for this run. If a specified
\fIpathname\fR is an executable file, it is added as a test. If a specified
\fIpathname\fR is a directory, the behavior depends upon the \fB-g\fR option.
If \fB-g\fR is specified, the directory is treated as a test group. See the
section on "Test Groups" below. Without the \fB-g\fR option, \fBrun\fR simply
descends into the directory looking for executable files. The tests are then
executed, and the results are logged.
With the \fB-w\fR option, \fBrun\fR finds tests in the manner described above.
Rather than executing the tests and logging the results, the test configuration
is stored in a \fIrunfile\fR which can be used in future invocations, or edited
to modify which tests are executed and which options are applied. Options
included on the command line with \fB-w\fR become defaults in the
\fIrunfile\fR.
With the \fB-c\fR option, \fBrun\fR parses a \fIrunfile\fR, which can specify a
series of tests and test groups to be executed. The tests are then executed,
and the results are logged.
.sp
.SS "Test Groups"
.sp
.LP
A test group is comprised of a set of executable files, all of which exist in
one directory. The options specified on the command line or in a \fIrunfile\fR
apply to individual tests in the group. The exception is options pertaining to
pre and post scripts, which act on all tests as a group. Rather than running
before and after each test, these scripts are run only once each at the start
and end of the test group.
.SS "Test Execution"
.sp
.LP
The specified tests run serially, and are typically assigned results according
to exit values. Tests that exit zero and non-zero are marked "PASS" and "FAIL"
respectively. When a pre script fails for a test group, only the post script is
executed, and the remaining tests are marked "SKIPPED." Any test that exceeds
its \fItimeout\fR is terminated, and marked "KILLED."
By default, tests are executed with the credentials of the \fBrun\fR script.
Executing tests with other credentials is done via \fBsudo\fR(1m), which must
be configured to allow execution without prompting for a password. Environment
variables from the calling shell are available to individual tests. During test
execution, the working directory is changed to \fIoutputdir\fR.
.SS "Output Logging"
.sp
.LP
By default, \fBrun\fR will print one line on standard output at the conclusion
of each test indicating the test name, result and elapsed time. Additionally,
for each invocation of \fBrun\fR, a directory is created using the ISO 8601
date format. Within this directory is a file named \fIlog\fR containing all the
test output with timestamps, and a directory for each test. Within the test
directories, there is one file each for standard output, standard error and
merged output. The default location for the \fIoutputdir\fR is
\fI/var/tmp/test_results\fR.
.SS "Runfiles"
.sp
.LP
The \fIrunfile\fR is an ini style configuration file that describes a test run.
The file has one section named "DEFAULT," which contains configuration option
names and their values in "name = value" format. The values in this section
apply to all the subsequent sections, unless they are also specified there, in
which case the default is overridden. The remaining section names are the
absolute pathnames of files and direcotries, describing tests and test groups
respectively. The legal option names are:
.sp
.ne 2
.na
\fBoutputdir\fR = \fIpathname\fR
.ad
.sp .6
.RS 4n
The name of the directory that holds test logs.
.RE
.sp
.ne 2
.na
\fBpre\fR = \fIscript\fR
.ad
.sp .6
.RS 4n
Run \fIscript\fR prior to the test or test group.
.RE
.sp
.ne 2
.na
\fBpre_user\fR = \fIusername\fR
.ad
.sp .6
.RS 4n
Execute the pre script as \fIusername\fR.
.RE
.sp
.ne 2
.na
\fBpost\fR = \fIscript\fR
.ad
.sp .6
.RS 4n
Run \fIscript\fR after the test or test group.
.RE
.sp
.ne 2
.na
\fBpost_user\fR = \fIusername\fR
.ad
.sp .6
.RS 4n
Execute the post script as \fIusername\fR.
.RE
.sp
.ne 2
.na
\fBquiet\fR = [\fITrue\fR|\fIFalse\fR]
.ad
.sp .6
.RS 4n
If set to True, only the results summary is printed to standard out.
.RE
.sp
.ne 2
.na
\fBtests\fR = [\fI'filename'\fR [,...]]
.ad
.sp .6
.RS 4n
Specify a list of \fIfilenames\fR for this test group. Only the basename of the
absolute path is required. This option is only valid for test groups, and each
\fIfilename\fR must be single quoted.
.RE
.sp
.ne 2
.na
\fBtimeout\fR = \fIn\fR
.ad
.sp .6
.RS 4n
A timeout value of \fIn\fR seconds.
.RE
.sp
.ne 2
.na
\fBuser\fR = \fIusername\fR
.ad
.sp .6
.RS 4n
Execute the test or test group as \fIusername\fR.
.RE
.SH OPTIONS
.sp
.LP
The following options are available for the \fBrun\fR command.
.sp
.ne 2
.na
\fB-c\fR \fIrunfile\fR
.ad
.RS 6n
Specify a \fIrunfile\fR to be consumed by the run command.
.RE
.ne 2
.na
\fB-d\fR
.ad
.RS 6n
Dry run mode. Execute no tests, but print a description of each test that would
have been run.
.RE
.ne 2
.na
\fB-g\fR
.ad
.RS 6n
Create test groups from any directories found while searching for tests.
.RE
.ne 2
.na
\fB-o\fR \fIoutputdir\fR
.ad
.RS 6n
Specify the directory in which to write test results.
.RE
.ne 2
.na
\fB-p\fR \fIscript\fR
.ad
.RS 6n
Run \fIscript\fR prior to any test or test group.
.RE
.ne 2
.na
\fB-P\fR \fIscript\fR
.ad
.RS 6n
Run \fIscript\fR after any test or test group.
.RE
.ne 2
.na
\fB-q\fR
.ad
.RS 6n
Print only the results sumary to the standard output.
.RE
.ne 2
.na
\fB-t\fR \fIn\fR
.ad
.RS 6n
Specify a timeout value of \fIn\fR seconds per test.
.RE
.ne 2
.na
\fB-u\fR \fIusername\fR
.ad
.RS 6n
Execute tests or test groups as \fIusername\fR.
.RE
.ne 2
.na
\fB-w\fR \fIrunfile\fR
.ad
.RS 6n
Specify the name of the \fIrunfile\fR to create.
.RE
.ne 2
.na
\fB-x\fR \fIusername\fR
.ad
.RS 6n
Execute the pre script as \fIusername\fR.
.RE
.ne 2
.na
\fB-X\fR \fIusername\fR
.ad
.RS 6n
Execute the post script as \fIusername\fR.
.RE
.SH EXAMPLES
.LP
\fBExample 1\fR Running ad-hoc tests.
.sp
.LP
This example demonstrates the simplest invocation of \fBrun\fR.
.sp
.in +2
.nf
% \fBrun my-tests\fR
Test: /home/jkennedy/my-tests/test-01 [00:02] [PASS]
Test: /home/jkennedy/my-tests/test-02 [00:04] [PASS]
Test: /home/jkennedy/my-tests/test-03 [00:01] [PASS]
Results Summary
PASS 3
Running Time: 00:00:07
Percent passed: 100.0%
Log directory: /var/tmp/test_results/20120923T180654
.fi
.in -2
.LP
\fBExample 2\fR Creating a \fIrunfile\fR for future use.
.sp
.LP
This example demonstrates creating a \fIrunfile\fR with non default options.
.sp
.in +2
.nf
% \fBrun -p setup -x root -g -w new-tests.run new-tests\fR
% \fBcat new-tests.run\fR
[DEFAULT]
pre = setup
post_user =
quiet = False
user =
timeout = 60
post =
pre_user = root
outputdir = /var/tmp/test_results
[/home/jkennedy/new-tests]
tests = ['test-01', 'test-02', 'test-03']
.fi
.in -2
.SH EXIT STATUS
.sp
.LP
The following exit values are returned:
.sp
.ne 2
.na
\fB\fB0\fR\fR
.ad
.sp .6
.RS 4n
Successful completion.
.RE
.sp
.ne 2
.na
\fB\fB1\fR\fR
.ad
.sp .6
.RS 4n
An error occurred.
.RE
.SH SEE ALSO
.sp
.LP
\fBsudo\fR(1m)

View File

@ -0,0 +1 @@
SUBDIRS = cmd include tests

View File

@ -0,0 +1,22 @@
EXTRA_DIST = file_common.h
SUBDIRS = \
chg_usr_exec \
devname2devid \
dir_rd_update \
file_check \
file_trunc \
file_write \
largest_file \
mkbusy \
mkfile \
mkfiles \
mktree \
mmap_exec \
mmapwrite \
randfree_file \
readmmap \
rename_dir \
rm_lnkcnt_zero_file \
threadsappend \
xattrtest

View File

@ -0,0 +1 @@
/chg_usr_exec

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = chg_usr_exec
chg_usr_exec_SOURCES = chg_usr_exec.c

View File

@ -0,0 +1,77 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#define EXECSHELL "/bin/sh"
int
main(int argc, char *argv[])
{
char *plogin = NULL;
char cmds[BUFSIZ] = { 0 };
char sep[] = " ";
struct passwd *ppw = NULL;
int i, len;
if (argc < 3 || strlen(argv[1]) == 0) {
(void) printf("\tUsage: %s <login> <commands> ...\n", argv[0]);
return (1);
}
plogin = argv[1];
len = 0;
for (i = 2; i < argc; i++) {
(void) snprintf(cmds+len, sizeof (cmds)-len,
"%s%s", argv[i], sep);
len += strlen(argv[i]) + strlen(sep);
}
if ((ppw = getpwnam(plogin)) == NULL) {
perror("getpwnam");
return (errno);
}
if (setgid(ppw->pw_gid) != 0) {
perror("setgid");
return (errno);
}
if (setuid(ppw->pw_uid) != 0) {
perror("setuid");
return (errno);
}
if (execl(EXECSHELL, "sh", "-c", cmds, (char *)NULL) != 0) {
perror("execl: " EXECSHELL);
return (errno);
}
return (0);
}

View File

@ -0,0 +1 @@
/devname2devid

View File

@ -0,0 +1,7 @@
include $(top_srcdir)/config/Rules.am
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/bin
# DISABLED: Not supported under Linux
# pkgdata_PROGRAMS = devname2devid
# devname2devid_SOURCES = devname2devid.c

View File

@ -0,0 +1,120 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <devid.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
/*
* Usage: devname2devid <devicepath>
*
* Examples:
* # ./devname2devid /dev/dsk/c1t4d0s0
* devid id1,sd@SSEAGATE_ST318404LSUN18G_3BT2G0Z300002146G4CR/a
* # ./devname2devid /dev/dsk/c1t4d0
* devid id1,sd@SSEAGATE_ST318404LSUN18G_3BT2G0Z300002146G4CR/wd
* # ./devname2devid /dev/dsk/c1t4d0s1
* devid id1,sd@SSEAGATE_ST318404LSUN18G_3BT2G0Z300002146G4CR/b
* #
*
* This program accepts a disk or disk slice path and prints a
* device id.
*
* Exit values:
* 0 - means success
* 1 - means failure
*
*/
int
main(int argc, char *argv[])
{
int fd;
ddi_devid_t devid;
char *minor_name, *devidstr, *device;
#ifdef DEBUG
devid_nmlist_t *list = NULL;
char *search_path;
int i;
#endif
if (argc == 1) {
(void) printf("%s <devicepath> [search path]\n",
argv[0]);
exit(1);
}
device = argv[1];
if ((fd = open(device, O_RDONLY|O_NDELAY)) < 0) {
perror(device);
exit(1);
}
if (devid_get(fd, &devid) != 0) {
perror("devid_get");
exit(1);
}
if (devid_get_minor_name(fd, &minor_name) != 0) {
perror("devid_get_minor_name");
exit(1);
}
if ((devidstr = devid_str_encode(devid, minor_name)) == 0) {
perror("devid_str_encode");
exit(1);
}
(void) printf("devid %s\n", devidstr);
devid_str_free(devidstr);
#ifdef DEBUG
if (argc == 3) {
search_path = argv[2];
} else {
search_path = "/dev/rdsk";
}
if (devid_deviceid_to_nmlist(search_path, devid, DEVID_MINOR_NAME_ALL,
&list)) {
perror("devid_deviceid_to_nmlist");
exit(1);
}
/* loop through list and process device names and numbers */
for (i = 0; list[i].devname != NULL; i++) {
(void) printf("devname: %s %p\n", list[i].devname, list[i].dev);
}
devid_free_nmlist(list);
#endif /* DEBUG */
devid_str_free(minor_name);
devid_free(devid);
return (0);
}

View File

@ -0,0 +1 @@
/dir_rd_update

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = dir_rd_update
dir_rd_update_SOURCES = dir_rd_update.c

View File

@ -0,0 +1,117 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Assertion:
*
* A read operation and directory update operation performed
* concurrently on the same directory can lead to deadlock
* on a UFS logging file system, but not on a ZFS file system.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define TMP_DIR /tmp
static char dirpath[256];
int
main(int argc, char **argv)
{
char *cp1 = "";
int i = 0;
int ret = 0;
int testdd = 0;
pid_t pid;
static const int op_num = 5;
if (argc == 1) {
(void) printf("Usage: %s <mount point>\n", argv[0]);
exit(-1);
}
for (i = 0; i < 256; i++) {
dirpath[i] = 0;
}
cp1 = argv[1];
(void) strcpy(&dirpath[0], (const char *)cp1);
(void) strcat(&dirpath[strlen(dirpath)], "TMP_DIR");
ret = mkdir(dirpath, 0777);
if (ret != 0) {
if (errno != EEXIST) {
(void) printf("%s: mkdir(<%s>, 0777) failed: errno "
"(decimal)=%d\n", argv[0], dirpath, errno);
exit(-1);
}
}
testdd = open(dirpath, O_RDONLY|O_RSYNC|O_SYNC|O_DSYNC);
if (testdd < 0) {
(void) printf("%s: open(<%s>, O_RDONLY|O_RSYNC|O_SYNC|O_DSYNC)"
" failed: errno (decimal)=%d\n", argv[0], dirpath, errno);
exit(-1);
} else {
(void) close(testdd);
}
pid = fork();
if (pid > 0) {
int fd = open(dirpath, O_RDONLY|O_RSYNC|O_SYNC|O_DSYNC);
char buf[16];
int rdret;
int j = 0;
while (j < op_num) {
(void) sleep(1);
rdret = read(fd, buf, 16);
if (rdret == -1) {
(void) printf("readdir failed");
}
j++;
}
} else if (pid == 0) {
int fd = open(dirpath, O_RDONLY);
int chownret;
int k = 0;
while (k < op_num) {
(void) sleep(1);
chownret = fchown(fd, 0, 0);
if (chownret == -1) {
(void) printf("chown failed");
}
k++;
}
}
return (0);
}

View File

@ -0,0 +1 @@
/file_check

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = file_check
file_check_SOURCES = file_check.c

View File

@ -0,0 +1,86 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "../file_common.h"
static unsigned char bigbuffer[BIGBUFFERSIZE];
/*
* Given a filename, check that the file consists entirely
* of a particular pattern. If the pattern is not specified a
* default will be used. For default values see file_common.h
*/
int
main(int argc, char **argv)
{
int bigfd;
long i, n;
unsigned char fillchar = DATA;
int bigbuffersize = BIGBUFFERSIZE;
int64_t read_count = 0;
/*
* Validate arguments
*/
if (argc < 2) {
(void) printf("Usage: %s filename [pattern]\n",
argv[0]);
exit(1);
}
if (argv[2]) {
fillchar = atoi(argv[2]);
}
/*
* Read the file contents and check every character
* against the supplied pattern. Abort if the
* pattern check fails.
*/
if ((bigfd = open(argv[1], O_RDONLY)) == -1) {
(void) printf("open %s failed %d\n", argv[1], errno);
exit(1);
}
do {
if ((n = read(bigfd, &bigbuffer, bigbuffersize)) == -1) {
(void) printf("read failed (%ld), %d\n", n, errno);
exit(errno);
}
for (i = 0; i < n; i++) {
if (bigbuffer[i] != fillchar) {
(void) printf("error %s: 0x%x != 0x%x)\n",
argv[1], bigbuffer[i], fillchar);
exit(1);
}
}
read_count += n;
} while (n == bigbuffersize);
return (0);
}

View File

@ -0,0 +1,62 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef FILE_COMMON_H
#define FILE_COMMON_H
/*
* header file for file_* utilities. These utilities
* are used by the test cases to perform various file
* operations (append writes, for example).
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#define BLOCKSZ 8192
#define DATA 0xa5
#define DATA_RANGE 120
#define BIGBUFFERSIZE 0x800000
#define BIGFILESIZE 20
extern char *optarg;
extern int optind, opterr, optopt;
#ifdef __cplusplus
}
#endif
#endif /* FILE_COMMON_H */

View File

@ -0,0 +1 @@
/file_trunc

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = file_trunc
file_trunc_SOURCES = file_trunc.c

View File

@ -0,0 +1,244 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <string.h>
#include <time.h>
#include <inttypes.h>
#define FSIZE 256*1024*1024
#define BSIZE 512
/* Initialize Globals */
static long fsize = FSIZE;
static size_t bsize = BSIZE;
static int count = 0;
static int rflag = 0;
static int seed = 0;
static int vflag = 0;
static int errflag = 0;
static off_t offset = 0;
static char *filename = NULL;
static void usage(char *execname);
static void parse_options(int argc, char *argv[]);
static void do_write(int fd);
static void do_trunc(int fd);
static void
usage(char *execname)
{
(void) fprintf(stderr,
"usage: %s [-b blocksize] [-c count] [-f filesize]"
" [-o offset] [-s seed] [-r] [-v] filename\n", execname);
(void) exit(1);
}
int
main(int argc, char *argv[])
{
int i = 0;
int fd = -1;
parse_options(argc, argv);
fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
if (fd < 0) {
perror("open");
exit(3);
}
while (i < count) {
(void) do_write(fd);
(void) do_trunc(fd);
i++;
}
(void) close(fd);
return (0);
}
static void
parse_options(int argc, char *argv[])
{
int c;
extern char *optarg;
extern int optind, optopt;
count = fsize / bsize;
seed = time(NULL);
while ((c = getopt(argc, argv, "b:c:f:o:rs:v")) != -1) {
switch (c) {
case 'b':
bsize = atoi(optarg);
break;
case 'c':
count = atoi(optarg);
break;
case 'f':
fsize = atoi(optarg);
break;
case 'o':
offset = atoi(optarg);
break;
case 'r':
rflag++;
break;
case 's':
seed = atoi(optarg);
break;
case 'v':
vflag++;
break;
case ':':
(void) fprintf(stderr,
"Option -%c requires an operand\n", optopt);
errflag++;
break;
case '?':
(void) fprintf(stderr,
"Unrecognized option: -%c\n", optopt);
errflag++;
break;
}
if (errflag) {
(void) usage(argv[0]);
}
}
if (argc <= optind) {
(void) fprintf(stderr,
"No filename specified\n");
usage(argv[0]);
}
filename = argv[optind];
if (vflag) {
(void) fprintf(stderr, "Seed = %d\n", seed);
}
srandom(seed);
}
static void
do_write(int fd)
{
off_t roffset = 0;
char *buf = NULL;
char *rbuf = NULL;
buf = (char *)calloc(1, bsize);
rbuf = (char *)calloc(1, bsize);
if (buf == NULL || rbuf == NULL) {
perror("malloc");
exit(4);
}
roffset = random() % fsize;
if (lseek64(fd, (offset + roffset), SEEK_SET) < 0) {
perror("lseek");
exit(5);
}
strcpy(buf, "ZFS Test Suite Truncation Test");
if (write(fd, buf, bsize) < bsize) {
perror("write");
exit(6);
}
if (rflag) {
if (lseek64(fd, (offset + roffset), SEEK_SET) < 0) {
perror("lseek");
exit(7);
}
if (read(fd, rbuf, bsize) < bsize) {
perror("read");
exit(8);
}
if (memcmp(buf, rbuf, bsize) != 0) {
perror("memcmp");
exit(9);
}
}
if (vflag) {
(void) fprintf(stderr,
"Wrote to offset %" PRId64 "\n", (offset + roffset));
if (rflag) {
(void) fprintf(stderr,
"Read back from offset %" PRId64 "\n",
(offset + roffset));
}
}
(void) free(buf);
(void) free(rbuf);
}
static void
do_trunc(int fd)
{
off_t roffset = 0;
roffset = random() % fsize;
if (ftruncate64(fd, (offset + roffset)) < 0) {
perror("truncate");
exit(7);
}
if (vflag) {
(void) fprintf(stderr, "Truncated at offset %" PRId64 "\n",
(offset + roffset));
}
}

View File

@ -0,0 +1 @@
/file_write

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = file_write
file_write_SOURCES = file_write.c

View File

@ -0,0 +1,229 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "../file_common.h"
#include <libgen.h>
#include <string.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
typedef unsigned char uchar_t;
typedef long long longlong_t;
typedef longlong_t offset_t;
static unsigned char bigbuffer[BIGBUFFERSIZE];
/*
* Writes (or appends) a given value to a file repeatedly.
* See header file for defaults.
*/
static void usage(char *);
int
main(int argc, char **argv)
{
int bigfd;
int c;
int oflag = 0;
int err = 0;
int k;
long i;
int64_t good_writes = 0;
uchar_t nxtfillchar;
char *prog = argv[0];
/*
* Default Parameters
*/
int write_count = BIGFILESIZE;
uchar_t fillchar = DATA;
int block_size = BLOCKSZ;
char *filename = NULL;
char *operation = NULL;
offset_t noffset, offset = 0;
int verbose = 0;
int rsync = 0;
int wsync = 0;
/*
* Process Arguments
*/
while ((c = getopt(argc, argv, "b:c:d:s:f:o:vwr")) != -1) {
switch (c) {
case 'b':
block_size = atoi(optarg);
break;
case 'c':
write_count = atoi(optarg);
break;
case 'd':
fillchar = atoi(optarg);
break;
case 's':
offset = atoll(optarg);
break;
case 'f':
filename = optarg;
break;
case 'o':
operation = optarg;
break;
case 'v':
verbose = 1;
break;
case 'w':
wsync = 1;
break;
case 'r':
rsync = 1;
break;
case '?':
(void) printf("unknown arg %c\n", optopt);
usage(prog);
break;
}
}
/*
* Validate Parameters
*/
if (!filename) {
(void) printf("Filename not specified (-f <file>)\n");
err++;
}
if (!operation) {
(void) printf("Operation not specified (-o <operation>).\n");
err++;
}
if (block_size > BIGBUFFERSIZE) {
(void) printf("block_size is too large max==%d.\n",
BIGBUFFERSIZE);
err++;
}
if (err) usage(prog);
/*
* Prepare the buffer and determine the requested operation
*/
nxtfillchar = fillchar;
k = 0;
for (i = 0; i < block_size; i++) {
bigbuffer[i] = nxtfillchar;
if (fillchar == 0) {
if ((k % DATA_RANGE) == 0) {
k = 0;
}
nxtfillchar = k++;
}
}
/*
* using the strncmp of operation will make the operation match the
* first shortest match - as the operations are unique from the first
* character this means that we match single character operations
*/
if ((strncmp(operation, "create", strlen(operation) + 1)) == 0 ||
(strncmp(operation, "overwrite", strlen(operation) + 1)) == 0) {
oflag = (O_RDWR|O_CREAT);
} else if ((strncmp(operation, "append", strlen(operation) + 1)) == 0) {
oflag = (O_RDWR|O_APPEND);
} else {
(void) printf("valid operations are <create|append> not '%s'\n",
operation);
usage(prog);
}
if (rsync) {
oflag = oflag | O_RSYNC;
}
if (wsync) {
oflag = oflag | O_SYNC;
}
/*
* Given an operation (create/overwrite/append), open the file
* accordingly and perform a write of the appropriate type.
*/
if ((bigfd = open(filename, oflag, 0666)) == -1) {
(void) printf("open %s: failed [%s]%d. Aborting!\n", filename,
strerror(errno), errno);
exit(errno);
}
noffset = lseek64(bigfd, offset, SEEK_SET);
if (noffset != offset) {
(void) printf("llseek %s (%lld/%lld) failed [%s]%d.Aborting!\n",
filename, offset, noffset, strerror(errno), errno);
exit(errno);
}
if (verbose) {
(void) printf("%s: block_size = %d, write_count = %d, "
"offset = %lld, data = %s%d\n", filename, block_size,
write_count, offset,
(fillchar == 0) ? "0->" : "",
(fillchar == 0) ? DATA_RANGE : fillchar);
}
for (i = 0; i < write_count; i++) {
ssize_t n;
if ((n = write(bigfd, &bigbuffer, block_size)) == -1) {
(void) printf("write failed (%ld), good_writes = %"
PRId64 ", " "error: %s[%d]\n",
(long)n, good_writes,
strerror(errno),
errno);
exit(errno);
}
good_writes++;
}
if (verbose) {
(void) printf("Success: good_writes = %" PRId64 "(%"
PRId64 ")\n", good_writes, (good_writes * block_size));
}
return (0);
}
static void
usage(char *prog)
{
(void) printf("Usage: %s [-v] -o {create,overwrite,append} -f file_name"
" [-b block_size]\n"
"\t[-s offset] [-c write_count] [-d data]\n"
"\twhere [data] equal to zero causes chars "
"0->%d to be repeated throughout\n", prog, DATA_RANGE);
exit(1);
}

View File

@ -0,0 +1 @@
/largest_file

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = largest_file
largest_file_SOURCES = largest_file.c

View File

@ -0,0 +1,140 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include "../file_common.h"
#include <sys/param.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
typedef long long offset_t;
#define MAXOFFSET_T LLONG_MAX
/*
* --------------------------------------------------------------
*
* Assertion:
* The last byte of the largest file size can be
* accessed without any errors. Also, the writing
* beyond the last byte of the largest file size
* will produce an errno of EFBIG.
*
* --------------------------------------------------------------
* If the write() system call below returns a "1",
* then the last byte can be accessed.
* --------------------------------------------------------------
*/
static void sigxfsz(int);
static void usage(char *);
int
main(int argc, char **argv)
{
int fd = 0;
offset_t offset = (MAXOFFSET_T - 1);
offset_t llseek_ret = 0;
int write_ret = 0;
int err = 0;
char mybuf[5];
char *testfile;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
if (argc != 2) {
usage(argv[0]);
}
(void) sigset(SIGXFSZ, sigxfsz);
testfile = strdup(argv[1]);
fd = open(testfile, O_CREAT | O_RDWR, mode);
if (fd < 0) {
perror("Failed to create testfile");
err = errno;
goto out;
}
llseek_ret = lseek64(fd, offset, SEEK_SET);
if (llseek_ret < 0) {
perror("Failed to seek to end of testfile");
err = errno;
goto out;
}
write_ret = write(fd, mybuf, 1);
if (write_ret < 0) {
perror("Failed to write to end of file");
err = errno;
goto out;
}
offset = 0;
llseek_ret = lseek64(fd, offset, SEEK_CUR);
if (llseek_ret < 0) {
perror("Failed to seek to end of file");
err = errno;
goto out;
}
write_ret = write(fd, mybuf, 1);
if (write_ret < 0) {
if (errno == EFBIG) {
(void) printf("write errno=EFBIG: success\n");
err = 0;
} else {
perror("Did not receive EFBIG");
err = errno;
}
} else {
(void) printf("write completed successfully, test failed\n");
err = 1;
}
out:
(void) unlink(testfile);
free(testfile);
return (err);
}
static void
usage(char *name)
{
(void) printf("%s <testfile>\n", name);
exit(1);
}
/* ARGSUSED */
static void
sigxfsz(int signo)
{
(void) printf("\nlargest_file: sigxfsz() caught SIGXFSZ\n");
}

1
tests/zfs-tests/cmd/mkbusy/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/mkbusy

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = mkbusy
mkbusy_SOURCES = mkbusy.c

View File

@ -0,0 +1,183 @@
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
* Make a directory busy. If the argument is an existing file or directory,
* simply open it directly and pause. If not, verify that the parent directory
* exists, and create a new file in that directory.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
typedef enum boolean { B_FALSE, B_TRUE } boolean_t;
static void
usage(char *progname)
{
(void) fprintf(stderr, "Usage: %s <dirname|filename>\n", progname);
exit(1);
}
static void
fail(char *err, int rval)
{
perror(err);
exit(rval);
}
static void
daemonize(void)
{
pid_t pid;
if ((pid = fork()) < 0) {
fail("fork", 1);
} else if (pid != 0) {
(void) fprintf(stdout, "%ld\n", (long)pid);
exit(0);
}
(void) setsid();
(void) close(0);
(void) close(1);
(void) close(2);
}
int
main(int argc, char *argv[])
{
int ret, c;
boolean_t isdir = B_FALSE;
boolean_t fflag = B_FALSE;
boolean_t rflag = B_FALSE;
struct stat sbuf;
char *fpath = NULL;
char *prog = argv[0];
while ((c = getopt(argc, argv, "fr")) != -1) {
switch (c) {
/* Open the file or directory read only */
case 'r':
rflag = B_TRUE;
break;
/* Run in the foreground */
case 'f':
fflag = B_TRUE;
break;
default:
usage(prog);
}
}
argc -= optind;
argv += optind;
if (argc != 1)
usage(prog);
if ((ret = stat(argv[0], &sbuf)) != 0) {
char *arg, *dname, *fname;
int arglen, dlen, flen;
char *slash;
/*
* The argument supplied doesn't exist. Copy the path, and
* remove the trailing slash if presnt.
*/
if ((arg = strdup(argv[0])) == NULL)
fail("strdup", 1);
arglen = strlen(arg);
if (arg[arglen - 1] == '/')
arg[arglen - 1] = '\0';
/*
* Get the directory and file names, using the current directory
* if the provided path doesn't specify a directory at all.
*/
if ((slash = strrchr(arg, '/')) == NULL) {
dname = strdup(".");
fname = strdup(arg);
} else {
*slash = '\0';
dname = strdup(arg);
fname = strdup(slash + 1);
}
free(arg);
if (dname == NULL || fname == NULL)
fail("strdup", 1);
dlen = strlen(dname);
flen = strlen(fname);
/* The directory portion of the path must exist */
if ((ret = stat(dname, &sbuf)) != 0 || !(sbuf.st_mode &
S_IFDIR))
usage(prog);
if ((fpath = (char *)malloc(dlen + 1 + flen + 1)) == NULL)
fail("malloc", 1);
(void) memset(fpath, '\0', dlen + 1 + flen + 1);
(void) strncpy(fpath, dname, dlen);
fpath[dlen] = '/';
(void) strncat(fpath, fname, flen);
free(dname);
free(fname);
} else if ((sbuf.st_mode & S_IFMT) == S_IFREG ||
(sbuf.st_mode & S_IFMT) == S_IFLNK ||
(sbuf.st_mode & S_IFMT) == S_IFCHR ||
(sbuf.st_mode & S_IFMT) == S_IFBLK) {
fpath = strdup(argv[0]);
} else if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
fpath = strdup(argv[0]);
isdir = B_TRUE;
} else {
usage(prog);
}
if (fpath == NULL)
fail("strdup", 1);
if (isdir == B_FALSE) {
int fd, flags;
mode_t mode = S_IRUSR | S_IWUSR;
flags = rflag == B_FALSE ? O_CREAT | O_RDWR : O_RDONLY;
if ((fd = open(fpath, flags, mode)) < 0)
fail("open", 1);
} else {
DIR *dp;
if ((dp = opendir(fpath)) == NULL)
fail("opendir", 1);
}
free(fpath);
if (fflag == B_FALSE)
daemonize();
(void) pause();
/* NOTREACHED */
return (0);
}

1
tests/zfs-tests/cmd/mkfile/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/mkfile

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = mkfile
mkfile_SOURCES = mkfile.c

View File

@ -0,0 +1,275 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <errno.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define BLOCK_SIZE 512 /* bytes */
#define KILOBYTE 1024
#define MEGABYTE (KILOBYTE * KILOBYTE)
#define GIGABYTE (KILOBYTE * MEGABYTE)
#define FILE_MODE (S_ISVTX + S_IRUSR + S_IWUSR)
typedef long long offset_t;
static void usage(void);
int
main(int argc, char **argv)
{
char *opts;
off_t size;
size_t len;
size_t mult = 1;
char *buf = NULL;
size_t bufsz = 0;
int errors = 0;
int i;
int verbose = 0; /* option variable */
int nobytes = 0; /* option variable */
int saverr;
if (argc == 1)
usage();
while (argv[1] && argv[1][0] == '-') {
opts = &argv[1][0];
while (*(++opts)) {
switch (*opts) {
case 'v':
verbose++;
break;
case 'n':
nobytes++;
break;
default:
usage();
}
}
argc--;
argv++;
}
if (argc < 3)
usage();
len = strlen(argv[1]);
if (len && isalpha(argv[1][len-1])) {
switch (argv[1][len-1]) {
case 'k':
case 'K':
mult = KILOBYTE;
break;
case 'b':
case 'B':
mult = BLOCK_SIZE;
break;
case 'm':
case 'M':
mult = MEGABYTE;
break;
case 'g':
case 'G':
mult = GIGABYTE;
break;
default:
(void) fprintf(stderr,
gettext("unknown size %s\n"), argv[1]);
usage();
}
for (i = 0; i <= (len-2); i++) {
if (!isdigit(argv[1][i])) {
(void) fprintf(stderr,
gettext("unknown size %s\n"), argv[1]);
usage();
}
}
argv[1][len-1] = '\0';
}
size = ((off_t)atoll(argv[1]) * (off_t)mult);
argv++;
argc--;
while (argc > 1) {
int fd;
if (verbose)
(void) fprintf(stdout, gettext("%s %lld bytes\n"),
argv[1], (offset_t)size);
fd = open(argv[1], O_CREAT|O_TRUNC|O_RDWR, FILE_MODE);
if (fd < 0) {
saverr = errno;
(void) fprintf(stderr,
gettext("Could not open %s: %s\n"),
argv[1], strerror(saverr));
errors++;
argv++;
argc--;
continue;
}
if (lseek(fd, (off_t)size-1, SEEK_SET) < 0) {
saverr = errno;
(void) fprintf(stderr, gettext(
"Could not seek to offset %ld in %s: %s\n"),
(unsigned long)size-1, argv[1], strerror(saverr));
(void) close(fd);
errors++;
argv++;
argc--;
continue;
} else if (write(fd, "", 1) != 1) {
saverr = errno;
(void) fprintf(stderr, gettext(
"Could not set length of %s: %s\n"),
argv[1], strerror(saverr));
(void) close(fd);
errors++;
argv++;
argc--;
continue;
}
if (!nobytes) {
off_t written = 0;
struct stat64 st;
if (lseek(fd, (off_t)0, SEEK_SET) < 0) {
saverr = errno;
(void) fprintf(stderr, gettext(
"Could not seek to beginning of %s: %s\n"),
argv[1], strerror(saverr));
(void) close(fd);
errors++;
argv++;
argc--;
continue;
}
if (fstat64(fd, &st) < 0) {
saverr = errno;
(void) fprintf(stderr, gettext(
"Could not fstat64 %s: %s\n"),
argv[1], strerror(saverr));
(void) close(fd);
errors++;
argv++;
argc--;
continue;
}
if (bufsz != st.st_blksize) {
if (buf)
free(buf);
bufsz = (size_t)st.st_blksize;
buf = calloc(bufsz, 1);
if (buf == NULL) {
(void) fprintf(stderr, gettext(
"Could not allocate buffer of"
" size %d\n"), (int)bufsz);
(void) close(fd);
bufsz = 0;
errors++;
argv++;
argc--;
continue;
}
}
while (written < size) {
ssize_t result;
size_t bytes = (size_t)MIN(bufsz, size-written);
if ((result = write(fd, buf, bytes)) !=
(ssize_t)bytes) {
saverr = errno;
if (result < 0)
result = 0;
written += result;
(void) fprintf(stderr, gettext(
"%s: initialized %lu of %lu bytes: %s\n"),
argv[1], (unsigned long)written,
(unsigned long)size,
strerror(saverr));
errors++;
break;
}
written += bytes;
}
/*
* A write(2) call in the above loop failed so
* close out this file and go on (error was
* already incremented when the write(2) failed).
*/
if (written < size) {
(void) close(fd);
argv++;
argc--;
continue;
}
}
if (close(fd) < 0) {
saverr = errno;
(void) fprintf(stderr, gettext(
"Error encountered when closing %s: %s\n"),
argv[1], strerror(saverr));
errors++;
argv++;
argc--;
continue;
}
/*
* Only set the modes (including the sticky bit) if we
* had no problems. It is not an error for the chmod(2)
* to fail, but do issue a warning.
*/
if (chmod(argv[1], FILE_MODE) < 0)
(void) fprintf(stderr, gettext(
"warning: couldn't set mode to %#o\n"), FILE_MODE);
argv++;
argc--;
}
return (errors);
}
static void usage()
{
(void) fprintf(stderr, gettext(
"Usage: mkfile [-nv] <size>[g|k|b|m] <name1> [<name2>] ...\n"));
exit(1);
/* NOTREACHED */
}

View File

@ -0,0 +1 @@
/mkfiles

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = mkfiles
mkfiles_SOURCES = mkfiles.c

View File

@ -0,0 +1,65 @@
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright (c) 2016 by Delphix. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/param.h>
#define MAX_INT_LENGTH 10
static void
usage(char *msg, int exit_value)
{
(void) fprintf(stderr, "mkfiles basename max_file [min_file]\n");
(void) fprintf(stderr, "%s\n", msg);
exit(exit_value);
}
int
main(int argc, char **argv)
{
unsigned int numfiles = 0;
unsigned int first_file = 0;
unsigned int i;
char buf[MAXPATHLEN];
if (argc < 3 || argc > 4)
usage("Invalid number of arguments", -1);
if (sscanf(argv[2], "%u", &numfiles) != 1)
usage("Invalid maximum file", -2);
if (argc == 4 && sscanf(argv[3], "%u", &first_file) != 1)
usage("Invalid first file", -3);
if (numfiles < first_file)
usage("First file larger than last file", -3);
for (i = first_file; i <= numfiles; i++) {
int fd;
(void) snprintf(buf, MAXPATHLEN, "%s%u", argv[1], i);
if ((fd = open(buf, O_CREAT | O_EXCL, O_RDWR)) == -1) {
(void) fprintf(stderr, "Failed to create %s %s\n", buf,
strerror(errno));
return (-4);
}
(void) close(fd);
}
return (0);
}

1
tests/zfs-tests/cmd/mktree/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/mktree

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = mktree
mktree_SOURCES = mktree.c

View File

@ -0,0 +1,183 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <attr/xattr.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
#define TYPE_D 'D'
#define TYPE_F 'F'
static char fdname[MAXPATHLEN] = {0};
static char *pbasedir = NULL;
static int nlevel = 2;
static int ndir = 2;
static int nfile = 2;
static void usage(char *this);
static void crtfile(char *pname);
static char *getfdname(char *pdir, char type, int level, int dir, int file);
static int mktree(char *pbasedir, int level);
int
main(int argc, char *argv[])
{
int c, ret;
while ((c = getopt(argc, argv, "b:l:d:f:")) != -1) {
switch (c) {
case 'b':
pbasedir = optarg;
break;
case 'l':
nlevel = atoi(optarg);
break;
case 'd':
ndir = atoi(optarg);
break;
case 'f':
nfile = atoi(optarg);
break;
case '?':
usage(argv[0]);
}
}
if (nlevel < 0 || ndir < 0 || nfile < 0 || pbasedir == NULL) {
usage(argv[0]);
}
ret = mktree(pbasedir, 1);
return (ret);
}
static void
usage(char *this)
{
(void) fprintf(stderr,
"\tUsage: %s -b <base_dir> -l [nlevel] -d [ndir] -f [nfile]\n",
this);
exit(1);
}
static int
mktree(char *pdir, int level)
{
int d, f;
char dname[MAXPATHLEN] = {0};
char fname[MAXPATHLEN] = {0};
if (level > nlevel) {
return (1);
}
for (d = 0; d < ndir; d++) {
(void) memset(dname, '\0', sizeof (dname));
(void) strcpy(dname, getfdname(pdir, TYPE_D, level, d, 0));
if (mkdir(dname, 0777) != 0) {
(void) fprintf(stderr, "mkdir(%s) failed."
"\n[%d]: %s.\n",
dname, errno, strerror(errno));
exit(errno);
}
/*
* No sub-directory need be created, only create files in it.
*/
if (mktree(dname, level+1) != 0) {
for (f = 0; f < nfile; f++) {
(void) memset(fname, '\0', sizeof (fname));
(void) strcpy(fname,
getfdname(dname, TYPE_F, level+1, d, f));
crtfile(fname);
}
}
}
for (f = 0; f < nfile; f++) {
(void) memset(fname, '\0', sizeof (fname));
(void) strcpy(fname, getfdname(pdir, TYPE_F, level, d, f));
crtfile(fname);
}
return (0);
}
static char *
getfdname(char *pdir, char type, int level, int dir, int file)
{
(void) snprintf(fdname, sizeof (fdname),
"%s/%c-l%dd%df%d", pdir, type, level, dir, file);
return (fdname);
}
static void
crtfile(char *pname)
{
int fd = -1;
int i, size;
char *context = "0123456789ABCDF";
char *pbuf;
if (pname == NULL) {
exit(1);
}
size = sizeof (char) * 1024;
pbuf = (char *)valloc(size);
for (i = 0; i < size / strlen(context); i++) {
int offset = i * strlen(context);
(void) snprintf(pbuf+offset, size-offset, "%s", context);
}
if ((fd = open(pname, O_CREAT|O_RDWR, 0777)) < 0) {
(void) fprintf(stderr, "open(%s, O_CREAT|O_RDWR, 0777) failed."
"\n[%d]: %s.\n", pname, errno, strerror(errno));
exit(errno);
}
if (write(fd, pbuf, 1024) < 1024) {
(void) fprintf(stderr, "write(fd, pbuf, 1024) failed."
"\n[%d]: %s.\n", errno, strerror(errno));
exit(errno);
}
if (fsetxattr(fd, "xattr", pbuf, 1024, 0) < 0) {
(void) fprintf(stderr, "fsetxattr(fd, \"xattr\", pbuf, "
"1024, 0) failed.\n[%d]: %s.\n", errno, strerror(errno));
exit(errno);
}
(void) close(fd);
free(pbuf);
}

View File

@ -0,0 +1 @@
/mmap_exec

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = mmap_exec
mmap_exec_SOURCES = mmap_exec.c

View File

@ -0,0 +1,69 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
int fd;
struct stat statbuf;
if (argc != 2) {
(void) printf("Error: missing binary name.\n");
(void) printf("Usage:\n\t%s <binary name>\n",
argv[0]);
return (1);
}
errno = 0;
if ((fd = open(argv[1], O_RDONLY)) < 0) {
perror("open");
return (errno);
}
if (fstat(fd, &statbuf) < 0) {
perror("fstat");
return (errno);
}
if (mmap(0, statbuf.st_size,
PROT_EXEC, MAP_SHARED, fd, 0) == MAP_FAILED) {
perror("mmap");
return (errno);
}
return (0);
}

View File

@ -0,0 +1 @@
/mmapwrite

View File

@ -0,0 +1,7 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = mmapwrite
mmapwrite_SOURCES = mmapwrite.c
mmapwrite_LDADD = -lpthread

View File

@ -0,0 +1,97 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <pthread.h>
/*
* --------------------------------------------------------------------
* Bug Id: 5032643
*
* Simply writing to a file and mmaping that file at the same time can
* result in deadlock. Nothing perverse like writing from the file's
* own mapping is required.
* --------------------------------------------------------------------
*/
static void *
mapper(void *fdp)
{
void *addr;
int fd = *(int *)fdp;
if ((addr =
mmap(0, 8192, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
perror("mmap");
exit(1);
}
for (;;) {
if (mmap(addr, 8192, PROT_READ,
MAP_SHARED|MAP_FIXED, fd, 0) == MAP_FAILED) {
perror("mmap");
exit(1);
}
}
/* NOTREACHED */
return ((void *)1);
}
int
main(int argc, char **argv)
{
int fd;
char buf[BUFSIZ];
pthread_t tid;
if (argc != 2) {
(void) printf("usage: %s <file name>\n", argv[0]);
exit(1);
}
if ((fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666)) == -1) {
perror("open");
exit(1);
}
(void) pthread_setconcurrency(2);
if (pthread_create(&tid, NULL, mapper, &fd) != 0) {
perror("pthread_create");
exit(1);
}
for (;;) {
if (write(fd, buf, sizeof (buf)) == -1) {
perror("write");
exit(1);
}
}
/* NOTREACHED */
return (0);
}

View File

@ -0,0 +1 @@
/randfree_file

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = randfree_file
randfree_file_SOURCES = randfree_file.c

View File

@ -0,0 +1,105 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include "../file_common.h"
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/falloc.h>
/*
* Create a file with assigned size and then free the specified
* section of the file
*/
static void usage(char *progname);
static void
usage(char *progname)
{
(void) fprintf(stderr,
"usage: %s [-l filesize] [-s start-offset]"
"[-n section-len] filename\n", progname);
exit(1);
}
int
main(int argc, char *argv[])
{
char *filename = NULL;
char *buf;
size_t filesize = 0;
off_t start_off = 0;
off_t off_len = 0;
int fd, ch;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
while ((ch = getopt(argc, argv, "l:s:n:")) != EOF) {
switch (ch) {
case 'l':
filesize = atoll(optarg);
break;
case 's':
start_off = atoll(optarg);
break;
case 'n':
off_len = atoll(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if (optind == argc - 1)
filename = argv[optind];
else
usage(argv[0]);
buf = (char *)malloc(filesize);
if ((fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, mode)) < 0) {
perror("open");
return (1);
}
if (write(fd, buf, filesize) < filesize) {
perror("write");
return (1);
}
if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
start_off, off_len) < 0) {
perror("fallocate");
return (1);
}
free(buf);
return (0);
}

View File

@ -0,0 +1 @@
/readmmap

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = readmmap
readmmap_SOURCES = readmmap.c

View File

@ -0,0 +1,138 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* --------------------------------------------------------------
* BugId 5047993 : Getting bad read data.
*
* Usage: readmmap <filename>
*
* where:
* filename is an absolute path to the file name.
*
* Return values:
* 1 : error
* 0 : no errors
* --------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <time.h>
int
main(int argc, char **argv)
{
char *filename = "badfile";
size_t size = 4395;
size_t idx = 0;
char *buf = NULL;
char *map = NULL;
int fd = -1, bytes, retval = 0;
unsigned seed;
if (argc < 2 || optind == argc) {
(void) fprintf(stderr,
"usage: %s <file name>\n", argv[0]);
exit(1);
}
if ((buf = calloc(1, size)) == NULL) {
perror("calloc");
exit(1);
}
filename = argv[optind];
(void) remove(filename);
fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
if (fd == -1) {
perror("open to create");
retval = 1;
goto end;
}
bytes = write(fd, buf, size);
if (bytes != size) {
(void) printf("short write: %d != %zd\n", bytes, size);
retval = 1;
goto end;
}
map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
perror("mmap");
retval = 1;
goto end;
}
seed = time(NULL);
srandom(seed);
idx = random() % size;
map[idx] = 1;
if (msync(map, size, MS_SYNC) != 0) {
perror("msync");
retval = 1;
goto end;
}
if (munmap(map, size) != 0) {
perror("munmap");
retval = 1;
goto end;
}
bytes = pread(fd, buf, size, 0);
if (bytes != size) {
(void) printf("short read: %d != %zd\n", bytes, size);
retval = 1;
goto end;
}
if (buf[idx] != 1) {
(void) printf(
"bad data from read! got buf[%zd]=%d, expected 1\n",
idx, buf[idx]);
retval = 1;
goto end;
}
(void) printf("good data from read: buf[%zd]=1\n", idx);
end:
if (fd != -1) {
(void) close(fd);
}
if (buf != NULL) {
free(buf);
}
return (retval);
}

View File

@ -0,0 +1 @@
/rename_dir

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = rename_dir
rename_dir_SOURCES = rename_dir.c

View File

@ -0,0 +1,88 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
* Assertion:
* Create two directory trees in zfs filesystem, and rename
* directory across the directory structure. ZFS can handle
* the race situation.
*/
/*
* Need to create the following directory structures before
* running this program:
*
* mkdir -p 1/2/3/4/5 a/b/c/d/e
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>
int
main(int argc, char *argvp[])
{
int i = 1;
switch (fork()) {
case -1:
perror("fork");
exit(1);
break;
case 0:
while (i > 0) {
int c_count = 0;
if (rename("a/b/c", "1/2/3/c") == 0)
c_count++;
if (rename("1/2/3/c", "a/b/c") == 0)
c_count++;
if (c_count) {
(void) fprintf(stderr, "c_count: %d", c_count);
}
}
break;
default:
while (i > 0) {
int p_count = 0;
if (rename("1", "a/b/c/d/e/1") == 0)
p_count++;
if (rename("a/b/c/d/e/1", "1") == 0)
p_count++;
if (p_count) {
(void) fprintf(stderr, "p_count: %d", p_count);
}
}
break;
}
return (0);
}

View File

@ -0,0 +1 @@
/rm_lnkcnt_zero_file

View File

@ -0,0 +1,7 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = rm_lnkcnt_zero_file
rm_lnkcnt_zero_file_SOURCES = rm_lnkcnt_zero_file.c
rm_lnkcnt_zero_file_LDADD = -lpthread

View File

@ -0,0 +1,155 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
* --------------------------------------------------------------------
* The purpose of this test is to see if the bug reported (#4723351) for
* UFS exists when using a ZFS file system.
* --------------------------------------------------------------------
*
*/
#define _REENTRANT 1
#include <stdio.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
static const int TRUE = 1;
static char *filebase;
static int
pickidx(void)
{
return (random() % 1000);
}
/* ARGSUSED */
static void *
mover(void *a)
{
char buf[256];
int idx, len, ret;
len = strlen(filebase) + 5;
while (TRUE) {
idx = pickidx();
(void) snprintf(buf, len, "%s.%03d", filebase, idx);
ret = rename(filebase, buf);
if (ret < 0 && errno != ENOENT)
(void) perror("renaming file");
}
return (NULL);
}
/* ARGSUSED */
static void *
cleaner(void *a)
{
char buf[256];
int idx, len, ret;
len = strlen(filebase) + 5;
while (TRUE) {
idx = pickidx();
(void) snprintf(buf, len, "%s.%03d", filebase, idx);
ret = remove(buf);
if (ret < 0 && errno != ENOENT)
(void) perror("removing file");
}
return (NULL);
}
static void *
writer(void *a)
{
int *fd = (int *)a;
int ret;
while (TRUE) {
(void) close (*fd);
*fd = open(filebase, O_APPEND | O_RDWR | O_CREAT, 0644);
if (*fd < 0)
perror("refreshing file");
ret = write(*fd, "test\n", 5);
if (ret != 5)
perror("writing file");
}
return (NULL);
}
int
main(int argc, char **argv)
{
int fd;
pthread_t tid;
if (argc == 1) {
(void) printf("Usage: %s <filebase>\n", argv[0]);
exit(-1);
}
filebase = argv[1];
fd = open(filebase, O_APPEND | O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("creating test file");
exit(-1);
}
(void) pthread_setconcurrency(4); /* 3 threads + main */
(void) pthread_create(&tid, NULL, mover, NULL);
(void) pthread_create(&tid, NULL, cleaner, NULL);
(void) pthread_create(&tid, NULL, writer, (void *) &fd);
while (TRUE) {
int ret;
struct stat st;
ret = stat(filebase, &st);
if (ret == 0 && (st.st_nlink > 2 || st.st_nlink < 1)) {
(void) printf("st.st_nlink = %d, exiting\n", \
(int)st.st_nlink);
exit(0);
}
(void) sleep(1);
}
return (0);
}

View File

@ -0,0 +1 @@
/threadsappend

View File

@ -0,0 +1,7 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = threadsappend
threadsappend_SOURCES = threadsappend.c
threadsappend_LDADD = -lpthread

View File

@ -0,0 +1,135 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
/*
* The size of the output file, "go.out", should be 80*8192*2 = 1310720
*
* $ cd /tmp; go; ls -l go.out
* done.
* -rwxr-xr-x 1 jdm staff 1310720 Apr 13 19:45 go.out
* $ cd /zfs; go; ls -l go.out
* done.
* -rwxr-xr-x 1 jdm staff 663552 Apr 13 19:45 go.out
*
* The file on zfs is short as it does not appear that zfs is making the
* implicit seek to EOF and the actual write atomic. From the SUSv3
* interface spec, behavior is undefined if concurrent writes are performed
* from multi-processes to a single file. So I don't know if this is a
* standards violation, but I cannot find any such disclaimers in our
* man pages. This issue came up at a customer site in another context, and
* the suggestion was to open the file with O_APPEND, but that wouldn't
* help with zfs(see 4977529). Also see bug# 5031301.
*/
static int outfd = 0;
static void *
go(void *data)
{
int ret, i = 0, n = *(int *)data;
char buf[8192] = {0};
(void) memset(buf, n, sizeof (buf));
for (i = 0; i < 80; i++) {
ret = write(outfd, buf, sizeof (buf));
if (ret != sizeof (buf))
perror("write");
}
return (NULL);
}
static void
usage(void)
{
(void) fprintf(stderr,
"usage: zfs_threadsappend <file name>\n");
exit(1);
}
int
main(int argc, char **argv)
{
pthread_t tid;
int ret = 0;
long ncpus = 0;
int i;
if (argc != 2) {
usage();
}
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
if (ncpus < 0) {
(void) fprintf(stderr,
"Invalid return from sysconf(_SC_NPROCESSORS_ONLN)"
" : errno (decimal)=%d\n", errno);
exit(1);
}
if (ncpus < 2) {
(void) fprintf(stderr,
"Must execute this binary on a multi-processor system\n");
exit(1);
}
outfd = open(argv[optind++], O_RDWR|O_CREAT|O_APPEND|O_TRUNC, 0777);
if (outfd == -1) {
(void) fprintf(stderr,
"zfs_threadsappend: "
"open(%s, O_RDWR|O_CREAT|O_APPEND|O_TRUNC, 0777)"
" failed\n", argv[optind]);
perror("open");
exit(1);
}
for (i = 0; i < 2; i++) {
ret = pthread_create(&tid, NULL, go, (void *)&i);
if (ret != 0) {
(void) fprintf(stderr,
"zfs_threadsappend: thr_create(#%d) "
"failed error=%d\n", i+1, ret);
exit(1);
}
}
while (pthread_join(tid, NULL) == 0)
continue;
return (0);
}

View File

@ -0,0 +1 @@
/xattrtest

View File

@ -0,0 +1,6 @@
include $(top_srcdir)/config/Rules.am
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
pkgexec_PROGRAMS = xattrtest
xattrtest_SOURCES = xattrtest.c

View File

@ -0,0 +1,641 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2016 Lawrence Livermore National Security, LLC.
*/
/*
* An extended attribute (xattr) correctness test. This program creates
* N files and sets M attrs on them of size S. Optionally is will verify
* a pattern stored in the xattr.
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <attr/xattr.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <linux/limits.h>
extern char *program_invocation_short_name;
#define ERROR(fmt, ...) \
fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
program_invocation_short_name, __FILE__, __LINE__, \
__func__, ## __VA_ARGS__);
static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRk";
static const struct option longopts[] = {
{ "help", no_argument, 0, 'h' },
{ "verbose", no_argument, 0, 'v' },
{ "verify", no_argument, 0, 'y' },
{ "nth", required_argument, 0, 'n' },
{ "files", required_argument, 0, 'f' },
{ "xattrs", required_argument, 0, 'x' },
{ "size", required_argument, 0, 's' },
{ "path", required_argument, 0, 'p' },
{ "synccaches", no_argument, 0, 'c' },
{ "dropcaches", no_argument, 0, 'd' },
{ "script", required_argument, 0, 't' },
{ "seed", required_argument, 0, 'e' },
{ "random", no_argument, 0, 'r' },
{ "randomvalue", no_argument, 0, 'R' },
{ "keep", no_argument, 0, 'k' },
{ 0, 0, 0, 0 }
};
static int verbose = 0;
static int verify = 0;
static int synccaches = 0;
static int dropcaches = 0;
static int nth = 0;
static int files = 1000;
static int xattrs = 1;
static int size = 1;
static int size_is_random = 0;
static int value_is_random = 0;
static int keep_files = 0;
static char path[PATH_MAX] = "/tmp/xattrtest";
static char script[PATH_MAX] = "/bin/true";
static int
usage(int argc, char **argv) {
fprintf(stderr,
"usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
" [-s <bytes>] [-p <path>] [-t <script> ]\n", argv[0]);
fprintf(stderr,
" --help -h This help\n"
" --verbose -v Increase verbosity\n"
" --verify -y Verify xattr contents\n"
" --nth -n <nth> Print every nth file\n"
" --files -f <files> Set xattrs on N files\n"
" --xattrs -x <xattrs> Set N xattrs on each file\n"
" --size -s <bytes> Set N bytes per xattr\n"
" --path -p <path> Path to files\n"
" --synccaches -c Sync caches between phases\n"
" --dropcaches -d Drop caches between phases\n"
" --script -t <script> Exec script between phases\n"
" --seed -e <seed> Random seed value\n"
" --random -r Randomly sized xattrs [16-size]\n"
" --randomvalue -R Random xattr values\n"
" --keep -k Don't unlink files\n\n");
return (0);
}
static int
parse_args(int argc, char **argv)
{
long seed = time(NULL);
int c;
int rc;
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
switch (c) {
case 'h':
return (usage(argc, argv));
case 'v':
verbose++;
break;
case 'y':
verify = 1;
if (value_is_random != 0) {
fprintf(stderr,
"Error: -y and -R are incompatible.\n");
rc = 1;
}
break;
case 'n':
nth = strtol(optarg, NULL, 0);
break;
case 'f':
files = strtol(optarg, NULL, 0);
break;
case 'x':
xattrs = strtol(optarg, NULL, 0);
break;
case 's':
size = strtol(optarg, NULL, 0);
if (size > XATTR_SIZE_MAX) {
fprintf(stderr, "Error: the size may not be "
"greater than %d\n", XATTR_SIZE_MAX);
rc = 1;
}
break;
case 'p':
strncpy(path, optarg, PATH_MAX);
break;
case 'c':
synccaches = 1;
break;
case 'd':
dropcaches = 1;
break;
case 't':
strncpy(script, optarg, PATH_MAX);
break;
case 'e':
seed = strtol(optarg, NULL, 0);
break;
case 'r':
size_is_random = 1;
break;
case 'R':
value_is_random = 1;
if (verify != 0) {
fprintf(stderr,
"Error: -y and -R are incompatible.\n");
rc = 1;
}
break;
case 'k':
keep_files = 1;
break;
default:
rc = 1;
break;
}
}
if (rc != 0)
return (rc);
srandom(seed);
if (verbose) {
fprintf(stdout, "verbose: %d\n", verbose);
fprintf(stdout, "verify: %d\n", verify);
fprintf(stdout, "nth: %d\n", nth);
fprintf(stdout, "files: %d\n", files);
fprintf(stdout, "xattrs: %d\n", xattrs);
fprintf(stdout, "size: %d\n", size);
fprintf(stdout, "path: %s\n", path);
fprintf(stdout, "synccaches: %d\n", synccaches);
fprintf(stdout, "dropcaches: %d\n", dropcaches);
fprintf(stdout, "script: %s\n", script);
fprintf(stdout, "seed: %ld\n", seed);
fprintf(stdout, "random size: %d\n", size_is_random);
fprintf(stdout, "random value: %d\n", value_is_random);
fprintf(stdout, "keep: %d\n", keep_files);
fprintf(stdout, "%s", "\n");
}
return (rc);
}
static int
drop_caches(void)
{
char file[] = "/proc/sys/vm/drop_caches";
int fd, rc;
fd = open(file, O_WRONLY);
if (fd == -1) {
ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file);
return (errno);
}
rc = write(fd, "3", 1);
if ((rc == -1) || (rc != 1)) {
ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd);
return (errno);
}
rc = close(fd);
if (rc == -1) {
ERROR("Error %d: close(%d)\n", errno, fd);
return (errno);
}
return (0);
}
static int
run_process(const char *path, char *argv[])
{
pid_t pid;
int rc, devnull_fd;
pid = vfork();
if (pid == 0) {
devnull_fd = open("/dev/null", O_WRONLY);
if (devnull_fd < 0)
_exit(-1);
(void) dup2(devnull_fd, STDOUT_FILENO);
(void) dup2(devnull_fd, STDERR_FILENO);
close(devnull_fd);
(void) execvp(path, argv);
_exit(-1);
} else if (pid > 0) {
int status;
while ((rc = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
if (rc < 0 || !WIFEXITED(status))
return (-1);
return (WEXITSTATUS(status));
}
return (-1);
}
static int
post_hook(char *phase)
{
char *argv[3] = { script, phase, (char *)0 };
int rc;
if (synccaches)
sync();
if (dropcaches) {
rc = drop_caches();
if (rc)
return (rc);
}
rc = run_process(script, argv);
if (rc)
return (rc);
return (0);
}
#define USEC_PER_SEC 1000000
static void
timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec)
{
while (usec >= USEC_PER_SEC) {
usec -= USEC_PER_SEC;
sec++;
}
while (usec < 0) {
usec += USEC_PER_SEC;
sec--;
}
tv->tv_sec = sec;
tv->tv_usec = usec;
}
static void
timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)
{
timeval_normalize(delta,
tv1->tv_sec - tv2->tv_sec,
tv1->tv_usec - tv2->tv_usec);
}
static int
create_files(void)
{
int i, rc;
char *file = NULL;
struct timeval start, stop, delta;
file = malloc(PATH_MAX);
if (file == NULL) {
rc = ENOMEM;
ERROR("Error %d: malloc(%d) bytes for file name\n",
rc, PATH_MAX);
goto out;
}
(void) gettimeofday(&start, NULL);
for (i = 1; i <= files; i++) {
(void) sprintf(file, "%s/file-%d", path, i);
if (nth && ((i % nth) == 0))
fprintf(stdout, "create: %s\n", file);
rc = unlink(file);
if ((rc == -1) && (errno != ENOENT)) {
ERROR("Error %d: unlink(%s)\n", errno, file);
rc = errno;
goto out;
}
rc = open(file, O_CREAT, 0644);
if (rc == -1) {
ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
errno, file);
rc = errno;
goto out;
}
rc = close(rc);
if (rc == -1) {
ERROR("Error %d: close(%d)\n", errno, rc);
rc = errno;
goto out;
}
}
(void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start);
fprintf(stdout, "create: %d.%d seconds\n",
(int)delta.tv_sec, (int)delta.tv_usec);
rc = post_hook("post");
out:
if (file)
free(file);
return (rc);
}
static int
get_random_bytes(char *buf, size_t bytes)
{
int rand;
ssize_t bytes_read = 0;
rand = open("/dev/urandom", O_RDONLY);
if (rand < 0)
return (rand);
while (bytes_read < bytes) {
ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read);
if (rc < 0)
break;
bytes_read += rc;
}
(void) close(rand);
return (bytes_read);
}
static int
setxattrs(void)
{
int i, j, rnd_size = size, shift, rc = 0;
char name[XATTR_NAME_MAX];
char *value = NULL;
char *file = NULL;
struct timeval start, stop, delta;
value = malloc(XATTR_SIZE_MAX);
if (value == NULL) {
rc = ENOMEM;
ERROR("Error %d: malloc(%d) bytes for xattr value\n",
rc, XATTR_SIZE_MAX);
goto out;
}
file = malloc(PATH_MAX);
if (file == NULL) {
rc = ENOMEM;
ERROR("Error %d: malloc(%d) bytes for file name\n",
rc, PATH_MAX);
goto out;
}
(void) gettimeofday(&start, NULL);
for (i = 1; i <= files; i++) {
(void) sprintf(file, "%s/file-%d", path, i);
if (nth && ((i % nth) == 0))
fprintf(stdout, "setxattr: %s\n", file);
for (j = 1; j <= xattrs; j++) {
if (size_is_random)
rnd_size = (random() % (size - 16)) + 16;
(void) sprintf(name, "user.%d", j);
if (value_is_random) {
rc = get_random_bytes(value, rnd_size);
if (rc < rnd_size) {
ERROR("Error %d: get_random_bytes() "
"wanted %d got %d\n", errno,
rnd_size, rc);
goto out;
}
} else {
shift = sprintf(value, "size=%d ", rnd_size);
memset(value + shift, 'x', XATTR_SIZE_MAX -
shift);
}
rc = lsetxattr(file, name, value, rnd_size, 0);
if (rc == -1) {
ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",
errno, file, name, rnd_size);
goto out;
}
}
}
(void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start);
fprintf(stdout, "setxattr: %d.%d seconds\n",
(int)delta.tv_sec, (int)delta.tv_usec);
rc = post_hook("post");
out:
if (file)
free(file);
if (value)
free(value);
return (rc);
}
static int
getxattrs(void)
{
int i, j, rnd_size, shift, rc = 0;
char name[XATTR_NAME_MAX];
char *verify_value = NULL;
char *value = NULL;
char *file = NULL;
struct timeval start, stop, delta;
verify_value = malloc(XATTR_SIZE_MAX);
if (verify_value == NULL) {
rc = ENOMEM;
ERROR("Error %d: malloc(%d) bytes for xattr verify\n",
rc, XATTR_SIZE_MAX);
goto out;
}
value = malloc(XATTR_SIZE_MAX);
if (value == NULL) {
rc = ENOMEM;
ERROR("Error %d: malloc(%d) bytes for xattr value\n",
rc, XATTR_SIZE_MAX);
goto out;
}
file = malloc(PATH_MAX);
if (file == NULL) {
rc = ENOMEM;
ERROR("Error %d: malloc(%d) bytes for file name\n",
rc, PATH_MAX);
goto out;
}
(void) gettimeofday(&start, NULL);
for (i = 1; i <= files; i++) {
(void) sprintf(file, "%s/file-%d", path, i);
if (nth && ((i % nth) == 0))
fprintf(stdout, "getxattr: %s\n", file);
for (j = 1; j <= xattrs; j++) {
(void) sprintf(name, "user.%d", j);
rc = lgetxattr(file, name, value, XATTR_SIZE_MAX);
if (rc == -1) {
ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
errno, file, name, XATTR_SIZE_MAX);
goto out;
}
if (verify) {
sscanf(value, "size=%d [a-z]", &rnd_size);
shift = sprintf(verify_value, "size=%d ",
rnd_size);
memset(verify_value + shift, 'x',
XATTR_SIZE_MAX - shift);
if (rnd_size != rc ||
memcmp(verify_value, value, rnd_size)) {
ERROR("Error %d: verify failed\n "
"verify: %s\nvalue: %s\n",
EINVAL, verify_value, value);
goto out;
}
}
}
}
(void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start);
fprintf(stdout, "getxattr: %d.%d seconds\n",
(int)delta.tv_sec, (int)delta.tv_usec);
rc = post_hook("post");
out:
if (file)
free(file);
if (value)
free(value);
if (verify_value)
free(verify_value);
return (rc);
}
static int
unlink_files(void)
{
int i, rc;
char *file = NULL;
struct timeval start, stop, delta;
file = malloc(PATH_MAX);
if (file == NULL) {
rc = ENOMEM;
ERROR("Error %d: malloc(%d) bytes for file name\n",
rc, PATH_MAX);
goto out;
}
(void) gettimeofday(&start, NULL);
for (i = 1; i <= files; i++) {
(void) sprintf(file, "%s/file-%d", path, i);
if (nth && ((i % nth) == 0))
fprintf(stdout, "unlink: %s\n", file);
rc = unlink(file);
if ((rc == -1) && (errno != ENOENT)) {
ERROR("Error %d: unlink(%s)\n", errno, file);
return (errno);
}
}
(void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start);
fprintf(stdout, "unlink: %d.%d seconds\n",
(int)delta.tv_sec, (int)delta.tv_usec);
rc = post_hook("post");
out:
if (file)
free(file);
return (rc);
}
int
main(int argc, char **argv)
{
int rc;
rc = parse_args(argc, argv);
if (rc)
return (rc);
rc = create_files();
if (rc)
return (rc);
rc = setxattrs();
if (rc)
return (rc);
rc = getxattrs();
if (rc)
return (rc);
if (!keep_files) {
rc = unlink_files();
if (rc)
return (rc);
}
return (0);
}

2
tests/zfs-tests/include/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/commands.cfg
/default.cfg

View File

@ -0,0 +1,10 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/include
dist_pkgdata_SCRIPTS = \
commands.cfg \
default.cfg \
libtest.shlib \
math.shlib \
properties.shlib
EXTRA_DIST = \
zfs_commands.cfg

View File

@ -0,0 +1,119 @@
export AWK="@AWK@"
export BLOCKDEV="@BLOCKDEV@"
export BASENAME="@BASENAME@"
export BC="@BC@"
export BUNZIP2="@BUNZIP2@"
export BZCAT="@BZCAT@"
export CAT="@CAT@"
export CD="@CD@"
export CHACL="@CHACL@"
export CHGRP="@CHGRP@"
export CHMOD="@CHMOD@"
export CHOWN="@CHOWN@"
export CKSUM="@CKSUM@"
export CMP="@CMP@"
export COMPRESS="@COMPRESS@"
export COREADM="@COREADM@"
export CP="@CP@"
export CPIO="@CPIO@"
export CUT="@CUT@"
export DATE="@DATE@"
export DD="@DD@"
export DF="@DF@"
export DIFF="@DIFF@"
export DIRCMP="@DIRCMP@"
export DIRNAME="@DIRNAME@"
export DU="@DU@"
export DUMPADM="@DUMPADM@"
export ECHO="@ECHO@"
export EGREP="@EGREP@"
export FDISK="@FDISK@"
export FGREP="@FGREP@"
export FILE="@FILE@"
export FIND="@FIND@"
export FORMAT="@FORMAT@"
export FSCK="@FSCK@"
export GETENT="@GETENT@"
export GETFACL="@GETFACL@"
export GETMAJOR="@GETMAJOR@"
export GNUDD="@GNUDD@"
export GREP="@GREP@"
export GROUPADD="@GROUPADD@"
export GROUPDEL="@GROUPDEL@"
export GROUPMOD="@GROUPMOD@"
export HEAD="@HEAD@"
export HOSTNAME="@HOSTNAME@"
export ID="@ID@"
export ISAINFO="@ISAINFO@"
export KILL="@KILL@"
export KSH="@KSH@"
export KSTAT="@KSTAT@"
export LOCKFS="@LOCKFS@"
export LOFIADM="@LOFIADM@"
export LOGNAME="@LOGNAME@"
export LS="@LS@"
export MD5SUM="@MD5SUM@"
export MKDIR="@MKDIR@"
export MKNOD="@MKNOD@"
export MKTEMP="@MKTEMP@"
export MNTTAB="@MNTTAB@"
export MODINFO="@MODINFO@"
export MODUNLOAD="@MODUNLOAD@"
export MOUNT="@MOUNT@"
export MV="@MV@"
export NAWK="@AWK@"
export NEWFS="@NEWFS@"
export NPROC="@NPROC@"
export PAGESIZE="@PAGESIZE@"
export PFEXEC="@PFEXEC@"
export PGREP="@PGREP@"
export PING="@PING@"
export PKGINFO="@PKGINFO@"
export PKILL="@PKILL@"
export PRINTF="@PRINTF@"
export PRTVTOC="@PRTVTOC@"
export PS="@PS@"
export PSRINFO="@PSRINFO@"
export PYTHON="@PYTHON@"
export REBOOT="@REBOOT@"
export RM="@RM@"
export RMDIR="@RMDIR@"
export RSH="@RSH@"
export SED="@SED@"
export SETFACL="@SETFACL@"
export SHARE="@SHARE@"
export SHUF="@SHUF@"
export SLEEP="@SLEEP@"
export SORT="@SORT@"
export STRINGS="@STRINGS@"
export SU="@SU@"
export SUM="@SUM@"
export SVCADM="@SVCADM@"
export SVCS="@SVCS@"
export SWAP="@SWAP@"
export SWAPADD="@SWAPADD@"
export SYNC="@SYNC@"
export TAIL="@TAIL@"
export TAR="@TAR@"
export TOUCH="@TOUCH@"
export TR="@TR@"
export TRUE="@TRUE@"
export TRUNCATE="@TRUNCATE@"
export UDEVADM="@UDEVADM@"
export UFSDUMP="@UFSDUMP@"
export UFSRESTORE="@UFSRESTORE@"
export UMASK="@UMASK@"
export UMOUNT="@UMOUNT@"
export UMOUNTALL="@UMOUNTALL@"
export UNAME="@UNAME@"
export UNCOMPRESS="@UNCOMPRESS@"
export UNIQ="@UNIQ@"
export UNSHARE="@UNSHARE@"
export USERADD="@USERADD@"
export USERDEL="@USERDEL@"
export USERMOD="@USERMOD@"
export WAIT="@WAIT@"
export WC="@WC@"
export ZONEADM="@ZONEADM@"
export ZONECFG="@ZONECFG@"
export ZONENAME="@ZONENAME@"

View File

@ -0,0 +1,200 @@
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#
# Copyright (c) 2016 by Delphix. All rights reserved.
#
. $STF_SUITE/include/commands.cfg
. $STF_SUITE/include/libtest.shlib
# Optionally override the installed ZFS commands to run in-tree
if [[ -f "$STF_SUITE/include/zfs_commands.cfg" ]]; then
. $STF_SUITE/include/zfs_commands.cfg
fi
# Define run length constants
export RT_LONG="3"
export RT_MEDIUM="2"
export RT_SHORT="1"
# Define macro for zone test
export ZONE_POOL="zonepool"
export ZONE_CTR="zonectr"
# Common paths
bindir=@bindir@
sbindir=@sbindir@
helperdir=@datarootdir@/@PACKAGE@/zfs-tests/bin
# ZFS Commands
ZDB=${ZDB:-${sbindir}/zdb}
ZFS=${ZFS:-${sbindir}/zfs}
ZHACK=${ZHACK:-${sbindir}/zhack}
ZINJECT=${ZINJECT:-${sbindir}/zinject}
ZPOOL=${ZPOOL:-${sbindir}/zpool}
ZTEST=${ZTEST:-${sbindir}/ztest}
ZPIOS=${ZPIOS:-${sbindir}/zpios}
# Test Suite Specific Commands
export CHG_USR_EXEC=${CHG_USR_EXEC:-${helperdir}/chg_usr_exec}
export DEVNAME2DEVID=${DEVNAME2DEVID:-${helperdir}/devname2devid}
export DIR_RD_UPDATE=${DIR_RD_UPDATE:-${helperdir}/dir_rd_update}
export FILE_CHECK=${FILE_CHECK:-${helperdir}/file_check}
export FILE_TRUNC=${FILE_TRUNC:-${helperdir}/file_trunc}
export FILE_WRITE=${FILE_WRITE:-${helperdir}/file_write}
export LARGEST_FILE=${LARGEST_FILE:-${helperdir}/largest_file}
export MKBUSY=${MKBUSY:-${helperdir}/mkbusy}
export MKFILE=${MKFILE:-${helperdir}/mkfile}
export MKFILES=${MKFILES:-${helperdir}/mkfiles}
export MKTREE=${MKTREE:-${helperdir}/mktree}
export MMAP_EXEC=${MMAP_EXEC:-${helperdir}/mmap_exec}
export MMAPWRITE=${MMAPWRITE:-${helperdir}/mmapwrite}
export RANDFREE_FILE=${RANDFREE_FILE:-${helperdir}/randfree_file}
export READMMAP=${READMMAP:-${helperdir}/readmmap}
export RENAME_DIR=${RENAME_DIR:-${helperdir}/rename_dir}
export RM_LNKCNT_ZERO_FILE=${RM_LNKCNT_ZERO_FILE:-${helperdir}/rm_lnkcnt_zero_file}
export THREADSAPPEND=${THREADSAPPEND:-${helperdir}/threadsappend}
export XATTRTEST=${XATTRTEST:-${helpdir}/xattrtest}
# ensure we're running in the C locale, since
# localised messages may result in test failures
export LC_ALL="C"
export LANG="C"
#
# pattern to ignore from 'zpool list'.
#
export NO_POOLS="no pools available"
# pattern to ignore from 'zfs list'.
export NO_DATASETS="no datasets available"
export TEST_BASE_DIR="/var/tmp"
# Default to compression ON
export COMPRESSION_PROP=on
# Default to using the checksum
export CHECKSUM_PROP=on
# some common variables used by test scripts :
# some test pool names
export TESTPOOL=testpool.$$
export TESTPOOL1=testpool1.$$
export TESTPOOL2=testpool2.$$
export TESTPOOL3=testpool3.$$
# some test file system names
export TESTFS=testfs.$$
export TESTFS1=testfs1.$$
export TESTFS2=testfs2.$$
export TESTFS3=testfs3.$$
# some test directory names
export TESTDIR=${TEST_BASE_DIR%%/}/testdir$$
export TESTDIR0=${TEST_BASE_DIR%%/}/testdir0$$
export TESTDIR1=${TEST_BASE_DIR%%/}/testdir1$$
export TESTDIR2=${TEST_BASE_DIR%%/}/testdir2$$
export ZFSROOT=
export TESTSNAP=testsnap$$
export TESTSNAP1=testsnap1$$
export TESTSNAP2=testsnap2$$
export TESTCLONE=testclone$$
export TESTCLONE1=testclone1$$
export TESTCLONE2=testclone2$$
export TESTCLCT=testclct$$
export TESTCTR=testctr$$
export TESTCTR1=testctr1$$
export TESTCTR2=testctr2$$
export TESTVOL=testvol$$
export TESTVOL1=testvol1$$
export TESTVOL2=testvol2$$
export TESTFILE0=testfile0.$$
export TESTFILE1=testfile1.$$
export TESTFILE2=testfile2.$$
export LONGPNAME="poolname50charslong_012345678901234567890123456789"
export LONGFSNAME="fsysname50charslong_012345678901234567890123456789"
export SNAPFS="$TESTPOOL/$TESTFS@$TESTSNAP"
export SNAPFS1="$TESTPOOL/$TESTVOL@$TESTSNAP"
export VOLSIZE=150m
export BIGVOLSIZE=1eb
# Default to limit disks to be checked
export MAX_FINDDISKSNUM=6
# For iscsi target support
export ISCSITGTFILE=/tmp/iscsitgt_file
export ISCSITGT_FMRI=svc:/system/iscsitgt:default
#
# finally, if we're running in a local zone
# we take some additional actions
if ! is_global_zone; then
reexport_pool
fi
export ZFS_VERSION=5
export ZFS_ALL_VERSIONS="1 2 3 4 5"
for i in $ZFS_ALL_VERSIONS; do
eval 'export ZFS_VERSION_$i="v${i}-fs"'
done
if is_linux; then
unpack_opts="--sparse -xf"
pack_opts="--sparse -cf"
verbose=" -v"
unpack_preserve=" -xpf"
pack_preserve=" -cpf"
ZVOL_DEVDIR="/dev/zvol"
ZVOL_RDEVDIR="/dev/zvol"
DEV_DSKDIR="/dev"
DEV_RDSKDIR="/dev"
NEWFS_DEFAULT_FS="ext2"
else
unpack_opts="xv"
pack_opts="cf"
verbose="v"
unpack_preserve="xpf"
pack_preserve="cpf"
ZVOL_DEVDIR="/dev/zvol/dsk"
ZVOL_RDEVDIR="/dev/zvol/rdsk"
DEV_DSKDIR="/dev/dsk"
DEV_RDSKDIR="/dev/rdsk"
NEWFS_DEFAULT_FS="ufs"
fi
export unpack_opts pack_opts verbose unpack_preserve pack_preserve \
ZVOL_DEVDIR ZVOL_RDEVDIR NEWFS_DEFAULT_FS DEV_DSKDIR DEV_RDSKDIR

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2012 by Delphix. All rights reserved.
#
#
# Return 0 if the percentage difference between $a and $b is $percent or
# greater. Return 1 if the percentage is lower or if we would divide by
# zero. For use like this:
#
# Do $action if the calculated percentage is greater or equal to that passed in:
# within_percent A B P && $action
# Do $action if the calculated percentage is less than that passed in:
# within_percent A B P || $action
#
function within_percent
{
typeset a=$1
typeset b=$1
typeset percent=$3
# Set $a or $b to $2 such that a >= b
[[ '1' = $($ECHO "if ($2 > $a) 1" | $BC) ]] && a=$2 || b=$2
# Prevent division by 0
[[ $a =~ [1-9] ]] || return 1
typeset p=$($ECHO "scale=2; $b * 100 / $a" | $BC)
log_note "Comparing $a and $b given $percent% (calculated: $p%)"
[[ '1' = $($ECHO "scale=2; if ($p >= $percent) 1" | $BC) ]] && return 0
return 1
}

View File

@ -0,0 +1,63 @@
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2012 by Delphix. All rights reserved.
#
typeset -a compress_props=('on' 'off' 'lzjb' 'gzip' 'gzip-1' 'gzip-2' 'gzip-3'
'gzip-4' 'gzip-5' 'gzip-6' 'gzip-7' 'gzip-8' 'gzip-9' 'zle')
typeset -a checksum_props=('on' 'off' 'fletcher2' 'fletcher4' 'sha256')
#
# Given the property array passed in, return 'num_props' elements to the
# user, excluding any elements below 'start.' This allows us to exclude
# 'off' and 'on' which can be either unwanted, or a duplicate of another
# property respectively.
#
function get_rand_prop
{
typeset prop_array=($(eval echo \${$1[@]}))
typeset -i num_props=$2
typeset -i start=$3
typeset retstr=""
[[ -z $prop_array || -z $num_props || -z $start ]] && \
log_fail "get_rand_prop: bad arguments"
typeset prop_max=$((${#prop_array[@]} - 1))
typeset -i i
for i in $($SHUF -i $start-$prop_max -n $num_props); do
retstr="${prop_array[$i]} $retstr"
done
echo $retstr
}
function get_rand_compress
{
get_rand_prop compress_props $1 2
}
function get_rand_compress_any
{
get_rand_prop compress_props $1 0
}
function get_rand_checksum
{
get_rand_prop checksum_props $1 2
}
function get_rand_checksum_any
{
get_rand_prop checksum_props $1 0
}

View File

@ -0,0 +1 @@
../../../zfs-script-config.sh

View File

@ -0,0 +1 @@
SUBDIRS = functional stress

View File

@ -0,0 +1,55 @@
SUBDIRS = \
acl \
atime \
bootfs \
cache \
cachefile \
casenorm \
clean_mirror \
cli_root \
cli_user \
compression \
ctime \
delegate \
devices \
exec \
features \
grow_pool \
grow_replicas \
history \
inheritance \
inuse \
large_files \
largest_pool \
link_count \
migration \
mmap \
mount \
mv_files \
nestedfs \
no_space \
nopwrite \
online_offline \
pool_names \
poolversion \
privilege \
quota \
redundancy \
refquota \
refreserv \
rename_dirs \
replacement \
reservation \
rootpool \
rsend \
scrub_mirror \
slog \
snapshot \
snapused \
sparse \
threadsappend \
truncate \
userquota \
write_dirs \
xattr \
zvol

View File

@ -0,0 +1,6 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/acl
dist_pkgdata_SCRIPTS = \
acl.cfg \
acl_common.kshlib
SUBDIRS = posix

View File

@ -0,0 +1,64 @@
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
. $STF_SUITE/include/libtest.shlib
export NISSTAFILE=/var/tmp/nis_state
export TESTFILE=testfile$$
export TESTFILE0=testfile0.$$
export TESTFILE2=testfile2.$$
# Define super user 'zfstest'
export ZFS_ACL_ADMIN=zfstest
export ZFS_ACL_STAFF_GROUP=zfsgrp
export ZFS_ACL_STAFF1=staff1
export ZFS_ACL_STAFF2=staff2
export ZFS_ACL_OTHER_GROUP=othergrp
export ZFS_ACL_OTHER1=other1
export ZFS_ACL_OTHER2=other2
# Define the current user who run 'usr_exec'
export ZFS_ACL_CUR_USER=""
# Define global error string
export ZFS_ACL_ERR_STR=""
# Define test file and test directory which will be operated by chmod
export testfile=$TESTDIR/testfile
export testdir=$TESTDIR/testdir
# Define several directories for trivial ACLs function test.
export RES_DIR=$TESTDIR/RES
export INI_DIR=$TESTDIR/INIT
export TST_DIR=$TESTDIR/TEST
export TMP_DIR=$TESTDIR/TMP
# Define test files and their attributes files number for trivial
# ACLs function test
export NUM_FILE=5
export NUM_ATTR=10

View File

@ -0,0 +1,626 @@
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
. $STF_SUITE/tests/functional/acl/acl.cfg
. $STF_SUITE/include/libtest.shlib
#
# Get the given file/directory access mode
#
# $1 object -- file or directroy
#
function get_mode #<obj>
{
typeset obj=$1
if (( ${#obj} == 0 )); then
return 1
fi
$LS -ld $obj | $AWK '{print $1}'
}
#
# Get the given file/directory ACL
#
# $1 object -- file or directroy
#
function get_acl #<obj>
{
typeset obj=$1
if (( ${#obj} == 0 )); then
return 1
fi
$LS -vd $obj | $NAWK '(NR != 1) {print $0}'
}
#
# Get the given file/directory ACL
#
# $1 object -- file or directroy
#
function get_compact_acl #<obj>
{
typeset obj=$1
if (( ${#obj} == 0 )); then
return 1
fi
$LS -Vd $obj | $NAWK '(NR != 1) {print $0}'
}
#
# Check the given two files/directories have the same ACLs
#
# Return 0, if source object acl is equal to target object acl.
#
# $1 source object
# $2 target object
#
function compare_acls #<src> <tgt>
{
typeset src=$1
typeset tgt=$2
(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
[[ $src == $tgt ]] && return 0
typeset tmpsrc=/tmp/compare_acls.src.$$
typeset tmptgt=/tmp/compare_acls.tgt.$$
get_acl $src > $tmpsrc
get_acl $tgt > $tmptgt
typeset -i ret=0
$DIFF $tmpsrc $tmptgt > /dev/null 2>&1
ret=$?
$RM -f $tmpsrc $tmptgt
if (( ret != 0 )); then
return $ret
fi
get_compact_acl $src > $tmpsrc
get_compact_acl $tgt > $tmptgt
$DIFF $tmpsrc $tmptgt > /dev/null 2>&1
ret=$?
$RM -f $tmpsrc $tmptgt
return $ret
}
#
# Check that the given two objects have the same modes.
# Return 0, if their modes are equal with each other. Otherwise, return 1.
#
# $1 source object
# $2 target object
#
function compare_modes #<src> <tgt>
{
typeset src=$1
typeset tgt=$2
typeset -i i=0
set -A mode
(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
[[ $src == $tgt ]] && return 0
typeset obj
for obj in $src $tgt
do
mode[i]=$(get_mode $obj)
(( i = i + 1 ))
done
[[ ${mode[0]} != ${mode[1]} ]] && return 1
return 0
}
#
# Check that the given two objects have the same xattrs.
# Return 0, if their xattrs are equal with each other. Otherwise, return 1.
#
# $1 source object
# $2 target object
#
function compare_xattrs #<src> <tgt>
{
typeset src=$1
typeset tgt=$2
(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
[[ $src == $tgt ]] && return 0
typeset tmpsrc=/tmp/compare_xattrs.src.$$
typeset tmptgt=/tmp/compare_xattrs.tgt.$$
get_xattr $src > $tmpsrc
get_xattr $tgt > $tmptgt
typeset -i ret=0
$DIFF $tmpsrc $tmptgt > /dev/null 2>&1
ret=$?
$RM -f $tmpsrc $tmptgt
return $ret
}
#
# Check '+' is set for a given file/directory with 'ls [-l]' command
#
# $1 object -- file or directory.
#
function plus_sign_check_l #<obj>
{
typeset obj=$1
if (( ${#obj} == 0 )); then
return 1
fi
$LS -ld $obj | $AWK '{print $1}' | $GREP "+\>" > /dev/null
return $?
}
#
# Check '+' is set for a given file/directory with 'ls [-v]' command
#
# $1 object -- file or directory.
#
function plus_sign_check_v #<obj>
{
typeset obj=$1
if (( ${#obj} == 0 )); then
return 1
fi
$LS -vd $obj | $NAWK '(NR == 1) {print $1}' | $GREP "+\>" > /dev/null
return $?
}
#
# A wrapper function of c program
#
# $1 legal login name
# $2-n commands and options
#
function chgusr_exec #<login_name> <commands> [...]
{
$CHG_USR_EXEC $@
return $?
}
#
# Export the current user for the following usr_exec operating.
#
# $1 legal login name
#
function set_cur_usr #<login_name>
{
export ZFS_ACL_CUR_USER=$1
}
#
# Run commands by $ZFS_ACL_CUR_USER
#
# $1-n commands and options
#
function usr_exec #<commands> [...]
{
$CHG_USR_EXEC "$ZFS_ACL_CUR_USER" $@
return $?
}
#
# Count how many ACEs for the speficied file or directory.
#
# $1 file or directroy name
#
function count_ACE #<file or dir name>
{
if [[ ! -e $1 ]]; then
log_note "Need input file or directroy name."
return 1
fi
$LS -vd $1 | $NAWK 'BEGIN {count=0}
(NR != 1)&&(/[0-9]:/) {count++}
END {print count}'
return 0
}
#
# Get specified number ACE content of specified file or directory.
#
# $1 file or directory name
# $2 specified number
#
function get_ACE #<file or dir name> <specified number> <verbose|compact>
{
if [[ ! -e $1 || $2 -ge $(count_ACE $1) ]]; then
return 1
fi
typeset file=$1
typeset -i num=$2
typeset format=${3:-verbose}
typeset -i next_num=-1
typeset tmpfile=/tmp/tmp_get_ACE.$$
typeset line=""
typeset args
case $format in
verbose) args="-vd"
;;
compact) args="-Vd"
;;
*) log_fail "Invalid parameter as ($format), " \
"only verbose|compact is supported."
;;
esac
$LS $args $file > $tmpfile
(( $? != 0 )) && log_fail "FAIL: $LS $args $file > $tmpfile"
while read line; do
[[ -z $line ]] && continue
if [[ $args == -vd ]]; then
if [[ $line == "$num":* ]]; then
(( next_num = num + 1 ))
fi
if [[ $line == "$next_num":* ]]; then
break
fi
if (( next_num != -1 )); then
print -n $line
fi
else
if (( next_num == num )); then
print -n $line
fi
(( next_num += 1 ))
fi
done < $tmpfile
$RM -f $tmpfile
(( $? != 0 )) && log_fail "FAIL: $RM -f $tmpfile"
}
#
# Cleanup exist user/group.
#
function cleanup_user_group
{
del_user $ZFS_ACL_ADMIN
del_user $ZFS_ACL_STAFF1
del_user $ZFS_ACL_STAFF2
del_group $ZFS_ACL_STAFF_GROUP
del_user $ZFS_ACL_OTHER1
del_user $ZFS_ACL_OTHER2
del_group $ZFS_ACL_OTHER_GROUP
return 0
}
#
# Clean up testfile and test directory
#
function cleanup
{
if [[ -d $TESTDIR ]]; then
cd $TESTDIR
$RM -rf $TESTDIR/*
fi
}
#
# According to specified access or acl_spec, do relevant operating by using the
# specified user.
#
# $1 specified user
# $2 node
# $3 acl_spec or access
#
function rwx_node #user node acl_spec|access
{
typeset user=$1
typeset node=$2
typeset acl_spec=$3
if [[ $user == "" || $node == "" || $acl_spec == "" ]]; then
log_note "node or acl_spec are not defined."
return 1
fi
if [[ -d $node ]]; then
case $acl_spec in
*:read_data:*|read_data)
chgusr_exec $user $LS -l $node > /dev/null 2>&1
return $? ;;
*:write_data:*|write_data)
if [[ -f ${node}/tmpfile ]]; then
log_must $RM -f ${node}/tmpfile
fi
chgusr_exec $user $TOUCH ${node}/tmpfile > \
/dev/null 2>&1
return $? ;;
*"execute:"*|execute)
chgusr_exec $user $FIND $node > /dev/null 2>&1
return $? ;;
esac
else
case $acl_spec in
*:read_data:*|read_data)
chgusr_exec $user $CAT $node > /dev/null 2>&1
return $? ;;
*:write_data:*|write_data)
chgusr_exec $user $DD if=/usr/bin/ls of=$node > \
/dev/null 2>&1
return $? ;;
*"execute:"*|execute)
ZFS_ACL_ERR_STR=$(chgusr_exec $user $node 2>&1)
return $? ;;
esac
fi
}
#
# Get the given file/directory xattr
#
# $1 object -- file or directroy
#
function get_xattr #<obj>
{
typeset obj=$1
typeset xattr
if (( ${#obj} == 0 )); then
return 1
fi
for xattr in `$RUNAT $obj $LS | \
/usr/xpg4/bin/egrep -v -e SUNWattr_ro -e SUNWattr_rw` ; do
$RUNAT $obj $SUM $xattr
done
}
#
# Get the owner of a file/directory
#
function get_owner #node
{
typeset node=$1
typeset value
if [[ -z $node ]]; then
log_fail "node are not defined."
fi
if [[ -d $node ]]; then
value=$($LS -dl $node | $AWK '{print $3}')
elif [[ -e $node ]]; then
value=$($LS -l $node | $AWK '{print $3}')
fi
$ECHO $value
}
#
# Get the group of a file/directory
#
function get_group #node
{
typeset node=$1
typeset value
if [[ -z $node ]]; then
log_fail "node are not defined."
fi
if [[ -d $node ]]; then
value=$($LS -dl $node | $AWK '{print $4}')
elif [[ -e $node ]]; then
value=$($LS -l $node | $AWK '{print $4}')
fi
$ECHO $value
}
#
# Get the group name that a UID belongs to
#
function get_user_group #uid
{
typeset uid=$1
typeset value
if [[ -z $uid ]]; then
log_fail "UID not defined."
fi
value=$(id $uid)
if [[ $? -eq 0 ]]; then
value=${value##*\(}
value=${value%%\)*}
$ECHO $value
else
log_fail "Invalid UID (uid)."
fi
}
#
# Get the specified item of the specified string
#
# $1: Item number, count from 0.
# $2-n: strings
#
function getitem
{
typeset -i n=$1
shift
(( n += 1 ))
eval echo \${$n}
}
#
# This function calculate the specified directory files checksum and write
# to the specified array.
#
# $1 directory in which the files will be cksum.
# $2 file array name which was used to store file cksum information.
# $3 attribute array name which was used to store attribute information.
#
function cksum_files #<dir> <file_array_name> <attribute_array_name>
{
typeset dir=$1
typeset farr_name=$2
typeset aarr_name=$3
[[ ! -d $dir ]] && return
typeset oldpwd=$PWD
cd $dir
typeset files=$($LS file*)
typeset -i i=0
typeset -i n=0
while (( i < NUM_FILE )); do
typeset f=$(getitem $i $files)
eval $farr_name[$i]=\$\(\$CKSUM $f\)
typeset -i j=0
while (( j < NUM_ATTR )); do
eval $aarr_name[$n]=\$\(\$RUNAT \$f \$CKSUM \
attribute.$j\)
(( j += 1 ))
(( n += 1 ))
done
(( i += 1 ))
done
cd $oldpwd
}
#
# This function compare two cksum results array.
#
# $1 The array name which stored the cksum before operation.
# $2 The array name which stored the cksum after operation.
#
function compare_cksum #<array1> <array2>
{
typeset before=$1
typeset after=$2
eval typeset -i count=\${#$before[@]}
typeset -i i=0
while (( i < count )); do
eval typeset var1=\${$before[$i]}
eval typeset var2=\${$after[$i]}
if [[ $var1 != $var2 ]]; then
return 1
fi
(( i += 1 ))
done
return 0
}
#
# This function calculate all the files cksum information in current directory
# and output them to the specified file.
#
# $1 directory from which the files will be cksum.
# $2 cksum output file
#
function record_cksum #<outfile>
{
typeset dir=$1
typeset outfile=$2
[[ ! -d ${outfile%/*} ]] && usr_exec $MKDIR -p ${outfile%/*}
usr_exec cd $dir ; $FIND . -depth -type f -exec cksum {} \\\; | \
$SORT > $outfile
usr_exec cd $dir ; $FIND . -depth -type f -xattr -exec runat {} \
cksum attribute* \\\; | $SORT >> $outfile
}
#
# The function create_files creates the directories and files that the script
# will operate on to test extended attribute functionality.
#
# $1 The base directory in which to create directories and files.
#
function create_files #<directory>
{
typeset basedir=$1
[[ ! -d $basedir ]] && usr_exec $MKDIR -m 777 $basedir
[[ ! -d $RES_DIR ]] && usr_exec $MKDIR -m 777 $RES_DIR
[[ ! -d $INI_DIR ]] && usr_exec $MKDIR -m 777 $INI_DIR
[[ ! -d $TST_DIR ]] && usr_exec $MKDIR -m 777 $TST_DIR
[[ ! -d $TMP_DIR ]] && usr_exec $MKDIR -m 777 $TMP_DIR
#
# Create the original file and its attribute files.
#
[[ ! -a $RES_DIR/file ]] && \
usr_exec $FILE_WRITE -o create -f $RES_DIR/file \
-b 1024 -d 0 -c 1
[[ ! -a $RES_DIR/attribute ]] && \
usr_exec $CP $RES_DIR/file $RES_DIR/attribute
typeset oldpwd=$PWD
cd $INI_DIR
typeset -i i=0
while (( i < NUM_FILE )); do
typeset dstfile=$INI_DIR/file.$$.$i
usr_exec $CP $RES_DIR/file $dstfile
typeset -i j=0
while (( j < NUM_ATTR )); do
usr_exec $RUNAT $dstfile \
$CP $RES_DIR/attribute ./attribute.$j
(( j += 1 ))
done
(( i += 1 ))
done
cd $oldpwd
}

View File

@ -0,0 +1,6 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/acl/posix
dist_pkgdata_SCRIPTS = \
cleanup.ksh \
setup.ksh \
posix_001_pos.ksh \
posix_002_pos.ksh

Some files were not shown because too many files have changed in this diff Show More