summaryrefslogtreecommitdiffstats
path: root/src/kernel/tests/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/tests/lib')
-rw-r--r--src/kernel/tests/lib/CMakeLists.txt70
-rw-r--r--src/kernel/tests/lib/cloner.c175
-rw-r--r--src/kernel/tests/lib/errnos.h186
-rw-r--r--src/kernel/tests/lib/get_path.c103
-rw-r--r--src/kernel/tests/lib/parse_opts.c621
-rw-r--r--src/kernel/tests/lib/random_range.c892
-rw-r--r--src/kernel/tests/lib/safe_file_ops.c422
-rw-r--r--src/kernel/tests/lib/safe_macros.c1109
-rw-r--r--src/kernel/tests/lib/safe_net.c459
-rw-r--r--src/kernel/tests/lib/safe_pthread.c43
-rw-r--r--src/kernel/tests/lib/safe_stdio.c97
-rw-r--r--src/kernel/tests/lib/self_exec.c225
-rw-r--r--src/kernel/tests/lib/signame.h106
-rw-r--r--src/kernel/tests/lib/tlibio.c2161
-rw-r--r--src/kernel/tests/lib/tst_af_alg.c212
-rw-r--r--src/kernel/tests/lib/tst_ansi_color.c64
-rw-r--r--src/kernel/tests/lib/tst_assert.c83
-rw-r--r--src/kernel/tests/lib/tst_buffers.c143
-rw-r--r--src/kernel/tests/lib/tst_capability.c90
-rw-r--r--src/kernel/tests/lib/tst_cgroup.c452
-rw-r--r--src/kernel/tests/lib/tst_checkpoint.c164
-rw-r--r--src/kernel/tests/lib/tst_checksum.c81
-rw-r--r--src/kernel/tests/lib/tst_clocks.c144
-rw-r--r--src/kernel/tests/lib/tst_cmd.c178
-rw-r--r--src/kernel/tests/lib/tst_coredump.c35
-rw-r--r--src/kernel/tests/lib/tst_cpu.c73
-rw-r--r--src/kernel/tests/lib/tst_crypto.c110
-rw-r--r--src/kernel/tests/lib/tst_device.c531
-rw-r--r--src/kernel/tests/lib/tst_dir_is_empty.c49
-rw-r--r--src/kernel/tests/lib/tst_fill_file.c118
-rw-r--r--src/kernel/tests/lib/tst_fill_fs.c68
-rw-r--r--src/kernel/tests/lib/tst_fs_has_free.c44
-rw-r--r--src/kernel/tests/lib/tst_fs_link_count.c183
-rw-r--r--src/kernel/tests/lib/tst_fs_setup.c49
-rw-r--r--src/kernel/tests/lib/tst_fs_type.c90
-rw-r--r--src/kernel/tests/lib/tst_get_bad_addr.c31
-rw-r--r--src/kernel/tests/lib/tst_hugepage.c61
-rw-r--r--src/kernel/tests/lib/tst_ioctl.c37
-rw-r--r--src/kernel/tests/lib/tst_kconfig.c285
-rw-r--r--src/kernel/tests/lib/tst_kernel.c100
-rw-r--r--src/kernel/tests/lib/tst_kvercmp.c195
-rw-r--r--src/kernel/tests/lib/tst_lockdown.c30
-rw-r--r--src/kernel/tests/lib/tst_memutils.c62
-rw-r--r--src/kernel/tests/lib/tst_mkfs.c117
-rw-r--r--src/kernel/tests/lib/tst_module.c124
-rw-r--r--src/kernel/tests/lib/tst_net.c221
-rw-r--r--src/kernel/tests/lib/tst_parse_opts.c36
-rw-r--r--src/kernel/tests/lib/tst_path_has_mnt_flags.c83
-rw-r--r--src/kernel/tests/lib/tst_pid.c64
-rw-r--r--src/kernel/tests/lib/tst_process_state.c86
-rw-r--r--src/kernel/tests/lib/tst_res.c606
-rw-r--r--src/kernel/tests/lib/tst_resource.c138
-rw-r--r--src/kernel/tests/lib/tst_safe_macros.c333
-rw-r--r--src/kernel/tests/lib/tst_safe_sysv_ipc.c145
-rw-r--r--src/kernel/tests/lib/tst_safe_timerfd.c56
-rw-r--r--src/kernel/tests/lib/tst_sig.c255
-rw-r--r--src/kernel/tests/lib/tst_sig_proc.c31
-rw-r--r--src/kernel/tests/lib/tst_status.c50
-rw-r--r--src/kernel/tests/lib/tst_supported_fs_types.c111
-rw-r--r--src/kernel/tests/lib/tst_sys_conf.c105
-rw-r--r--src/kernel/tests/lib/tst_taint.c107
-rw-r--r--src/kernel/tests/lib/tst_test.c1387
-rw-r--r--src/kernel/tests/lib/tst_timer.c59
-rw-r--r--src/kernel/tests/lib/tst_timer_test.c472
-rw-r--r--src/kernel/tests/lib/tst_tmpdir.c347
-rw-r--r--src/kernel/tests/lib/tst_virt.c132
-rw-r--r--src/kernel/tests/lib/tst_wallclock.c60
67 files changed, 15556 insertions, 0 deletions
diff --git a/src/kernel/tests/lib/CMakeLists.txt b/src/kernel/tests/lib/CMakeLists.txt
new file mode 100644
index 0000000..0ce8982
--- /dev/null
+++ b/src/kernel/tests/lib/CMakeLists.txt
@@ -0,0 +1,70 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(xloop-kernel-test-lib)
+
+add_library(libltp STATIC ${CMAKE_CURRENT_SOURCE_DIR}/cloner.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/get_path.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/parse_opts.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/random_range.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/safe_file_ops.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/safe_macros.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/safe_net.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/safe_pthread.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/safe_stdio.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/self_exec.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tlibio.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_af_alg.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_ansi_color.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_assert.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_buffers.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_capability.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_cgroup.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_checkpoint.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_checksum.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_clocks.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_cmd.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_coredump.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_cpu.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_crypto.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_device.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_dir_is_empty.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_fill_file.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_fill_fs.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_fs_has_free.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_fs_link_count.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_fs_setup.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_fs_type.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_get_bad_addr.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_hugepage.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_ioctl.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_kconfig.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_kernel.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_kvercmp.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_lockdown.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_memutils.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_mkfs.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_module.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_net.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_parse_opts.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_path_has_mnt_flags.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_pid.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_process_state.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_res.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_resource.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_safe_macros.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_safe_sysv_ipc.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_safe_timerfd.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_sig.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_sig_proc.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_status.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_supported_fs_types.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_sys_conf.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_taint.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_test.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_timer.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_timer_test.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_tmpdir.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_virt.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_wallclock.c)
+target_include_directories(libltp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_compile_options(libltp PUBLIC "-Wno-deprecated-declarations")
diff --git a/src/kernel/tests/lib/cloner.c b/src/kernel/tests/lib/cloner.c
new file mode 100644
index 0000000..11401f2
--- /dev/null
+++ b/src/kernel/tests/lib/cloner.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2009
+ * Some wrappers for clone functionality. Thrown together by Serge Hallyn
+ * <serue@us.ibm.com> based on existing clone usage in ltp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <stdarg.h>
+#include "config.h"
+#include "tst_clone.h"
+
+#undef clone /* we want to use clone() */
+
+/*
+ * The ia64 port has never included a prototype for __clone2(). It was updated
+ * to take eight parameters in glibc commit:
+ *
+ * commit 625f22fc7f8e0d61e3e6cff2c65468b91dbad426
+ * Author: Ulrich Drepper <drepper@redhat.com>
+ * Date: Mon Mar 3 19:53:27 2003 +0000
+ *
+ * The first release that contained this commit was glibc-2.3.3 which is old
+ * enough to assume that __clone2() takes eight parameters.
+ */
+#if defined(__ia64__)
+extern int __clone2(int (*fn) (void *arg), void *child_stack_base,
+ size_t child_stack_size, int flags, void *arg,
+ pid_t *parent_tid, void *tls, pid_t *child_tid);
+#endif
+
+#ifndef CLONE_SUPPORTS_7_ARGS
+# define clone(fn, stack, flags, arg, ptid, tls, ctid) \
+ clone(fn, stack, flags, arg)
+#endif
+
+/*
+ * ltp_clone: wrapper for clone to hide the architecture dependencies.
+ * 1. hppa takes bottom of stack and no stacksize (stack grows up)
+ * 2. __ia64__ takes bottom of stack and uses clone2
+ * 3. all others take top of stack (stack grows down)
+ */
+static int
+ltp_clone_(unsigned long flags, int (*fn)(void *arg), void *arg,
+ size_t stack_size, void *stack, pid_t *ptid, void *tls, pid_t *ctid)
+{
+ int ret;
+
+#if defined(__ia64__)
+ ret = __clone2(fn, stack, stack_size, flags, arg, ptid, tls, ctid);
+#else
+# if defined(__hppa__) || defined(__metag__)
+ /*
+ * These arches grow their stack up, so don't need to adjust the base.
+ * XXX: This should be made into a runtime test.
+ */
+# else
+ /*
+ * For archs where stack grows downwards, stack points to the topmost
+ * address of the memory space set up for the child stack.
+ */
+ if (stack)
+ stack += stack_size;
+# endif
+
+ ret = clone(fn, stack, flags, arg, ptid, tls, ctid);
+#endif
+
+ return ret;
+}
+
+int ltp_clone(unsigned long flags, int (*fn)(void *arg), void *arg,
+ size_t stack_size, void *stack)
+{
+ return ltp_clone_(flags, fn, arg, stack_size, stack, NULL, NULL, NULL);
+}
+
+int ltp_clone7(unsigned long flags, int (*fn)(void *arg), void *arg,
+ size_t stack_size, void *stack, ...)
+{
+ pid_t *ptid, *ctid;
+ void *tls;
+ va_list arg_clone;
+
+ va_start(arg_clone, stack);
+ ptid = va_arg(arg_clone, pid_t *);
+ tls = va_arg(arg_clone, void *);
+ ctid = va_arg(arg_clone, pid_t *);
+ va_end(arg_clone);
+
+#ifdef CLONE_SUPPORTS_7_ARGS
+ return ltp_clone_(flags, fn, arg, stack_size, stack, ptid, tls, ctid);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+/*
+ * ltp_alloc_stack: allocate stack of size 'size', that is sufficiently
+ * aligned for all arches. User is responsible for freeing allocated
+ * memory.
+ * Returns pointer to new stack. On error, returns NULL with errno set.
+ */
+void *ltp_alloc_stack(size_t size)
+{
+ void *ret = NULL;
+ int err;
+
+ err = posix_memalign(&ret, 64, size);
+ if (err)
+ errno = err;
+
+ return ret;
+}
+
+/*
+ * ltp_clone_alloc: also does the memory allocation for clone with a
+ * caller-specified size.
+ */
+int
+ltp_clone_alloc(unsigned long clone_flags, int (*fn) (void *arg), void *arg,
+ size_t stack_size)
+{
+ void *stack;
+ int ret;
+ int saved_errno;
+
+ stack = ltp_alloc_stack(stack_size);
+ if (stack == NULL)
+ return -1;
+
+ ret = ltp_clone(clone_flags, fn, arg, stack_size, stack);
+
+ if (ret == -1) {
+ saved_errno = errno;
+ free(stack);
+ errno = saved_errno;
+ }
+
+ return ret;
+}
+
+/*
+ * ltp_clone_quick: calls ltp_clone_alloc with predetermined stack size.
+ * Experience thus far suggests that one page is often insufficient,
+ * while 6*getpagesize() seems adequate.
+ */
+int ltp_clone_quick(unsigned long clone_flags, int (*fn) (void *arg), void *arg)
+{
+ size_t stack_size = getpagesize() * 6;
+
+ return ltp_clone_alloc(clone_flags, fn, arg, stack_size);
+}
diff --git a/src/kernel/tests/lib/errnos.h b/src/kernel/tests/lib/errnos.h
new file mode 100644
index 0000000..df8fea8
--- /dev/null
+++ b/src/kernel/tests/lib/errnos.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2009-2013 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ */
+
+const char *tst_strerrno(int err)
+{
+ static const struct pair errno_pairs[] = {
+ STRPAIR(0, "SUCCESS")
+ /* asm-generic/errno-base.h */
+ PAIR(EPERM)
+ PAIR(ENOENT)
+ PAIR(ESRCH)
+ PAIR(EINTR)
+ PAIR(EIO)
+ PAIR(ENXIO)
+ PAIR(E2BIG)
+ PAIR(ENOEXEC)
+ PAIR(EBADF)
+ PAIR(ECHILD)
+ STRPAIR(EAGAIN, "EAGAIN/EWOULDBLOCK")
+ PAIR(ENOMEM)
+ PAIR(EACCES)
+ PAIR(EFAULT)
+ PAIR(ENOTBLK)
+ PAIR(EBUSY)
+ PAIR(EEXIST)
+ PAIR(EXDEV)
+ PAIR(ENODEV)
+ PAIR(ENOTDIR)
+ PAIR(EISDIR)
+ PAIR(EINVAL)
+ PAIR(ENFILE)
+ PAIR(EMFILE)
+ PAIR(ENOTTY)
+ PAIR(ETXTBSY)
+ PAIR(EFBIG)
+ PAIR(ENOSPC)
+ PAIR(ESPIPE)
+ PAIR(EROFS)
+ PAIR(EMLINK)
+ PAIR(EPIPE)
+ PAIR(EDOM)
+ PAIR(ERANGE)
+ /* asm-generic/errno.h */
+ PAIR(EDEADLK)
+ PAIR(ENAMETOOLONG)
+ PAIR(ENOLCK)
+ PAIR(ENOSYS)
+ PAIR(ENOTEMPTY)
+ PAIR(ELOOP)
+ /* EWOULDBLOCK == EAGAIN skipped */
+ PAIR(ENOMSG)
+ PAIR(EIDRM)
+ PAIR(ECHRNG)
+ PAIR(EL2NSYNC)
+ PAIR(EL3HLT)
+ PAIR(EL3RST)
+ PAIR(ELNRNG)
+ PAIR(EUNATCH)
+ PAIR(ENOCSI)
+ PAIR(EL2HLT)
+ PAIR(EBADE)
+ PAIR(EBADR)
+ PAIR(EXFULL)
+ PAIR(ENOANO)
+ PAIR(EBADRQC)
+ PAIR(EBADSLT)
+ /* EDEADLOCK == EDEADLK skipped */
+ PAIR(EBFONT)
+ PAIR(ENOSTR)
+ PAIR(ENODATA)
+ PAIR(ETIME)
+ PAIR(ENOSR)
+ PAIR(ENONET)
+ PAIR(ENOPKG)
+ PAIR(EREMOTE)
+ PAIR(ENOLINK)
+ PAIR(EADV)
+ PAIR(ESRMNT)
+ PAIR(ECOMM)
+ PAIR(EPROTO)
+ PAIR(EMULTIHOP)
+ PAIR(EDOTDOT)
+ PAIR(EBADMSG)
+ PAIR(EOVERFLOW)
+ PAIR(ENOTUNIQ)
+ PAIR(EBADFD)
+ PAIR(EREMCHG)
+ PAIR(ELIBACC)
+ PAIR(ELIBBAD)
+ PAIR(ELIBSCN)
+ PAIR(ELIBMAX)
+ PAIR(ELIBEXEC)
+ PAIR(EILSEQ)
+ PAIR(ERESTART)
+ PAIR(ESTRPIPE)
+ PAIR(EUSERS)
+ PAIR(ENOTSOCK)
+ PAIR(EDESTADDRREQ)
+ PAIR(EMSGSIZE)
+ PAIR(EPROTOTYPE)
+ PAIR(ENOPROTOOPT)
+ PAIR(EPROTONOSUPPORT)
+ PAIR(ESOCKTNOSUPPORT)
+ PAIR(EOPNOTSUPP)
+ PAIR(EPFNOSUPPORT)
+ PAIR(EAFNOSUPPORT)
+ PAIR(EADDRINUSE)
+ PAIR(EADDRNOTAVAIL)
+ PAIR(ENETDOWN)
+ PAIR(ENETUNREACH)
+ PAIR(ENETRESET)
+ PAIR(ECONNABORTED)
+ PAIR(ECONNRESET)
+ PAIR(ENOBUFS)
+ PAIR(EISCONN)
+ PAIR(ENOTCONN)
+ PAIR(ESHUTDOWN)
+ PAIR(ETOOMANYREFS)
+ PAIR(ETIMEDOUT)
+ PAIR(ECONNREFUSED)
+ PAIR(EHOSTDOWN)
+ PAIR(EHOSTUNREACH)
+ PAIR(EALREADY)
+ PAIR(EINPROGRESS)
+ PAIR(ESTALE)
+ PAIR(EUCLEAN)
+ PAIR(ENOTNAM)
+ PAIR(ENAVAIL)
+ PAIR(EISNAM)
+ PAIR(EREMOTEIO)
+ PAIR(EDQUOT)
+ PAIR(ENOMEDIUM)
+ PAIR(EMEDIUMTYPE)
+ PAIR(ECANCELED)
+#ifdef ENOKEY
+ PAIR(ENOKEY)
+#endif
+#ifdef EKEYEXPIRED
+ PAIR(EKEYEXPIRED)
+#endif
+#ifdef EKEYREVOKED
+ PAIR(EKEYREVOKED)
+#endif
+#ifdef EKEYREJECTED
+ PAIR(EKEYREJECTED)
+#endif
+#ifdef EOWNERDEAD
+ PAIR(EOWNERDEAD)
+#endif
+#ifdef ENOTRECOVERABLE
+ PAIR(ENOTRECOVERABLE)
+#endif
+#ifdef ERFKILL
+ PAIR(ERFKILL)
+#endif
+#ifdef EHWPOISON
+ PAIR(EHWPOISON)
+#endif
+ };
+
+ PAIR_LOOKUP(errno_pairs, err);
+}
diff --git a/src/kernel/tests/lib/get_path.c b/src/kernel/tests/lib/get_path.c
new file mode 100644
index 0000000..aafbc2c
--- /dev/null
+++ b/src/kernel/tests/lib/get_path.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 Cyril Hrubis chrubis@suse.cz
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+ /*
+ * Looks for binary prog_name in $PATH.
+ *
+ * If such file exists and if you are able at least to read it, zero is
+ * returned and absolute path to the file is filled into buf. In case buf is
+ * too short to hold the absolute path + prog_name for the file we are looking
+ * for -1 is returned as well as when there is no such file in all paths in
+ * $PATH.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "test.h"
+
+static int file_exist(const char *path)
+{
+ struct stat st;
+
+ if (!access(path, R_OK) && !stat(path, &st) && S_ISREG(st.st_mode))
+ return 1;
+
+ return 0;
+}
+
+int tst_get_path(const char *prog_name, char *buf, size_t buf_len)
+{
+ const char *path = (const char *)getenv("PATH");
+ const char *start = path;
+ const char *end;
+ size_t size, ret;
+
+ if (path == NULL)
+ return -1;
+
+ do {
+ end = strchr(start, ':');
+
+ if (end != NULL)
+ snprintf(buf, MIN(buf_len, (size_t) (end - start + 1)),
+ "%s", start);
+ else
+ snprintf(buf, buf_len, "%s", start);
+
+ size = strlen(buf);
+
+ /*
+ * "::" inside $PATH, $PATH ending with ':' or $PATH starting
+ * with ':' should be expanded into current working directory.
+ */
+ if (size == 0) {
+ snprintf(buf, buf_len, ".");
+ size = strlen(buf);
+ }
+
+ /*
+ * If there is no '/' ad the end of path from $PATH add it.
+ */
+ if (buf[size - 1] != '/')
+ ret =
+ snprintf(buf + size, buf_len - size, "/%s",
+ prog_name);
+ else
+ ret =
+ snprintf(buf + size, buf_len - size, "%s",
+ prog_name);
+
+ if (buf_len - size > ret && file_exist(buf))
+ return 0;
+
+ start = end + 1;
+
+ } while (end != NULL);
+
+ return -1;
+}
diff --git a/src/kernel/tests/lib/parse_opts.c b/src/kernel/tests/lib/parse_opts.c
new file mode 100644
index 0000000..a9d5058
--- /dev/null
+++ b/src/kernel/tests/lib/parse_opts.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ * AUTHOR : William Roske/Richard Logan
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdint.h>
+
+#include "test.h"
+#include "ltp_priv.h"
+#include "usctest.h"
+#include "tst_clocks.h"
+
+#ifndef UNIT_TEST
+#define UNIT_TEST 0
+#endif
+
+/* Define flags and args for standard options */
+static int STD_INFINITE = 0; /* flag indciating to loop forever */
+int STD_LOOP_COUNT = 1; /* number of iterations */
+
+static float STD_LOOP_DURATION = 0.0; /* duration value in fractional seconds */
+
+static char **STD_opt_arr = NULL; /* array of option strings */
+static int STD_argind = 1; /* argv index to next argv element */
+ /* (first argument) */
+ /* To getopt users, it is like optind */
+
+/*
+ * The following variables are to support system testing additions.
+ */
+static int STD_TP_barrier = 0; /* flag to do barrier in TEST_PAUSE */
+ /* 2 - wait_barrier(), 3 - set_barrier(), * - barrier() */
+static int STD_LP_barrier = 0; /* flag to do barrier in TEST_LOOPING */
+ /* 2 - wait_barrier(), 3 - set_barrier(), * - barrier() */
+static int STD_TP_shmem_sz = 0; /* shmalloc this many words per pe in TEST_PAUSE */
+static int STD_LD_shmem = 0; /* flag to do shmem_puts and shmem_gets during delay */
+static int STD_LP_shmem = 0; /* flag to do shmem_puts and gets during TEST_LOOPING */
+static int STD_LD_recfun = 0; /* do recressive function calls in loop delay */
+static int STD_LP_recfun = 0; /* do recressive function calls in TEST_LOOPING */
+static int STD_TP_sbrk = 0; /* do sbrk in TEST_PAUSE */
+static int STD_LP_sbrk = 0; /* do sbrk in TEST_LOOPING */
+static char *STD_start_break = 0; /* original sbrk size */
+static int Debug = 0;
+
+static struct std_option_t {
+ char *optstr;
+ char *help;
+ char *flag;
+ char **arg;
+} std_options[] = {
+ {"h", " -h Show this help screen\n", NULL, NULL},
+ {"i:", " -i n Execute test n times\n", NULL, NULL},
+ {"I:", " -I x Execute test for x seconds\n", NULL, NULL},
+#ifdef UCLINUX
+ {"C:",
+ " -C ARG Run the child process with arguments ARG (for internal use)\n",
+ NULL, NULL},
+#endif
+ {NULL, NULL, NULL, NULL}
+};
+
+/*
+ * Structure for usc_recressive_func argument
+ */
+struct usc_bigstack_t {
+ char space[4096];
+};
+
+static struct usc_bigstack_t *STD_bigstack = NULL;
+
+/* define the string length for Mesg and Mesg2 strings */
+#define STRLEN 2048
+
+static char Mesg2[STRLEN]; /* holds possible return string */
+static void usc_recressive_func();
+
+/*
+ * Define bits for options that might have env variable default
+ */
+#define OPT_iteration 01
+#define OPT_duration 04
+#define OPT_delay 010
+
+#ifdef UCLINUX
+/* Allocated and used in self_exec.c: */
+extern char *child_args; /* Arguments to child when -C is used */
+#endif
+
+static void print_help(void (*user_help)(void))
+{
+ int i;
+
+ for (i = 0; std_options[i].optstr; ++i) {
+ if (std_options[i].help)
+ printf("%s", std_options[i].help);
+ }
+
+ if (user_help)
+ user_help();
+}
+
+/**********************************************************************
+ * parse_opts:
+ **********************************************************************/
+const char *parse_opts(int ac, char **av, const option_t * user_optarr,
+ void (*uhf)(void))
+{
+ int found; /* flag to indicate that an option specified was */
+ /* found in the user's list */
+ int k; /* scratch integer for returns and short time usage */
+ float ftmp; /* tmp float for parsing env variables */
+ char *ptr; /* used in getting env variables */
+ int options = 0; /* no options specified */
+ int optstrlen, i;
+ char *optionstr;
+ int opt;
+
+ /*
+ * If not the first time this function is called, release the old STD_opt_arr
+ * vector.
+ */
+ if (STD_opt_arr != NULL) {
+ free(STD_opt_arr);
+ STD_opt_arr = NULL;
+ }
+ /* Calculate how much space we need for the option string */
+ optstrlen = 0;
+ for (i = 0; std_options[i].optstr; ++i)
+ optstrlen += strlen(std_options[i].optstr);
+ if (user_optarr)
+ for (i = 0; user_optarr[i].option; ++i) {
+ if (strlen(user_optarr[i].option) > 2)
+ return
+ "parse_opts: ERROR - Only short options are allowed";
+ optstrlen += strlen(user_optarr[i].option);
+ }
+ optstrlen += 1;
+
+ /* Create the option string for getopt */
+ optionstr = malloc(optstrlen);
+ if (!optionstr)
+ return
+ "parse_opts: ERROR - Could not allocate memory for optionstr";
+
+ optionstr[0] = '\0';
+
+ for (i = 0; std_options[i].optstr; ++i)
+ strcat(optionstr, std_options[i].optstr);
+ if (user_optarr)
+ for (i = 0; user_optarr[i].option; ++i)
+ /* only add the option if it wasn't there already */
+ if (strchr(optionstr, user_optarr[i].option[0]) == NULL)
+ strcat(optionstr, user_optarr[i].option);
+
+ /*
+ * Loop through av parsing options.
+ */
+ while ((opt = getopt(ac, av, optionstr)) > 0) {
+
+ STD_argind = optind;
+
+ switch (opt) {
+ case '?': /* Unknown option */
+ return "Unknown option";
+ break;
+ case ':': /* Missing Arg */
+ return "Missing argument";
+ break;
+ case 'i': /* Iterations */
+ options |= OPT_iteration;
+ STD_LOOP_COUNT = atoi(optarg);
+ if (STD_LOOP_COUNT == 0)
+ STD_INFINITE = 1;
+ break;
+ case 'I': /* Time duration */
+ options |= OPT_duration;
+ STD_LOOP_DURATION = atof(optarg);
+ if (STD_LOOP_DURATION == 0.0)
+ STD_INFINITE = 1;
+ break;
+ case 'h': /* Help */
+ print_help(uhf);
+ exit(0);
+ break;
+#ifdef UCLINUX
+ case 'C': /* Run child */
+ child_args = optarg;
+ break;
+#endif
+ default:
+
+ /* Check all the user specified options */
+ found = 0;
+ for (i = 0; user_optarr[i].option; ++i) {
+
+ if (opt == user_optarr[i].option[0]) {
+ /* Yup, This is a user option, set the flag and look for argument */
+ if (user_optarr[i].flag) {
+ *user_optarr[i].flag = 1;
+ }
+ found++;
+
+ /* save the argument at the user's location */
+ if (user_optarr[i].
+ option[strlen(user_optarr[i].option)
+ - 1] == ':') {
+ *user_optarr[i].arg = optarg;
+ }
+ break; /* option found - break out of the for loop */
+ }
+ }
+ /* This condition "should never happen". SO CHECK FOR IT!!!! */
+ if (!found) {
+ sprintf(Mesg2,
+ "parse_opts: ERROR - option:\"%c\" NOT FOUND... INTERNAL "
+ "ERROR", opt);
+ return (Mesg2);
+ }
+ }
+
+ }
+ free(optionstr);
+
+ STD_argind = optind;
+
+ /*
+ * Turn on debug
+ */
+ if (getenv("USC_DEBUG") != NULL) {
+ Debug = 1;
+ printf("env USC_DEBUG is defined, turning on debug\n");
+ }
+ if (getenv("USC_VERBOSE") != NULL) {
+ Debug = 1;
+ printf("env USC_VERBOSE is defined, turning on debug\n");
+ }
+
+ /*
+ * If the USC_ITERATION_ENV environmental variable is set to
+ * a number, use that number as iteration count (same as -c option).
+ * The -c option with arg will be used even if this env var is set.
+ */
+ if (!(options & OPT_iteration)
+ && (ptr = getenv(USC_ITERATION_ENV)) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1) {
+ if (k == 0) { /* if arg is 0, set infinite loop flag */
+ STD_INFINITE = 1;
+ if (Debug)
+ printf
+ ("Using env %s, set STD_INFINITE to 1\n",
+ USC_ITERATION_ENV);
+ } else { /* else, set the loop count to the arguement */
+ STD_LOOP_COUNT = k;
+ if (Debug)
+ printf
+ ("Using env %s, set STD_LOOP_COUNT to %d\n",
+ USC_ITERATION_ENV, k);
+ }
+ }
+ }
+
+ /*
+ * If the USC_LOOP_WALLTIME environmental variable is set,
+ * use that number as duration (same as -I option).
+ * The -I option with arg will be used even if this env var is set.
+ */
+
+ if (!(options & OPT_duration) &&
+ (ptr = getenv(USC_LOOP_WALLTIME)) != NULL) {
+ if (sscanf(ptr, "%f", &ftmp) == 1 && ftmp >= 0.0) {
+ STD_LOOP_DURATION = ftmp;
+ if (Debug)
+ printf
+ ("Using env %s, set STD_LOOP_DURATION to %f\n",
+ USC_LOOP_WALLTIME, ftmp);
+ if (STD_LOOP_DURATION == 0.0) { /* if arg is 0, set infinite loop flag */
+ STD_INFINITE = 1;
+ if (Debug)
+ printf
+ ("Using env %s, set STD_INFINITE to 1\n",
+ USC_LOOP_WALLTIME);
+ }
+ }
+ }
+ if (!(options & OPT_duration) && (ptr = getenv("USC_DURATION")) != NULL) {
+ if (sscanf(ptr, "%f", &ftmp) == 1 && ftmp >= 0.0) {
+ STD_LOOP_DURATION = ftmp;
+ if (Debug)
+ printf
+ ("Using env USC_DURATION, set STD_LOOP_DURATION to %f\n",
+ ftmp);
+ if (STD_LOOP_DURATION == 0.0) { /* if arg is 0, set infinite loop flag */
+ STD_INFINITE = 1;
+ if (Debug)
+ printf
+ ("Using env USC_DURATION, set STD_INFINITE to 1\n");
+ }
+ }
+ }
+
+ /*
+ * The following are special system testing envs to turn on special
+ * hooks in the code.
+ */
+ if ((ptr = getenv("USC_TP_BARRIER")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0)
+ STD_TP_barrier = k;
+ else
+ STD_TP_barrier = 1;
+ if (Debug)
+ printf
+ ("using env USC_TP_BARRIER, Set STD_TP_barrier to %d\n",
+ STD_TP_barrier);
+ }
+
+ if ((ptr = getenv("USC_LP_BARRIER")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0)
+ STD_LP_barrier = k;
+ else
+ STD_LP_barrier = 1;
+ if (Debug)
+ printf
+ ("using env USC_LP_BARRIER, Set STD_LP_barrier to %d\n",
+ STD_LP_barrier);
+ }
+
+ if ((ptr = getenv("USC_TP_SHMEM")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0) {
+ STD_TP_shmem_sz = k;
+ if (Debug)
+ printf
+ ("Using env USC_TP_SHMEM, Set STD_TP_shmem_sz to %d\n",
+ STD_TP_shmem_sz);
+ }
+ }
+
+ if ((ptr = getenv("USC_LP_SHMEM")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0) {
+ STD_LP_shmem = k;
+ if (Debug)
+ printf
+ ("Using env USC_LP_SHMEM, Set STD_LP_shmem to %d\n",
+ STD_LP_shmem);
+ }
+ }
+
+ if ((ptr = getenv("USC_LD_SHMEM")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0) {
+ STD_LD_shmem = k;
+ if (Debug)
+ printf
+ ("Using env USC_LD_SHMEM, Set STD_LD_shmem to %d\n",
+ STD_LD_shmem);
+ }
+ }
+
+ if ((ptr = getenv("USC_TP_SBRK")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0) {
+ STD_TP_sbrk = k;
+ if (Debug)
+ printf
+ ("Using env USC_TP_SBRK, Set STD_TP_sbrk to %d\n",
+ STD_TP_sbrk);
+ }
+ }
+#if !defined(UCLINUX)
+ if ((ptr = getenv("USC_LP_SBRK")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0) {
+ STD_LP_sbrk = k;
+ if (Debug)
+ printf
+ ("Using env USC_LP_SBRK, Set STD_LP_sbrk to %d\n",
+ STD_LP_sbrk);
+ }
+ }
+#endif /* if !defined(UCLINUX) */
+
+ if ((ptr = getenv("USC_LP_RECFUN")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0) {
+ STD_LP_recfun = k;
+ if (STD_bigstack != NULL)
+ STD_bigstack =
+ malloc(sizeof(struct usc_bigstack_t));
+ if (Debug)
+ printf
+ ("Using env USC_LP_RECFUN, Set STD_LP_recfun to %d\n",
+ STD_LP_recfun);
+ }
+ }
+
+ if ((ptr = getenv("USC_LD_RECFUN")) != NULL) {
+ if (sscanf(ptr, "%i", &k) == 1 && k >= 0) {
+ STD_LD_recfun = k;
+ if (STD_bigstack != NULL)
+ STD_bigstack =
+ malloc(sizeof(struct usc_bigstack_t));
+ if (Debug)
+ printf
+ ("Using env USC_LD_RECFUN, Set STD_LD_recfun to %d\n",
+ STD_LD_recfun);
+ }
+ }
+#if UNIT_TEST
+ printf("The following variables after option and env parsing:\n");
+ printf("STD_LOOP_DURATION = %f\n", STD_LOOP_DURATION);
+ printf("STD_LOOP_COUNT = %d\n", STD_LOOP_COUNT);
+ printf("STD_INFINITE = %d\n", STD_INFINITE);
+#endif
+
+ return NULL;
+}
+
+/***********************************************************************
+ * This function will do desired end of global setup test
+ * hooks.
+ ***********************************************************************/
+int usc_global_setup_hook(void)
+{
+#ifndef UCLINUX
+ if (STD_TP_sbrk || STD_LP_sbrk)
+ STD_start_break = sbrk(0); /* get original sbreak size */
+
+ if (STD_TP_sbrk) {
+ sbrk(STD_TP_sbrk);
+ if (Debug)
+ printf("after sbrk(%d)\n", STD_TP_sbrk);
+ }
+#endif
+ return 0;
+}
+
+#define USECS_PER_SEC 1000000 /* microseconds per second */
+
+static uint64_t get_current_time(void)
+{
+ struct timespec ts;
+
+ tst_clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ return (((uint64_t) ts.tv_sec) * USECS_PER_SEC) + ts.tv_nsec / 1000;
+}
+
+/***********************************************************************
+ *
+ * This function will determine if test should continue iterating
+ * If the STD_INFINITE flag is set, return 1.
+ * If the STD_LOOP_COUNT variable is set, compare it against
+ * the counter.
+ * If the STD_LOOP_DURATION variable is set, compare current time against
+ * calculated stop_time.
+ * This function will return 1 until all desired looping methods
+ * have been met.
+ *
+ * counter integer is supplied by the user program.
+ ***********************************************************************/
+int usc_test_looping(int counter)
+{
+ static int first_time = 1;
+ static uint64_t stop_time = 0;
+ int keepgoing = 0;
+
+ /*
+ * If this is the first iteration and we are looping for
+ * duration of STD_LOOP_DURATION seconds (fractional) or
+ * doing loop delays, get the clocks per second.
+ */
+ if (first_time) {
+ first_time = 0;
+
+ /*
+ * If looping for duration, calculate stop time in
+ * clocks.
+ */
+ if (STD_LOOP_DURATION) {
+ stop_time =
+ (uint64_t) (USECS_PER_SEC * STD_LOOP_DURATION)
+ + get_current_time();
+ }
+ }
+
+ if (STD_INFINITE)
+ keepgoing++;
+
+ if (STD_LOOP_COUNT && counter < STD_LOOP_COUNT)
+ keepgoing++;
+
+ if (STD_LOOP_DURATION != 0.0 && get_current_time() < stop_time)
+ keepgoing++;
+
+ if (keepgoing == 0)
+ return 0;
+
+ /*
+ * The following code allows special system testing hooks.
+ */
+
+ if (STD_LP_recfun) {
+ if (Debug)
+ printf
+ ("calling usc_recressive_func(0, %d, *STD_bigstack)\n",
+ STD_LP_recfun);
+ usc_recressive_func(0, STD_LP_recfun, *STD_bigstack);
+ }
+#if !defined(UCLINUX)
+ if (STD_LP_sbrk) {
+ if (Debug)
+ printf("about to do sbrk(%d)\n", STD_LP_sbrk);
+ sbrk(STD_LP_sbrk);
+ }
+#endif
+
+ if (keepgoing)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * This function recressively calls itself max times.
+ */
+static void usc_recressive_func(int cnt, int max, struct usc_bigstack_t bstack)
+{
+ if (cnt < max)
+ usc_recressive_func(cnt + 1, max, bstack);
+
+}
+
+#if UNIT_TEST
+
+/******************************************************************************
+ * UNIT TEST CODE
+ * UNIT TEST CODE
+ *
+ * this following code is provide so that unit testing can
+ * be done fairly easily.
+ ******************************************************************************/
+
+int Help = 0;
+int Help2 = 0;
+char *ptr;
+
+long TEST_RETURN;
+int TEST_ERRNO;
+
+/* for test specific parse_opts options */
+option_t Options[] = {
+ {"help", &Help2, NULL}, /* -help option */
+ {"h", &Help, NULL}, /* -h option */
+
+#if INVALID_TEST_CASES
+ {"missingflag", NULL, &ptr}, /* error */
+ {"missingarg:", &Help, NULL}, /* error */
+#endif /* INVALID_TEST_CASES */
+
+ {NULL, NULL, NULL}
+};
+
+int main(int argc, char **argv)
+{
+ int lc;
+ char *msg;
+ struct timeval t;
+ int cnt;
+
+ if ((msg = parse_opts(argc, argv, Options, NULL)) != NULL) {
+ printf("ERROR: %s\n", msg);
+ exit(1);
+ }
+
+ TEST_PAUSE;
+
+ for (lc = 0; TEST_LOOPING(lc); lc++) {
+
+ TEST(gettimeofday(&t, NULL));
+ printf("iter=%d: sec:%d, usec:%6.6d %s", lc + 1, t.tv_sec,
+ t.tv_usec, ctime(&t.tv_sec));
+ }
+
+ TEST_CLEANUP;
+
+ exit(0);
+}
+
+#endif /* UNIT_TEST */
diff --git a/src/kernel/tests/lib/random_range.c b/src/kernel/tests/lib/random_range.c
new file mode 100644
index 0000000..510a4a1
--- /dev/null
+++ b/src/kernel/tests/lib/random_range.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include "random_range.h"
+
+/*
+ * Internal format of the range array set up by parse_range()
+ */
+
+struct range {
+ int min;
+ int max;
+ int mult;
+};
+
+/*
+ * parse_ranges() is a function to parse a comma-separated list of range
+ * tokens each having the following form:
+ *
+ * num
+ * or
+ * min:max[:mult]
+ *
+ * any of the values may be blank (ie. min::mult, :max, etc.) and default
+ * values for missing arguments may be supplied by the caller.
+ *
+ * The special first form is short hand for 'num:num'.
+ *
+ * After parsing the string, the ranges are put into an array of integers,
+ * which is malloc'd by the routine. The min, max, and mult entries of each
+ * range can be extracted from the array using the range_min(), range_max(),
+ * and range_mult() functions.
+ *
+ * It is the responsibility of the caller to free the space allocated by
+ * parse_ranges() - a single call to free() will free the space.
+ *
+ * str The string to parse - assumed to be a comma-separated
+ * list of tokens having the above format.
+ * defmin default value to plug in for min, if it is missing
+ * defmax default value to plug in for max, if it is missing
+ * defmult default value to plug in for mult, if missing
+ * parse_func A user-supplied function pointer, which parse_ranges()
+ * can call to parse the min, max, and mult strings. This
+ * allows for customized number formats. The function
+ * MUST have the following prototype:
+ * parse_func(char *str, int *val)
+ * The function should return -1 if str cannot be parsed
+ * into an integer, or >= 0 if it was successfully
+ * parsed. The resulting integer will be stored in
+ * *val. If parse_func is NULL, parse_ranges will parse
+ * the tokens in a manner consistent with the the sscanf
+ * %i format.
+ * range_ptr A user-supplied char **, which will be set to point
+ * at malloc'd space which holds the parsed range
+ * values. If range_ptr is NULL, parse_ranges() just
+ * parses the string. The data returned in range_ptr
+ * should not be processed directly - use the functions
+ * range_min(), range_max(), and range_mult() to access
+ * data for a given range.
+ * errptr user-supplied char ** which can be set to point to a
+ * static error string. If errptr is NULL, it is ignored.
+ *
+ * parse_range() returns -1 on error, or the number of ranges parsed.
+ */
+
+static int str_to_int();
+static long long divider(long long, long long, long long, long long);
+
+int parse_ranges(char *str, int defmin, int defmax, int defmult,
+ int (*parse_func)(), char **rangeptr, char **errptr)
+{
+ int ncommas;
+ char *tmpstr, *cp, *tok, *n1str, *n2str, *multstr;
+ struct range *rp, *ranges;
+ static char errmsg[256];
+
+ if (errptr != NULL) {
+ *errptr = errmsg;
+ }
+
+ for (ncommas = 0, cp = str; *cp != '\0'; cp++) {
+ if (*cp == ',') {
+ ncommas++;
+ }
+ }
+
+ if (parse_func == NULL) {
+ parse_func = str_to_int;
+ }
+
+ tmpstr = strdup(str);
+ ranges = malloc((ncommas + 1) * sizeof(struct range));
+ rp = ranges;
+
+ tok = strtok(tmpstr, ",");
+ while (tok != NULL) {
+ n1str = tok;
+ n2str = NULL;
+ multstr = NULL;
+
+ rp->min = defmin;
+ rp->max = defmax;
+ rp->mult = defmult;
+
+ if ((cp = strchr(n1str, ':')) != NULL) {
+ *cp = '\0';
+ n2str = cp + 1;
+
+ if ((cp = strchr(n2str, ':')) != NULL) {
+ *cp = '\0';
+ multstr = cp + 1;
+ }
+ }
+
+ /*
+ * Parse the 'min' field - if it is zero length (:n2[:mult]
+ * format), retain the default value, otherwise, pass the
+ * string to the parse function.
+ */
+
+ if ((int)strlen(n1str) > 0) {
+ if ((*parse_func) (n1str, &rp->min) < 0) {
+ sprintf(errmsg,
+ "error parsing string %s into an integer",
+ n1str);
+ free(tmpstr);
+ free(ranges);
+ return -1;
+ }
+ }
+
+ /*
+ * Process the 'max' field - if one was not present (n1 format)
+ * set max equal to min. If the field was present, but
+ * zero length (n1: format), retain the default. Otherwise
+ * pass the string to the parse function.
+ */
+
+ if (n2str == NULL) {
+ rp->max = rp->min;
+ } else if ((int)strlen(n2str) > 0) {
+ if ((*parse_func) (n2str, &rp->max) < 0) {
+ sprintf(errmsg,
+ "error parsing string %s into an integer",
+ n2str);
+ free(tmpstr);
+ free(ranges);
+ return -1;
+ }
+ }
+
+ /*
+ * Process the 'mult' field - if one was not present
+ * (n1:n2 format), or the field was zero length (n1:n2: format)
+ * then set the mult field to defmult - otherwise pass then
+ * mult field to the parse function.
+ */
+
+ if (multstr != NULL && (int)strlen(multstr) > 0) {
+ if ((*parse_func) (multstr, &rp->mult) < 0) {
+ sprintf(errmsg,
+ "error parsing string %s into an integer",
+ multstr);
+ free(tmpstr);
+ free(ranges);
+ return -1;
+ }
+ }
+
+ rp++;
+ tok = strtok(NULL, ",");
+ }
+
+ free(tmpstr);
+
+ if (rangeptr != NULL) {
+ *rangeptr = (char *)ranges;
+ } else {
+ free(ranges); /* just running in parse mode */
+ }
+
+ return (rp - ranges);
+}
+
+/*
+ * The default integer-parsing function
+ */
+
+static int str_to_int(char *str, int *ip)
+{
+ char c;
+
+ if (sscanf(str, "%i%c", ip, &c) != 1) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Three simple functions to return the min, max, and mult values for a given
+ * range. It is assumed that rbuf is a range buffer set up by parse_ranges(),
+ * and that r is a valid range within that buffer.
+ */
+
+int range_min(char *rbuf, int r)
+{
+ return ((struct range *)rbuf)[r].min;
+}
+
+int range_max(char *rbuf, int r)
+{
+ return ((struct range *)rbuf)[r].max;
+}
+
+int range_mult(char *rbuf, int r)
+{
+ return ((struct range *)rbuf)[r].mult;
+}
+
+/*****************************************************************************
+ * random_range(int start, int end, int mult, char **errp)
+ *
+ * Returns a psuedo-random number which is >= 'start', <= 'end', and a multiple
+ * of 'mult'. Start and end may be any valid integer, but mult must be an
+ * integer > 0. errp is a char ** which will be set to point to a static
+ * error message buffer if it is not NULL, and an error occurs.
+ *
+ * The errp is the only way to check if the routine fails - currently the only
+ * failure conditions are:
+ *
+ * mult < 1
+ * no numbers in the start-end range that are a multiple of 'mult'
+ *
+ * If random_range_fails, and errp is a valid pointer, it will point to an
+ * internal error buffer. If errp is a vaild pointer, and random_range
+ * is successful, errp will be set to NULL.
+ *
+ * Note - if mult is 1 (the most common case), there are error conditions
+ * possible, and errp need not be used.
+ *
+ * Note: Uses lrand48(), assuming that set_random_seed() uses srand48() when
+ * setting the seed.
+ *****************************************************************************/
+
+long random_range(int min, int max, int mult, char **errp)
+{
+ int r, nmults, orig_min, orig_max, orig_mult, tmp;
+ extern long lrand48();
+ static char errbuf[128];
+
+ /*
+ * Sanity check
+ */
+
+ if (mult < 1) {
+ if (errp != NULL) {
+ sprintf(errbuf, "mult arg must be greater than 0");
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ /*
+ * Save original parameter values for use in error message
+ */
+
+ orig_min = min;
+ orig_max = max;
+ orig_mult = mult;
+
+ /*
+ * switch min/max if max < min
+ */
+
+ if (max < min) {
+ tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ /*
+ * select the random number
+ */
+
+ if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
+ min += mult - r;
+
+ if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
+ max -= r;
+
+ if (min > max) { /* no 'mult' multiples between min & max */
+ if (errp != NULL) {
+ sprintf(errbuf,
+ "no numbers in the range %d:%d that are a multiple of %d",
+ orig_min, orig_max, orig_mult);
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ nmults = ((max - min) / mult) + 1;
+#if CRAY
+ /*
+ * If max is less than 2gb, then the value can fit in 32 bits
+ * and the standard lrand48() routine can be used.
+ */
+ if (max <= (long)2147483647) {
+ return (long)(min + (((long)lrand48() % nmults) * mult));
+ } else {
+ /*
+ * max is greater than 2gb - meeds more than 32 bits.
+ * Since lrand48 only will get a number up to 32bits.
+ */
+ long randnum;
+ randnum = divider(min, max, 0, -1);
+ return (long)(min + ((randnum % nmults) * mult));
+ }
+
+#else
+ return (min + ((lrand48() % nmults) * mult));
+#endif
+
+}
+
+/*
+ * Just like random_range, but all values are longs.
+ */
+long random_rangel(long min, long max, long mult, char **errp)
+{
+ long r, nmults, orig_min, orig_max, orig_mult, tmp;
+ extern long lrand48();
+ static char errbuf[128];
+
+ /*
+ * Sanity check
+ */
+
+ if (mult < 1) {
+ if (errp != NULL) {
+ sprintf(errbuf, "mult arg must be greater than 0");
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ /*
+ * Save original parameter values for use in error message
+ */
+
+ orig_min = min;
+ orig_max = max;
+ orig_mult = mult;
+
+ /*
+ * switch min/max if max < min
+ */
+
+ if (max < min) {
+ tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ /*
+ * select the random number
+ */
+
+ if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
+ min += mult - r;
+
+ if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
+ max -= r;
+
+ if (min > max) { /* no 'mult' multiples between min & max */
+ if (errp != NULL) {
+ sprintf(errbuf,
+ "no numbers in the range %ld:%ld that are a multiple of %ld",
+ orig_min, orig_max, orig_mult);
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ nmults = ((max - min) / mult) + 1;
+#if CRAY || (_MIPS_SZLONG == 64)
+ /*
+ * If max is less than 2gb, then the value can fit in 32 bits
+ * and the standard lrand48() routine can be used.
+ */
+ if (max <= (long)2147483647) {
+ return (long)(min + (((long)lrand48() % nmults) * mult));
+ } else {
+ /*
+ * max is greater than 2gb - meeds more than 32 bits.
+ * Since lrand48 only will get a number up to 32bits.
+ */
+ long randnum;
+ randnum = divider(min, max, 0, -1);
+ return (long)(min + ((randnum % nmults) * mult));
+ }
+
+#else
+ return (min + ((lrand48() % nmults) * mult));
+#endif
+}
+
+/*
+ * Attempts to be just like random_range, but everything is long long (64 bit)
+ */
+long long random_rangell(long long min, long long max,
+ long long mult, char **errp)
+{
+ long long r, nmults, orig_min, orig_max, orig_mult, tmp;
+ long long randnum;
+ extern long lrand48();
+ static char errbuf[128];
+
+ /*
+ * Sanity check
+ */
+
+ if (mult < 1) {
+ if (errp != NULL) {
+ sprintf(errbuf, "mult arg must be greater than 0");
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ /*
+ * Save original parameter values for use in error message
+ */
+
+ orig_min = min;
+ orig_max = max;
+ orig_mult = mult;
+
+ /*
+ * switch min/max if max < min
+ */
+
+ if (max < min) {
+ tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ /*
+ * select the random number
+ */
+
+ if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
+ min += mult - r;
+
+ if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
+ max -= r;
+
+ if (min > max) { /* no 'mult' multiples between min & max */
+ if (errp != NULL) {
+ sprintf(errbuf,
+ "no numbers in the range %lld:%lld that are a multiple of %lld",
+ orig_min, orig_max, orig_mult);
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ nmults = ((max - min) / mult) + 1;
+ /*
+ * If max is less than 2gb, then the value can fit in 32 bits
+ * and the standard lrand48() routine can be used.
+ */
+ if (max <= (long)2147483647) {
+ return (long long)(min +
+ (((long long)lrand48() % nmults) * mult));
+ } else {
+ /*
+ * max is greater than 2gb - meeds more than 32 bits.
+ * Since lrand48 only will get a number up to 32bits.
+ */
+ randnum = divider(min, max, 0, -1);
+ return (long long)(min + ((randnum % nmults) * mult));
+ }
+
+}
+
+/*
+ * This functional will recusively call itself to return a random
+ * number min and max. It was designed to work the 64bit numbers
+ * even when compiled as 32 bit process.
+ * algorithm: to use the official lrand48() routine - limited to 32 bits.
+ * find the difference between min and max (max-min).
+ * if the difference is 2g or less, use the random number gotton from lrand48().
+ * Determine the midway point between min and max.
+ * if the midway point is less than 2g from min or max,
+ * randomly add the random number gotton from lrand48() to
+ * either min or the midpoint.
+ * Otherwise, call outself with min and max being min and midway value or
+ * midway value and max. This will reduce the range in half.
+ */
+static long long
+divider(long long min, long long max, long long cnt, long long rand)
+{
+ long long med, half, diff;
+
+ /*
+ * prevent run away code. We are dividing by two each count.
+ * if we get to a count of more than 32, we should have gotten
+ * to 2gb.
+ */
+ if (cnt > 32)
+ return -1;
+
+ /*
+ * Only get a random number the first time.
+ */
+ if (cnt == 0 || rand < -1) {
+ rand = (long long)lrand48(); /* 32 bit random number */
+ }
+
+ diff = max - min;
+
+ if (diff <= 2147483647)
+ return min + rand;
+
+ half = diff / (long long)2; /* half the distance between min and max */
+ med = min + half; /* med way point between min and max */
+
+#if DEBUG
+ printf("divider: min=%lld, max=%lld, cnt=%lld, rand=%lld\n", min, max,
+ cnt, rand);
+ printf(" diff = %lld, half = %lld, med = %lld\n", diff, half, med);
+#endif
+
+ if (half <= 2147483647) {
+ /*
+ * If half is smaller than 2gb, we can use the random number
+ * to pick the number within the min to med or med to max
+ * if the cnt bit of rand is zero or one, respectively.
+ */
+ if (rand & (1 << cnt))
+ return med + rand;
+ else
+ return min + rand;
+ } else {
+ /*
+ * recursively call ourself to reduce the value to the bottom half
+ * or top half (bit cnt is set).
+ */
+ if (rand & (1 << cnt)) {
+ return divider(med, max, cnt + 1, rand);
+ } else {
+ return divider(min, med, cnt + 1, rand);
+ }
+
+ }
+
+}
+
+/*****************************************************************************
+ * random_range_seed(s)
+ *
+ * Sets the random seed to s. Uses srand48(), assuming that lrand48() will
+ * be used in random_range().
+ *****************************************************************************/
+
+void random_range_seed(long s)
+{
+ extern void srand48();
+
+ srand48(s);
+}
+
+/****************************************************************************
+ * random_bit(mask)
+ *
+ * This function randomly returns a single bit from the bits
+ * set in mask. If mask is zero, zero is returned.
+ *
+ ****************************************************************************/
+long random_bit(long mask)
+{
+ int nbits = 0; /* number of set bits in mask */
+ long bit; /* used to count bits and num of set bits choosen */
+ int nshift; /* used to count bit shifts */
+
+ if (mask == 0)
+ return 0;
+
+ /*
+ * get the number of bits set in mask
+ */
+#ifndef CRAY
+
+ bit = 1L;
+ for (nshift = 0; (unsigned int)nshift < sizeof(long) * 8; nshift++) {
+ if (mask & bit)
+ nbits++;
+ bit = bit << 1;
+ }
+
+#else
+ nbits = _popcnt(mask);
+#endif /* if CRAY */
+
+ /*
+ * randomly choose a bit.
+ */
+ bit = random_range(1, nbits, 1, NULL);
+
+ /*
+ * shift bits until you determine which bit was randomly choosen.
+ * nshift will hold the number of shifts to make.
+ */
+
+ nshift = 0;
+ while (bit) {
+ /* check if the current one's bit is set */
+ if (mask & 1L) {
+ bit--;
+ }
+ mask = mask >> 1;
+ nshift++;
+ }
+
+ return 01L << (nshift - 1);
+
+}
+
+#if RANDOM_BIT_UNITTEST
+/*
+ * The following is a unit test main function for random_bit().
+ */
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int ind;
+ int cnt, iter;
+ long mask, ret;
+
+ printf("test for first and last bit set\n");
+ mask = 1L;
+ ret = random_bit(mask);
+ printf("random_bit(%#o) returned %#o\n", mask, ret);
+
+ mask = 1L << (sizeof(long) * 8 - 1);
+ ret = random_bit(mask);
+ printf("random_bit(%#o) returned %#o\n", mask, ret);
+
+ if (argc >= 3) {
+ iter = atoi(argv[1]);
+ for (ind = 2; ind < argc; ind++) {
+ printf("Calling random_bit %d times for mask %#o\n",
+ iter, mask);
+ sscanf(argv[ind], "%i", &mask);
+ for (cnt = 0; cnt < iter; cnt++) {
+ ret = random_bit(mask);
+ printf("random_bit(%#o) returned %#o\n", mask,
+ ret);
+ }
+ }
+ }
+ exit(0);
+}
+
+#endif /* end if RANDOM_BIT_UNITTEST */
+
+#if UNIT_TEST
+/*
+ * The following is a unit test main function for random_range*().
+ */
+
+#define PARTNUM 10 /* used to determine even distribution of random numbers */
+#define MEG 1024*1024*1024
+#define GIG 1073741824
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ int ind;
+ int cnt, iter = 10;
+ int imin = 0, imult = 1, itmin, itmax = 0;
+#if CRAY
+ int imax = 6 * GIG; /* higher than 32 bits */
+#else
+ int imax = 1048576;
+#endif
+
+ long lret, lmin = 0, lmult = 1, ltmin, ltmax = 0;
+#if CRAY || (_MIPS_SZLONG == 64)
+ long lmax = 6 * (long)GIG; /* higher than 32 bits */
+#else
+ long lmax = 1048576;
+#endif
+ long long llret, llmin = 0, llmult = 1, lltmin, lltmax = 0;
+ long long llmax = (long long)80 * (long long)GIG;
+
+ long part;
+ long long lpart;
+ long cntarr[PARTNUM];
+ long valbound[PARTNUM];
+ long long lvalbound[PARTNUM];
+
+ for (ind = 0; ind < PARTNUM; ind++)
+ cntarr[ind] = 0;
+
+ if (argc < 2) {
+ printf("Usage: %s func [iterations] \n", argv[0]);
+ printf
+ ("func can be random_range, random_rangel, random_rangell\n");
+ exit(1);
+ }
+
+ if (argc >= 3) {
+ if (sscanf(argv[2], "%i", &iter) != 1) {
+ printf("Usage: %s [func iterations] \n", argv[0]);
+ printf("argv[2] is not a number\n");
+ exit(1);
+ }
+ }
+
+ /*
+ * random_rangel ()
+ */
+ if (strcmp(argv[1], "random_rangel") == 0) {
+ ltmin = lmax;
+ part = lmax / PARTNUM;
+ for (ind = 0; ind < PARTNUM; ind++) {
+ valbound[ind] = part * ind;
+ }
+
+ for (cnt = 0; cnt < iter; cnt++) {
+ lret = random_rangel(lmin, lmax, lmult, NULL);
+ if (iter < 100)
+ printf("%ld\n", lret);
+ if (lret < ltmin)
+ ltmin = lret;
+ if (lret > ltmax)
+ ltmax = lret;
+ for (ind = 0; ind < PARTNUM - 1; ind++) {
+ if (valbound[ind] < lret
+ && lret <= valbound[ind + 1]) {
+ cntarr[ind]++;
+ break;
+ }
+ }
+ if (lret > valbound[PARTNUM - 1]) {
+ cntarr[PARTNUM - 1]++;
+ }
+ }
+ for (ind = 0; ind < PARTNUM - 1; ind++) {
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", ind + 1,
+ valbound[ind], valbound[ind + 1], cntarr[ind],
+ (float)(cntarr[ind] / (float)iter));
+ }
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", PARTNUM,
+ valbound[PARTNUM - 1], lmax, cntarr[PARTNUM - 1],
+ (float)(cntarr[PARTNUM - 1] / (float)iter));
+ printf(" min=%ld, max=%ld\n", ltmin, ltmax);
+
+ } else if (strcmp(argv[1], "random_rangell") == 0) {
+ /*
+ * random_rangell() unit test
+ */
+ lltmin = llmax;
+ lpart = llmax / PARTNUM;
+ for (ind = 0; ind < PARTNUM; ind++) {
+ lvalbound[ind] = (long long)(lpart * ind);
+ }
+
+ for (cnt = 0; cnt < iter; cnt++) {
+ llret = random_rangell(llmin, llmax, llmult, NULL);
+ if (iter < 100)
+ printf("random_rangell returned %lld\n", llret);
+ if (llret < lltmin)
+ lltmin = llret;
+ if (llret > lltmax)
+ lltmax = llret;
+
+ for (ind = 0; ind < PARTNUM - 1; ind++) {
+ if (lvalbound[ind] < llret
+ && llret <= lvalbound[ind + 1]) {
+ cntarr[ind]++;
+ break;
+ }
+ }
+ if (llret > lvalbound[PARTNUM - 1]) {
+ cntarr[PARTNUM - 1]++;
+ }
+ }
+ for (ind = 0; ind < PARTNUM - 1; ind++) {
+ printf("%2d %-13lld to %-13lld %5ld %4.4f\n",
+ ind + 1, lvalbound[ind], lvalbound[ind + 1],
+ cntarr[ind], (float)(cntarr[ind] / (float)iter));
+ }
+ printf("%2d %-13lld to %-13lld %5ld %4.4f\n", PARTNUM,
+ lvalbound[PARTNUM - 1], llmax, cntarr[PARTNUM - 1],
+ (float)(cntarr[PARTNUM - 1] / (float)iter));
+ printf(" min=%lld, max=%lld\n", lltmin, lltmax);
+
+ } else {
+ /*
+ * random_range() unit test
+ */
+ itmin = imax;
+ part = imax / PARTNUM;
+ for (ind = 0; ind < PARTNUM; ind++) {
+ valbound[ind] = part * ind;
+ }
+
+ for (cnt = 0; cnt < iter; cnt++) {
+ lret = random_range(imin, imax, imult, NULL);
+ if (iter < 100)
+ printf("%ld\n", lret);
+ if (lret < itmin)
+ itmin = lret;
+ if (lret > itmax)
+ itmax = lret;
+
+ for (ind = 0; ind < PARTNUM - 1; ind++) {
+ if (valbound[ind] < lret
+ && lret <= valbound[ind + 1]) {
+ cntarr[ind]++;
+ break;
+ }
+ }
+ if (lret > valbound[PARTNUM - 1]) {
+ cntarr[PARTNUM - 1]++;
+ }
+ }
+ for (ind = 0; ind < PARTNUM - 1; ind++) {
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", ind + 1,
+ valbound[ind], valbound[ind + 1], cntarr[ind],
+ (float)(cntarr[ind] / (float)iter));
+ }
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", PARTNUM,
+ valbound[PARTNUM - 1], (long)imax, cntarr[PARTNUM - 1],
+ (float)(cntarr[PARTNUM - 1] / (float)iter));
+ printf(" min=%d, max=%d\n", itmin, itmax);
+
+ }
+
+ exit(0);
+}
+
+#endif
diff --git a/src/kernel/tests/lib/safe_file_ops.c b/src/kernel/tests/lib/safe_file_ops.c
new file mode 100644
index 0000000..e06d399
--- /dev/null
+++ b/src/kernel/tests/lib/safe_file_ops.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2012 Cyril Hrubis chrubis@suse.cz
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "test.h"
+#include "safe_file_ops_fn.h"
+
+/*
+ * Count number of expected assigned conversions. Any conversion starts with '%'.
+ * The '%%' matches % and no assignment is done. The %*x matches as x would do but
+ * the assignment is suppressed.
+ *
+ * NOTE: This is not 100% correct for complex scanf strings, but will do for
+ * all of our intended usage.
+ */
+static int count_scanf_conversions(const char *fmt)
+{
+ unsigned int cnt = 0;
+ int flag = 0;
+
+ while (*fmt) {
+ switch (*fmt) {
+ case '%':
+ if (flag) {
+ cnt--;
+ flag = 0;
+ } else {
+ flag = 1;
+ cnt++;
+ }
+ break;
+ case '*':
+ if (flag) {
+ cnt--;
+ flag = 0;
+ }
+ break;
+ default:
+ flag = 0;
+ }
+
+ fmt++;
+ }
+
+ return cnt;
+}
+
+int file_scanf(const char *file, const int lineno,
+ const char *path, const char *fmt, ...)
+{
+ va_list va;
+ FILE *f;
+ int exp_convs, ret;
+
+ f = fopen(path, "r");
+
+ if (f == NULL) {
+ tst_resm(TWARN,
+ "Failed to open FILE '%s' at %s:%d",
+ path, file, lineno);
+ return 1;
+ }
+
+ exp_convs = count_scanf_conversions(fmt);
+
+ va_start(va, fmt);
+ ret = vfscanf(f, fmt, va);
+ va_end(va);
+
+ if (ret == EOF) {
+ tst_resm(TWARN,
+ "The FILE '%s' ended prematurely at %s:%d",
+ path, file, lineno);
+ goto err;
+ }
+
+ if (ret != exp_convs) {
+ tst_resm(TWARN,
+ "Expected %i conversions got %i FILE '%s' at %s:%d",
+ exp_convs, ret, path, file, lineno);
+ goto err;
+ }
+
+ if (fclose(f)) {
+ tst_resm(TWARN,
+ "Failed to close FILE '%s' at %s:%d",
+ path, file, lineno);
+ return 1;
+ }
+
+ return 0;
+
+err:
+ if (fclose(f)) {
+ tst_resm(TWARN,
+ "Failed to close FILE '%s' at %s:%d",
+ path, file, lineno);
+ }
+ return 1;
+}
+
+void safe_file_scanf(const char *file, const int lineno,
+ void (*cleanup_fn) (void),
+ const char *path, const char *fmt, ...)
+{
+ va_list va;
+ FILE *f;
+ int exp_convs, ret;
+
+ f = fopen(path, "r");
+
+ if (f == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to open FILE '%s' for reading at %s:%d",
+ path, file, lineno);
+ return;
+ }
+
+ exp_convs = count_scanf_conversions(fmt);
+
+ va_start(va, fmt);
+ ret = vfscanf(f, fmt, va);
+ va_end(va);
+
+ if (ret == EOF) {
+ tst_brkm(TBROK, cleanup_fn,
+ "The FILE '%s' ended prematurely at %s:%d",
+ path, file, lineno);
+ return;
+ }
+
+ if (ret != exp_convs) {
+ tst_brkm(TBROK, cleanup_fn,
+ "Expected %i conversions got %i FILE '%s' at %s:%d",
+ exp_convs, ret, path, file, lineno);
+ return;
+ }
+
+ if (fclose(f)) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to close FILE '%s' at %s:%d",
+ path, file, lineno);
+ return;
+ }
+}
+
+
+/*
+ * Try to parse each line from file specified by 'path' according
+ * to scanf format 'fmt'. If all fields could be parsed, stop and
+ * return 0, otherwise continue or return 1 if EOF is reached.
+ */
+int file_lines_scanf(const char *file, const int lineno,
+ void (*cleanup_fn)(void), int strict,
+ const char *path, const char *fmt, ...)
+{
+ FILE *fp;
+ int ret = 0;
+ int arg_count = 0;
+ char line[BUFSIZ];
+ va_list ap;
+
+ if (!fmt) {
+ tst_brkm(TBROK, cleanup_fn, "pattern is NULL, %s:%d",
+ file, lineno);
+ return 1;
+ }
+
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to open FILE '%s' for reading at %s:%d",
+ path, file, lineno);
+ return 1;
+ }
+
+ arg_count = count_scanf_conversions(fmt);
+
+ while (fgets(line, BUFSIZ, fp) != NULL) {
+ va_start(ap, fmt);
+ ret = vsscanf(line, fmt, ap);
+ va_end(ap);
+
+ if (ret == arg_count)
+ break;
+ }
+ fclose(fp);
+
+ if (strict && ret != arg_count) {
+ tst_brkm(TBROK, cleanup_fn, "Expected %i conversions got %i"
+ " FILE '%s' at %s:%d", arg_count, ret, path, file, lineno);
+ return 1;
+ }
+
+ return !(ret == arg_count);
+}
+
+int file_printf(const char *file, const int lineno,
+ const char *path, const char *fmt, ...)
+{
+ va_list va;
+ FILE *f;
+
+ f = fopen(path, "w");
+
+ if (f == NULL) {
+ tst_resm(TWARN,
+ "Failed to open FILE '%s' at %s:%d",
+ path, file, lineno);
+ return 1;
+ }
+
+ va_start(va, fmt);
+
+ if (vfprintf(f, fmt, va) < 0) {
+ tst_resm(TWARN,
+ "Failed to print to FILE '%s' at %s:%d",
+ path, file, lineno);
+ goto err;
+ }
+
+ va_end(va);
+
+ if (fclose(f)) {
+ tst_resm(TWARN,
+ "Failed to close FILE '%s' at %s:%d",
+ path, file, lineno);
+ return 1;
+ }
+
+ return 0;
+
+err:
+ if (fclose(f)) {
+ tst_resm(TWARN,
+ "Failed to close FILE '%s' at %s:%d",
+ path, file, lineno);
+ }
+ return 1;
+}
+
+void safe_file_printf(const char *file, const int lineno,
+ void (*cleanup_fn) (void),
+ const char *path, const char *fmt, ...)
+{
+ va_list va;
+ FILE *f;
+
+ f = fopen(path, "w");
+
+ if (f == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to open FILE '%s' for writing at %s:%d",
+ path, file, lineno);
+ return;
+ }
+
+ va_start(va, fmt);
+
+ if (vfprintf(f, fmt, va) < 0) {
+ tst_brkm(TBROK, cleanup_fn,
+ "Failed to print to FILE '%s' at %s:%d",
+ path, file, lineno);
+ return;
+ }
+
+ va_end(va);
+
+ if (fclose(f)) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to close FILE '%s' at %s:%d",
+ path, file, lineno);
+ return;
+ }
+}
+
+//TODO: C implementation? better error condition reporting?
+void safe_cp(const char *file, const int lineno,
+ void (*cleanup_fn) (void), const char *src, const char *dst)
+{
+ size_t len = strlen(src) + strlen(dst) + 16;
+ char buf[len];
+ int ret;
+
+ snprintf(buf, sizeof(buf), "cp \"%s\" \"%s\"", src, dst);
+
+ ret = system(buf);
+
+ if (ret) {
+ tst_brkm(TBROK, cleanup_fn,
+ "Failed to copy '%s' to '%s' at %s:%d",
+ src, dst, file, lineno);
+ }
+}
+
+#ifndef HAVE_UTIMENSAT
+
+static void set_time(struct timeval *res, const struct timespec *src,
+ long cur_tv_sec, long cur_tv_usec)
+{
+ switch (src->tv_nsec) {
+ case UTIME_NOW:
+ break;
+ case UTIME_OMIT:
+ res->tv_sec = cur_tv_sec;
+ res->tv_usec = cur_tv_usec;
+ break;
+ default:
+ res->tv_sec = src->tv_sec;
+ res->tv_usec = src->tv_nsec / 1000;
+ }
+}
+
+#endif
+
+void safe_touch(const char *file, const int lineno,
+ void (*cleanup_fn)(void),
+ const char *pathname,
+ mode_t mode, const struct timespec times[2])
+{
+ int ret;
+ mode_t defmode;
+
+ defmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+ ret = open(pathname, O_CREAT | O_WRONLY, defmode);
+ if (ret == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to open file '%s' at %s:%d",
+ pathname, file, lineno);
+ return;
+ }
+
+ ret = close(ret);
+ if (ret == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to close file '%s' at %s:%d",
+ pathname, file, lineno);
+ return;
+ }
+
+ if (mode != 0) {
+ ret = chmod(pathname, mode);
+ if (ret == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to chmod file '%s' at %s:%d",
+ pathname, file, lineno);
+ return;
+ }
+ }
+
+
+#ifdef HAVE_UTIMENSAT
+ ret = utimensat(AT_FDCWD, pathname, times, 0);
+#else
+ if (times == NULL) {
+ ret = utimes(pathname, NULL);
+ } else {
+ struct stat sb;
+ struct timeval cotimes[2];
+
+ ret = stat(pathname, &sb);
+ if (ret == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to stat file '%s' at %s:%d",
+ pathname, file, lineno);
+ return;
+ }
+
+ ret = gettimeofday(cotimes, NULL);
+ if (ret == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to gettimeofday() at %s:%d",
+ file, lineno);
+ return;
+ }
+
+ cotimes[1] = cotimes[0];
+
+ set_time(cotimes, times,
+ sb.st_atime, sb.st_atim.tv_nsec / 1000);
+ set_time(cotimes + 1, times + 1,
+ sb.st_mtime, sb.st_mtim.tv_nsec / 1000);
+
+ ret = utimes(pathname, cotimes);
+ }
+#endif
+ if (ret == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "Failed to update the access/modification time on file"
+ " '%s' at %s:%d", pathname, file, lineno);
+ }
+}
diff --git a/src/kernel/tests/lib/safe_macros.c b/src/kernel/tests/lib/safe_macros.c
new file mode 100644
index 0000000..4f48d75
--- /dev/null
+++ b/src/kernel/tests/lib/safe_macros.c
@@ -0,0 +1,1109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) Linux Test Project, 2010-2020
+ */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/xattr.h>
+#include <sys/sysinfo.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <malloc.h>
+#include "test.h"
+#include "safe_macros.h"
+
+char *safe_basename(const char *file, const int lineno,
+ void (*cleanup_fn) (void), char *path)
+{
+ char *rval;
+
+ rval = basename(path);
+ if (rval == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: basename(%s) failed",
+ file, lineno, path);
+ }
+
+ return rval;
+}
+
+int
+safe_chdir(const char *file, const int lineno, void (*cleanup_fn) (void),
+ const char *path)
+{
+ int rval;
+
+ rval = chdir(path);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: chdir(%s) failed",
+ file, lineno, path);
+ }
+
+ return rval;
+}
+
+int
+safe_close(const char *file, const int lineno, void (*cleanup_fn) (void),
+ int fildes)
+{
+ int rval;
+
+ rval = close(fildes);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: close(%d) failed",
+ file, lineno, fildes);
+ }
+
+ return rval;
+}
+
+int
+safe_creat(const char *file, const int lineno, void (*cleanup_fn) (void),
+ const char *pathname, mode_t mode)
+{
+ int rval;
+
+ rval = creat(pathname, mode);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: creat(%s,0%o) failed",
+ file, lineno, pathname, mode);
+ }
+
+ return rval;
+}
+
+char *safe_dirname(const char *file, const int lineno,
+ void (*cleanup_fn) (void), char *path)
+{
+ char *rval;
+
+ rval = dirname(path);
+ if (rval == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: dirname(%s) failed",
+ file, lineno, path);
+ }
+
+ return rval;
+}
+
+char *safe_getcwd(const char *file, const int lineno, void (*cleanup_fn) (void),
+ char *buf, size_t size)
+{
+ char *rval;
+
+ rval = getcwd(buf, size);
+ if (rval == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: getcwd(%p,%zu) failed",
+ file, lineno, buf, size);
+ }
+
+ return rval;
+}
+
+struct passwd *safe_getpwnam(const char *file, const int lineno,
+ void (*cleanup_fn) (void), const char *name)
+{
+ struct passwd *rval;
+
+ rval = getpwnam(name);
+ if (rval == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: getpwnam(%s) failed",
+ file, lineno, name);
+ }
+
+ return rval;
+}
+
+int
+safe_getrusage(const char *file, const int lineno, void (*cleanup_fn) (void),
+ int who, struct rusage *usage)
+{
+ int rval;
+
+ rval = getrusage(who, usage);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: getrusage(%d,%p) failed",
+ file, lineno, who, usage);
+ }
+
+ return rval;
+}
+
+void *safe_malloc(const char *file, const int lineno, void (*cleanup_fn) (void),
+ size_t size)
+{
+ void *rval;
+
+ rval = malloc(size);
+ if (rval == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: malloc(%zu) failed",
+ file, lineno, size);
+ }
+
+ return rval;
+}
+
+int safe_mkdir(const char *file, const int lineno, void (*cleanup_fn) (void),
+ const char *pathname, mode_t mode)
+{
+ int rval;
+
+ rval = mkdir(pathname, mode);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: mkdir(%s,0%o) failed",
+ file, lineno, pathname, mode);
+ }
+
+ return (rval);
+}
+
+int safe_rmdir(const char *file, const int lineno, void (*cleanup_fn) (void),
+ const char *pathname)
+{
+ int rval;
+
+ rval = rmdir(pathname);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: rmdir(%s) failed",
+ file, lineno, pathname);
+ }
+
+ return (rval);
+}
+
+int safe_munmap(const char *file, const int lineno, void (*cleanup_fn) (void),
+ void *addr, size_t length)
+{
+ int rval;
+
+ rval = munmap(addr, length);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: munmap(%p,%zu) failed",
+ file, lineno, addr, length);
+ }
+
+ return rval;
+}
+
+int safe_open(const char *file, const int lineno, void (*cleanup_fn) (void),
+ const char *pathname, int oflags, ...)
+{
+ va_list ap;
+ int rval;
+ mode_t mode;
+
+ va_start(ap, oflags);
+
+ /* Android's NDK's mode_t is smaller than an int, which results in
+ * SIGILL here when passing the mode_t type.
+ */
+ mode = va_arg(ap, int);
+
+ va_end(ap);
+
+ rval = open(pathname, oflags, mode);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: open(%s,%d,0%o) failed",
+ file, lineno, pathname, oflags, mode);
+ }
+
+ return rval;
+}
+
+int safe_pipe(const char *file, const int lineno, void (*cleanup_fn) (void),
+ int fildes[2])
+{
+ int rval;
+
+ rval = pipe(fildes);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: pipe({%d,%d}) failed",
+ file, lineno, fildes[0], fildes[1]);
+ }
+
+ return rval;
+}
+
+ssize_t safe_read(const char *file, const int lineno, void (*cleanup_fn) (void),
+ char len_strict, int fildes, void *buf, size_t nbyte)
+{
+ ssize_t rval;
+
+ rval = read(fildes, buf, nbyte);
+ if (rval == -1 || (len_strict && (size_t)rval != nbyte)) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: read(%d,%p,%zu) failed, returned %zd",
+ file, lineno, fildes, buf, nbyte, rval);
+ }
+
+ return rval;
+}
+
+int safe_setegid(const char *file, const int lineno, void (*cleanup_fn) (void),
+ gid_t egid)
+{
+ int rval;
+
+ rval = setegid(egid);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: setegid(%u) failed",
+ file, lineno, (unsigned) egid);
+ }
+
+ return rval;
+}
+
+int safe_seteuid(const char *file, const int lineno, void (*cleanup_fn) (void),
+ uid_t euid)
+{
+ int rval;
+
+ rval = seteuid(euid);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: seteuid(%u) failed",
+ file, lineno, (unsigned) euid);
+ }
+
+ return rval;
+}
+
+int safe_setgid(const char *file, const int lineno, void (*cleanup_fn) (void),
+ gid_t gid)
+{
+ int rval;
+
+ rval = setgid(gid);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: setgid(%u) failed",
+ file, lineno, (unsigned) gid);
+ }
+
+ return rval;
+}
+
+int safe_setuid(const char *file, const int lineno, void (*cleanup_fn) (void),
+ uid_t uid)
+{
+ int rval;
+
+ rval = setuid(uid);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: setuid(%u) failed",
+ file, lineno, (unsigned) uid);
+ }
+
+ return rval;
+}
+
+int safe_getresuid(const char *file, const int lineno, void (*cleanup_fn)(void),
+ uid_t *ruid, uid_t *euid, uid_t *suid)
+{
+ int rval;
+
+ rval = getresuid(ruid, euid, suid);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: getresuid(%p, %p, %p) failed",
+ file, lineno, ruid, euid, suid);
+ }
+
+ return rval;
+}
+
+int safe_getresgid(const char *file, const int lineno, void (*cleanup_fn)(void),
+ gid_t *rgid, gid_t *egid, gid_t *sgid)
+{
+ int rval;
+
+ rval = getresgid(rgid, egid, sgid);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: getresgid(%p, %p, %p) failed",
+ file, lineno, rgid, egid, sgid);
+ }
+
+ return rval;
+}
+
+int safe_unlink(const char *file, const int lineno, void (*cleanup_fn) (void),
+ const char *pathname)
+{
+ int rval;
+
+ rval = unlink(pathname);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: unlink(%s) failed",
+ file, lineno, pathname);
+ }
+
+ return rval;
+}
+
+
+int safe_link(const char *file, const int lineno,
+ void (cleanup_fn)(void), const char *oldpath,
+ const char *newpath)
+{
+ int rval;
+
+ rval = link(oldpath, newpath);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: link(%s,%s) failed",
+ file, lineno, oldpath, newpath);
+ }
+
+ return rval;
+}
+
+int safe_linkat(const char *file, const int lineno,
+ void (cleanup_fn)(void), int olddirfd, const char *oldpath,
+ int newdirfd, const char *newpath, int flags)
+{
+ int rval;
+
+ rval = linkat(olddirfd, oldpath, newdirfd, newpath, flags);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: linkat(%d,%s,%d,%s,%d) failed",
+ file, lineno, olddirfd, oldpath, newdirfd,
+ newpath, flags);
+ }
+
+ return rval;
+}
+
+ssize_t safe_readlink(const char *file, const int lineno,
+ void (cleanup_fn)(void), const char *path,
+ char *buf, size_t bufsize)
+{
+ ssize_t rval;
+
+ rval = readlink(path, buf, bufsize);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: readlink(%s,%p,%zu) failed",
+ file, lineno, path, buf, bufsize);
+ } else {
+ /* readlink does not append a NUL byte to the buffer.
+ * Add it now. */
+ if ((size_t) rval < bufsize)
+ buf[rval] = '\0';
+ else
+ buf[bufsize-1] = '\0';
+ }
+
+ return rval;
+}
+
+int safe_symlink(const char *file, const int lineno,
+ void (cleanup_fn)(void), const char *oldpath,
+ const char *newpath)
+{
+ int rval;
+
+ rval = symlink(oldpath, newpath);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: symlink(%s,%s) failed",
+ file, lineno, oldpath, newpath);
+ }
+
+ return rval;
+}
+
+ssize_t safe_write(const char *file, const int lineno, void (cleanup_fn) (void),
+ char len_strict, int fildes, const void *buf, size_t nbyte)
+{
+ ssize_t rval;
+
+ rval = write(fildes, buf, nbyte);
+ if (rval == -1 || (len_strict && (size_t)rval != nbyte)) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: write(%d,%p,%zu) failed",
+ file, lineno, fildes, buf, rval);
+ }
+
+ return rval;
+}
+
+long safe_strtol(const char *file, const int lineno,
+ void (cleanup_fn) (void), char *str, long min, long max)
+{
+ long rval;
+ char *endptr;
+
+ errno = 0;
+ rval = strtol(str, &endptr, 10);
+
+ if ((errno == ERANGE && (rval == LONG_MAX || rval == LONG_MIN))
+ || (errno != 0 && rval == 0)) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: strtol(%s) failed", file, lineno, str);
+ return rval;
+ }
+
+ if (endptr == str || (*endptr != '\0' && *endptr != '\n')) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: strtol(%s): Invalid value", file, lineno, str);
+ return 0;
+ }
+
+ if (rval > max || rval < min) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: strtol(%s): %ld is out of range %ld - %ld",
+ file, lineno, str, rval, min, max);
+ return 0;
+ }
+
+ return rval;
+}
+
+unsigned long safe_strtoul(const char *file, const int lineno,
+ void (cleanup_fn) (void), char *str,
+ unsigned long min, unsigned long max)
+{
+ unsigned long rval;
+ char *endptr;
+
+ errno = 0;
+ rval = strtoul(str, &endptr, 10);
+
+ if ((errno == ERANGE && rval == ULONG_MAX)
+ || (errno != 0 && rval == 0)) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: strtoul(%s) failed", file, lineno, str);
+ return rval;
+ }
+
+ if (rval > max || rval < min) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: strtoul(%s): %lu is out of range %lu - %lu",
+ file, lineno, str, rval, min, max);
+ return 0;
+ }
+
+ if (endptr == str || (*endptr != '\0' && *endptr != '\n')) {
+ tst_brkm(TBROK, cleanup_fn,
+ "Invalid value: '%s' at %s:%d", str, file, lineno);
+ return 0;
+ }
+
+ return rval;
+}
+
+long safe_sysconf(const char *file, const int lineno,
+ void (cleanup_fn) (void), int name)
+{
+ long rval;
+ errno = 0;
+
+ rval = sysconf(name);
+
+ if (rval == -1) {
+ if (errno) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: sysconf(%d) failed",
+ file, lineno, name);
+ } else {
+ tst_resm(TINFO, "%s:%d: sysconf(%d): "
+ "queried option is not available"
+ " or there is no definite limit",
+ file, lineno, name);
+ }
+ }
+
+ return rval;
+}
+
+int safe_chmod(const char *file, const int lineno,
+ void (cleanup_fn)(void), const char *path, mode_t mode)
+{
+ int rval;
+
+ rval = chmod(path, mode);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: chmod(%s,0%o) failed",
+ file, lineno, path, mode);
+ }
+
+ return rval;
+}
+
+int safe_fchmod(const char *file, const int lineno,
+ void (cleanup_fn)(void), int fd, mode_t mode)
+{
+ int rval;
+
+ rval = fchmod(fd, mode);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: fchmod(%d,0%o) failed",
+ file, lineno, fd, mode);
+ }
+
+ return rval;
+}
+
+int safe_chown(const char *file, const int lineno, void (cleanup_fn)(void),
+ const char *path, uid_t owner, gid_t group)
+{
+ int rval;
+
+ rval = chown(path, owner, group);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: chown(%s,%d,%d) failed",
+ file, lineno, path, owner, group);
+ }
+
+ return rval;
+}
+
+int safe_fchown(const char *file, const int lineno, void (cleanup_fn)(void),
+ int fd, uid_t owner, gid_t group)
+{
+ int rval;
+
+ rval = fchown(fd, owner, group);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: fchown(%d,%d,%d) failed",
+ file, lineno, fd, owner, group);
+ }
+
+ return rval;
+}
+
+pid_t safe_wait(const char *file, const int lineno, void (cleanup_fn)(void),
+ int *status)
+{
+ pid_t rval;
+
+ rval = wait(status);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: wait(%p) failed",
+ file, lineno, status);
+ }
+
+ return rval;
+}
+
+pid_t safe_waitpid(const char *file, const int lineno, void (cleanup_fn)(void),
+ pid_t pid, int *status, int opts)
+{
+ pid_t rval;
+
+ rval = waitpid(pid, status, opts);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: waitpid(%d,%p,%d) failed",
+ file, lineno, pid, status, opts);
+ }
+
+ return rval;
+}
+
+void *safe_memalign(const char *file, const int lineno,
+ void (*cleanup_fn) (void), size_t alignment, size_t size)
+{
+ void *rval;
+
+ rval = memalign(alignment, size);
+ if (rval == NULL)
+ tst_brkm(TBROK | TERRNO, cleanup_fn, "memalign failed at %s:%d",
+ file, lineno);
+
+ return rval;
+}
+
+int safe_kill(const char *file, const int lineno, void (cleanup_fn)(void),
+ pid_t pid, int sig)
+{
+ int rval;
+
+ rval = kill(pid, sig);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: kill(%d,%s) failed",
+ file, lineno, pid, tst_strsig(sig));
+ }
+
+ return rval;
+}
+
+int safe_mkfifo(const char *file, const int lineno,
+ void (*cleanup_fn)(void), const char *pathname, mode_t mode)
+{
+ int rval;
+
+ rval = mkfifo(pathname, mode);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: mkfifo(%s, 0%o) failed",
+ file, lineno, pathname, mode);
+ }
+
+ return rval;
+}
+
+int safe_rename(const char *file, const int lineno, void (*cleanup_fn)(void),
+ const char *oldpath, const char *newpath)
+{
+ int rval;
+
+ rval = rename(oldpath, newpath);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: rename(%s, %s) failed",
+ file, lineno, oldpath, newpath);
+ }
+
+ return rval;
+}
+
+static const char *const fuse_fs_types[] = {
+ "exfat",
+ "ntfs",
+};
+
+static int possibly_fuse(const char *fs_type)
+{
+ unsigned int i;
+
+ if (!fs_type)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(fuse_fs_types); i++) {
+ if (!strcmp(fuse_fs_types[i], fs_type))
+ return 1;
+ }
+
+ return 0;
+}
+
+int safe_mount(const char *file, const int lineno, void (*cleanup_fn)(void),
+ const char *source, const char *target,
+ const char *filesystemtype, unsigned long mountflags,
+ const void *data)
+{
+ int rval;
+
+ /*
+ * Don't try using the kernel's NTFS driver when mounting NTFS, since
+ * the kernel's NTFS driver doesn't have proper write support.
+ */
+ if (!filesystemtype || strcmp(filesystemtype, "ntfs")) {
+ rval = mount(source, target, filesystemtype, mountflags, data);
+ if (!rval)
+ return 0;
+ }
+
+ /*
+ * The FUSE filesystem executes mount.fuse helper, which tries to
+ * execute corresponding binary name which is encoded at the start of
+ * the source string and separated by # from the device name.
+ *
+ * The mount helpers are called mount.$fs_type.
+ */
+ if (possibly_fuse(filesystemtype)) {
+ char buf[1024];
+
+ tst_resm(TINFO, "Trying FUSE...");
+ snprintf(buf, sizeof(buf), "mount.%s '%s' '%s'",
+ filesystemtype, source, target);
+
+ rval = tst_system(buf);
+ if (WIFEXITED(rval) && WEXITSTATUS(rval) == 0)
+ return 0;
+
+ tst_brkm(TBROK, cleanup_fn, "mount.%s failed with %i",
+ filesystemtype, rval);
+ return -1;
+ } else {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: mount(%s, %s, %s, %lu, %p) failed",
+ file, lineno, source, target, filesystemtype,
+ mountflags, data);
+ }
+
+ return -1;
+}
+
+int safe_umount(const char *file, const int lineno, void (*cleanup_fn)(void),
+ const char *target)
+{
+ int rval;
+
+ rval = tst_umount(target);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: umount(%s) failed",
+ file, lineno, target);
+ }
+
+ return rval;
+}
+
+DIR* safe_opendir(const char *file, const int lineno, void (cleanup_fn)(void),
+ const char *name)
+{
+ DIR *rval;
+
+ rval = opendir(name);
+
+ if (!rval) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: opendir(%s) failed", file, lineno, name);
+ }
+
+ return rval;
+}
+
+int safe_closedir(const char *file, const int lineno, void (cleanup_fn)(void),
+ DIR *dirp)
+{
+ int rval;
+
+ rval = closedir(dirp);
+
+ if (rval) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: closedir(%p) failed", file, lineno, dirp);
+ }
+
+ return rval;
+}
+
+struct dirent *safe_readdir(const char *file, const int lineno, void (cleanup_fn)(void),
+ DIR *dirp)
+{
+ struct dirent *rval;
+ int err = errno;
+
+ errno = 0;
+ rval = readdir(dirp);
+
+ if (!rval && errno) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: readdir(%p) failed", file, lineno, dirp);
+ }
+
+ errno = err;
+ return rval;
+}
+
+int safe_getpriority(const char *file, const int lineno, int which, id_t who)
+{
+ int rval, err = errno;
+
+ errno = 0;
+ rval = getpriority(which, who);
+ if (errno) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d getpriority(%i, %i) failed",
+ file, lineno, which, who);
+ }
+
+ errno = err;
+ return rval;
+}
+
+ssize_t safe_getxattr(const char *file, const int lineno, const char *path,
+ const char *name, void *value, size_t size)
+{
+ ssize_t rval;
+
+ rval = getxattr(path, name, value, size);
+
+ if (rval == -1) {
+ if (errno == ENOTSUP) {
+ tst_brkm(TCONF, NULL,
+ "%s:%d: no xattr support in fs or mounted "
+ "without user_xattr option", file, lineno);
+ }
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: getxattr(%s, %s, %p, %zu) failed",
+ file, lineno, path, name, value, size);
+ }
+
+ return rval;
+}
+
+int safe_setxattr(const char *file, const int lineno, const char *path,
+ const char *name, const void *value, size_t size, int flags)
+{
+ int rval;
+
+ rval = setxattr(path, name, value, size, flags);
+
+ if (rval) {
+ if (errno == ENOTSUP) {
+ tst_brkm(TCONF, NULL,
+ "%s:%d: no xattr support in fs or mounted "
+ "without user_xattr option", file, lineno);
+ }
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: setxattr(%s, %s, %p, %zu) failed",
+ file, lineno, path, name, value, size);
+ }
+
+ return rval;
+}
+
+int safe_lsetxattr(const char *file, const int lineno, const char *path,
+ const char *name, const void *value, size_t size, int flags)
+{
+ int rval;
+
+ rval = lsetxattr(path, name, value, size, flags);
+
+ if (rval) {
+ if (errno == ENOTSUP) {
+ tst_brkm(TCONF, NULL,
+ "%s:%d: no xattr support in fs or mounted "
+ "without user_xattr option", file, lineno);
+ }
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: lsetxattr(%s, %s, %p, %zu, %i) failed",
+ file, lineno, path, name, value, size, flags);
+ }
+
+ return rval;
+}
+
+int safe_fsetxattr(const char *file, const int lineno, int fd, const char *name,
+ const void *value, size_t size, int flags)
+{
+ int rval;
+
+ rval = fsetxattr(fd, name, value, size, flags);
+
+ if (rval) {
+ if (errno == ENOTSUP) {
+ tst_brkm(TCONF, NULL,
+ "%s:%d: no xattr support in fs or mounted "
+ "without user_xattr option", file, lineno);
+ }
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: fsetxattr(%i, %s, %p, %zu, %i) failed",
+ file, lineno, fd, name, value, size, flags);
+ }
+
+ return rval;
+}
+
+int safe_removexattr(const char *file, const int lineno, const char *path,
+ const char *name)
+{
+ int rval;
+
+ rval = removexattr(path, name);
+
+ if (rval) {
+ if (errno == ENOTSUP) {
+ tst_brkm(TCONF, NULL,
+ "%s:%d: no xattr support in fs or mounted "
+ "without user_xattr option", file, lineno);
+ }
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: removexattr(%s, %s) failed",
+ file, lineno, path, name);
+ }
+
+ return rval;
+}
+
+int safe_lremovexattr(const char *file, const int lineno, const char *path,
+ const char *name)
+{
+ int rval;
+
+ rval = lremovexattr(path, name);
+
+ if (rval) {
+ if (errno == ENOTSUP) {
+ tst_brkm(TCONF, NULL,
+ "%s:%d: no xattr support in fs or mounted "
+ "without user_xattr option", file, lineno);
+ }
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: lremovexattr(%s, %s) failed",
+ file, lineno, path, name);
+ }
+
+ return rval;
+}
+
+int safe_fremovexattr(const char *file, const int lineno, int fd,
+ const char *name)
+{
+ int rval;
+
+ rval = fremovexattr(fd, name);
+
+ if (rval) {
+ if (errno == ENOTSUP) {
+ tst_brkm(TCONF, NULL,
+ "%s:%d: no xattr support in fs or mounted "
+ "without user_xattr option", file, lineno);
+ }
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: fremovexattr(%i, %s) failed",
+ file, lineno, fd, name);
+ }
+
+ return rval;
+}
+
+int safe_fsync(const char *file, const int lineno, int fd)
+{
+ int rval;
+
+ rval = fsync(fd);
+
+ if (rval) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: fsync(%i) failed", file, lineno, fd);
+ }
+
+ return rval;
+}
+
+pid_t safe_setsid(const char *file, const int lineno)
+{
+ pid_t rval;
+
+ rval = setsid();
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: setsid() failed", file, lineno);
+ }
+
+ return rval;
+}
+
+int safe_mknod(const char *file, const int lineno, const char *pathname,
+ mode_t mode, dev_t dev)
+{
+ int rval;
+
+ rval = mknod(pathname, mode, dev);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: mknod() failed", file, lineno);
+ }
+
+ return rval;
+}
+
+int safe_mlock(const char *file, const int lineno, const void *addr,
+ size_t len)
+{
+ int rval;
+
+ rval = mlock(addr, len);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: mlock() failed", file, lineno);
+ }
+
+ return rval;
+}
+
+int safe_munlock(const char *file, const int lineno, const void *addr,
+ size_t len)
+{
+ int rval;
+
+ rval = munlock(addr, len);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: munlock() failed", file, lineno);
+ }
+
+ return rval;
+}
+
+int safe_mincore(const char *file, const int lineno, void *start,
+ size_t length, unsigned char *vec)
+{
+ int rval;
+
+ rval = mincore(start, length, vec);
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: mincore() failed", file, lineno);
+ }
+
+ return rval;
+}
+
+int safe_sysinfo(const char *file, const int lineno, struct sysinfo *info)
+{
+ int ret;
+
+ errno = 0;
+ ret = sysinfo(info);
+
+ if (ret == -1) {
+ tst_brkm_(file, lineno, TBROK | TERRNO, NULL,
+ "sysinfo() failed");
+ } else if (ret) {
+ tst_brkm_(file, lineno, TBROK | TERRNO, NULL,
+ "Invalid sysinfo() return value %d", ret);
+ }
+
+ return ret;
+}
diff --git a/src/kernel/tests/lib/safe_net.c b/src/kernel/tests/lib/safe_net.c
new file mode 100644
index 0000000..4993680
--- /dev/null
+++ b/src/kernel/tests/lib/safe_net.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 2015 Fujitsu Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include "test.h"
+#include "safe_net_fn.h"
+
+char *tst_sock_addr(const struct sockaddr *sa, socklen_t salen, char *res,
+ size_t len)
+{
+ char portstr[8];
+
+ switch (sa->sa_family) {
+
+ case AF_INET: {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+ if (!inet_ntop(AF_INET, &sin->sin_addr, res, len))
+ return NULL;
+
+ if (ntohs(sin->sin_port) != 0) {
+ snprintf(portstr, sizeof(portstr), ":%d",
+ ntohs(sin->sin_port));
+ strcat(res, portstr);
+ }
+
+ return res;
+ }
+
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ res[0] = '[';
+ if (!inet_ntop(AF_INET6, &sin6->sin6_addr, res + 1, len - 1))
+ return NULL;
+
+ if (ntohs(sin6->sin6_port) != 0) {
+ snprintf(portstr, sizeof(portstr), "]:%d",
+ ntohs(sin6->sin6_port));
+ strcat(res, portstr);
+ return res;
+ }
+
+ return res + 1;
+ }
+
+ case AF_UNIX: {
+ struct sockaddr_un *unp = (struct sockaddr_un *)sa;
+
+ if (unp->sun_path[0] == '\0')
+ strcpy(res, "(no pathname bound)");
+ else
+ snprintf(res, len, "%s", unp->sun_path);
+
+ return res;
+ }
+
+ default: {
+ snprintf(res, len,
+ "sock_ntop: unknown AF_xxx: %d, len: %d",
+ sa->sa_family, salen);
+
+ return res;
+ }
+
+ }
+}
+
+int tst_getsockport(const char *file, const int lineno, int sockfd)
+{
+ struct sockaddr_storage ss;
+ socklen_t addrlen = sizeof(ss);
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+
+ safe_getsockname(file, lineno, NULL, sockfd, sa, &addrlen);
+
+ switch (sa->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+ return ntohs(sin->sin_port);
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ return ntohs(sin6->sin6_port);
+ } }
+
+ return -1;
+}
+
+int safe_socket(const char *file, const int lineno, void (cleanup_fn)(void),
+ int domain, int type, int protocol)
+{
+ int rval, ttype;
+
+ rval = socket(domain, type, protocol);
+
+ if (rval < 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ case ESOCKTNOSUPPORT:
+ case EOPNOTSUPP:
+ case EPFNOSUPPORT:
+ case EAFNOSUPPORT:
+ ttype = TCONF;
+ break;
+ default:
+ ttype = TBROK;
+ }
+
+ tst_brkm(ttype | TERRNO, cleanup_fn,
+ "%s:%d: socket(%d, %d, %d) failed", file, lineno,
+ domain, type, protocol);
+ }
+
+ return rval;
+}
+
+int safe_socketpair(const char *file, const int lineno, int domain, int type,
+ int protocol, int sv[])
+{
+ int rval, ttype;
+
+ rval = socketpair(domain, type, protocol, sv);
+
+ if (rval < 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ case EOPNOTSUPP:
+ case EAFNOSUPPORT:
+ ttype = TCONF;
+ break;
+ default:
+ ttype = TBROK;
+ }
+
+ tst_brkm(ttype | TERRNO, NULL,
+ "%s:%d: socketpair(%d, %d, %d, %p) failed",
+ file, lineno, domain, type, protocol, sv);
+ }
+
+ return rval;
+}
+
+int safe_getsockopt(const char *file, const int lineno, int sockfd, int level,
+ int optname, void *optval, socklen_t *optlen)
+{
+ int rval = getsockopt(sockfd, level, optname, optval, optlen);
+
+ if (!rval)
+ return 0;
+
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: getsockopt(%d, %d, %d, %p, %p) failed",
+ file, lineno, sockfd, level, optname, optval, optlen);
+
+ return rval;
+}
+
+int safe_setsockopt(const char *file, const int lineno, int sockfd, int level,
+ int optname, const void *optval, socklen_t optlen)
+{
+ int rval;
+
+ rval = setsockopt(sockfd, level, optname, optval, optlen);
+
+ if (rval) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: setsockopt(%d, %d, %d, %p, %d) failed",
+ file, lineno, sockfd, level, optname, optval, optlen);
+ }
+
+ return rval;
+}
+
+ssize_t safe_send(const char *file, const int lineno, char len_strict,
+ int sockfd, const void *buf, size_t len, int flags)
+{
+ ssize_t rval;
+
+ rval = send(sockfd, buf, len, flags);
+
+ if (rval == -1 || (len_strict && (size_t)rval != len)) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: send(%d, %p, %zu, %d) failed",
+ file, lineno, sockfd, buf, len, flags);
+ }
+
+ return rval;
+}
+
+ssize_t safe_sendto(const char *file, const int lineno, char len_strict,
+ int sockfd, const void *buf, size_t len, int flags,
+ const struct sockaddr *dest_addr, socklen_t addrlen)
+{
+ ssize_t rval;
+ char res[128];
+
+ rval = sendto(sockfd, buf, len, flags, dest_addr, addrlen);
+
+ if (rval == -1 || (len_strict && (size_t)rval != len)) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: sendto(%d, %p, %zu, %d, %s, %d) failed",
+ file, lineno, sockfd, buf, len, flags,
+ tst_sock_addr(dest_addr, addrlen, res, sizeof(res)),
+ addrlen);
+ }
+
+ return rval;
+}
+
+ssize_t safe_sendmsg(const char *file, const int lineno, size_t len,
+ int sockfd, const struct msghdr *msg, int flags)
+{
+ ssize_t rval;
+
+ rval = sendmsg(sockfd, msg, flags);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: sendmsg(%d, %p, %d) failed",
+ file, lineno, sockfd, msg, flags);
+ }
+
+ if (len && (size_t)rval != len) {
+ tst_brkm(TBROK, NULL,
+ "%s:%d: sendmsg(%d, %p, %d) ret(%zd) != len(%zu)",
+ file, lineno, sockfd, msg, flags, rval, len);
+ }
+
+ return rval;
+}
+
+ssize_t safe_recvmsg(const char *file, const int lineno, size_t len,
+ int sockfd, struct msghdr *msg, int flags)
+{
+ ssize_t rval;
+
+ rval = recvmsg(sockfd, msg, flags);
+
+ if (rval == -1) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: recvmsg(%d, %p, %d) failed",
+ file, lineno, sockfd, msg, flags);
+ }
+
+ if (len && (size_t)rval != len) {
+ tst_brkm(TBROK, NULL,
+ "%s:%d: recvmsg(%d, %p, %d) ret(%zd) != len(%zu)",
+ file, lineno, sockfd, msg, flags, rval, len);
+ }
+
+ return rval;
+
+}
+
+int safe_bind(const char *file, const int lineno, void (cleanup_fn)(void),
+ int socket, const struct sockaddr *address,
+ socklen_t address_len)
+{
+ int i;
+ char buf[128];
+
+ for (i = 0; i < 120; i++) {
+ if (!bind(socket, address, address_len))
+ return 0;
+
+ if (errno != EADDRINUSE) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: bind(%d, %s, %d) failed", file, lineno,
+ socket, tst_sock_addr(address, address_len,
+ buf, sizeof(buf)),
+ address_len);
+ return -1;
+ }
+
+ if ((i + 1) % 10 == 0) {
+ tst_resm(TINFO, "address is in use, waited %3i sec",
+ i + 1);
+ }
+
+ sleep(1);
+ }
+
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: Failed to bind(%d, %s, %d) after 120 retries", file,
+ lineno, socket,
+ tst_sock_addr(address, address_len, buf, sizeof(buf)),
+ address_len);
+ return -1;
+}
+
+int safe_listen(const char *file, const int lineno, void (cleanup_fn)(void),
+ int socket, int backlog)
+{
+ int rval;
+
+ rval = listen(socket, backlog);
+
+ if (rval < 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: listen(%d, %d) failed", file, lineno, socket,
+ backlog);
+ }
+
+ return rval;
+}
+
+int safe_accept(const char *file, const int lineno, void (cleanup_fn)(void),
+ int sockfd, struct sockaddr *addr, socklen_t *addrlen)
+{
+ int rval;
+
+ rval = accept(sockfd, addr, addrlen);
+
+ if (rval < 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: accept(%d, %p, %d) failed", file, lineno,
+ sockfd, addr, *addrlen);
+ }
+
+ return rval;
+}
+
+int safe_connect(const char *file, const int lineno, void (cleanup_fn)(void),
+ int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+ int rval;
+ char buf[128];
+
+ rval = connect(sockfd, addr, addrlen);
+
+ if (rval < 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: connect(%d, %s, %d) failed", file, lineno,
+ sockfd, tst_sock_addr(addr, addrlen, buf,
+ sizeof(buf)), addrlen);
+ }
+
+ return rval;
+}
+
+int safe_getsockname(const char *file, const int lineno,
+ void (cleanup_fn)(void), int sockfd, struct sockaddr *addr,
+ socklen_t *addrlen)
+{
+ int rval;
+ char buf[128];
+
+ rval = getsockname(sockfd, addr, addrlen);
+
+ if (rval < 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: getsockname(%d, %s, %d) failed", file, lineno,
+ sockfd, tst_sock_addr(addr, *addrlen, buf,
+ sizeof(buf)), *addrlen);
+ }
+
+ return rval;
+}
+
+int safe_gethostname(const char *file, const int lineno,
+ char *name, size_t size)
+{
+ int rval = gethostname(name, size);
+
+ if (rval < 0) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s:%d: gethostname(%p, %zu) failed",
+ file, lineno, name, size);
+ }
+
+ return rval;
+}
+
+/*
+ * @return port in network byte order.
+ */
+unsigned short tst_get_unused_port(const char *file, const int lineno,
+ void (cleanup_fn)(void), unsigned short family, int type)
+{
+ int sock;
+ socklen_t slen;
+ struct sockaddr_storage _addr;
+ struct sockaddr *addr = (struct sockaddr *)&_addr;
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
+
+ switch (family) {
+ case AF_INET:
+ addr4->sin_family = AF_INET;
+ addr4->sin_port = 0;
+ addr4->sin_addr.s_addr = INADDR_ANY;
+ slen = sizeof(*addr4);
+ break;
+
+ case AF_INET6:
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = 0;
+ addr6->sin6_addr = in6addr_any;
+ slen = sizeof(*addr6);
+ break;
+
+ default:
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: unknown family", file, lineno);
+ return -1;
+ }
+
+ sock = socket(addr->sa_family, type, 0);
+ if (sock < 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: socket failed", file, lineno);
+ return -1;
+ }
+
+ if (bind(sock, addr, slen) < 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: bind failed", file, lineno);
+ return -1;
+ }
+
+ if (getsockname(sock, addr, &slen) == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: getsockname failed", file, lineno);
+ return -1;
+ }
+
+ if (close(sock) == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: close failed", file, lineno);
+ return -1;
+ }
+
+ switch (family) {
+ case AF_INET:
+ return addr4->sin_port;
+ case AF_INET6:
+ return addr6->sin6_port;
+ default:
+ return -1;
+ }
+}
diff --git a/src/kernel/tests/lib/safe_pthread.c b/src/kernel/tests/lib/safe_pthread.c
new file mode 100644
index 0000000..2866aa5
--- /dev/null
+++ b/src/kernel/tests/lib/safe_pthread.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
+ */
+
+#include <pthread.h>
+#include <stdio.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+
+int safe_pthread_create(const char *file, const int lineno,
+ pthread_t *thread_id, const pthread_attr_t *attr,
+ void *(*thread_fn)(void *), void *arg)
+{
+ int rval;
+
+ rval = pthread_create(thread_id, attr, thread_fn, arg);
+
+ if (rval) {
+ tst_brk_(file, lineno, TBROK,
+ "pthread_create(%p,%p,%p,%p) failed: %s", thread_id,
+ attr, thread_fn, arg, tst_strerrno(rval));
+ }
+
+ return rval;
+}
+
+int safe_pthread_join(const char *file, const int lineno,
+ pthread_t thread_id, void **retval)
+{
+ int rval;
+
+ rval = pthread_join(thread_id, retval);
+
+ if (rval) {
+ tst_brk_(file, lineno, TBROK,
+ "pthread_join(..., %p) failed: %s",
+ retval, tst_strerrno(rval));
+ }
+
+ return rval;
+}
diff --git a/src/kernel/tests/lib/safe_stdio.c b/src/kernel/tests/lib/safe_stdio.c
new file mode 100644
index 0000000..966a039
--- /dev/null
+++ b/src/kernel/tests/lib/safe_stdio.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2013 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include "test.h"
+#include "safe_stdio_fn.h"
+
+FILE *safe_fopen(const char *file, const int lineno, void (cleanup_fn)(void),
+ const char *path, const char *mode)
+{
+ FILE *f = fopen(path, mode);
+
+ if (f == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: fopen(%s,%s) failed",
+ file, lineno, path, mode);
+ }
+
+ return f;
+}
+
+int safe_fclose(const char *file, const int lineno, void (cleanup_fn)(void),
+ FILE *f)
+{
+ int ret;
+
+ ret = fclose(f);
+
+ if (ret) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: fclose(%p) failed", file, lineno, f);
+ }
+
+ return ret;
+}
+
+int safe_asprintf(const char *file, const int lineno, void (cleanup_fn)(void),
+ char **strp, const char *fmt, ...)
+{
+ int ret;
+ va_list va;
+
+ va_start(va, fmt);
+ ret = vasprintf(strp, fmt, va);
+ va_end(va);
+
+ if (ret < 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: asprintf(%s,...) failed", file, lineno, fmt);
+ }
+
+ return ret;
+}
+
+FILE *safe_popen(const char *file, const int lineno, void (cleanup_fn)(void),
+ const char *command, const char *type)
+{
+ FILE *stream;
+ const int saved_errno = errno;
+
+ errno = 0;
+ stream = popen(command, type);
+
+ if (stream == NULL) {
+ if (errno != 0) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: popen(%s,%s) failed",
+ file, lineno, command, type);
+ } else {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: popen(%s,%s) failed: Out of memory",
+ file, lineno, command, type);
+ }
+ }
+
+ errno = saved_errno;
+
+ return stream;
+}
diff --git a/src/kernel/tests/lib/self_exec.c b/src/kernel/tests/lib/self_exec.c
new file mode 100644
index 0000000..de7d095
--- /dev/null
+++ b/src/kernel/tests/lib/self_exec.c
@@ -0,0 +1,225 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: t -*- */
+/*
+ * self_exec.c: self_exec magic required to run child functions on uClinux
+ *
+ * Copyright (C) 2005 Paul J.Y. Lahaie <pjlahaie-at-steamballoon.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * This software was produced by Steamballoon Incorporated
+ * 55 Byward Market Square, 2nd Floor North, Ottawa, ON K1N 9C3, Canada
+ */
+
+#define _GNU_SOURCE /* for asprintf */
+
+#include "config.h"
+
+#ifdef UCLINUX
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include "test.h"
+#include "safe_macros.h"
+
+/* Set from parse_opts.c: */
+char *child_args; /* Arguments to child when -C is used */
+
+static char *start_cwd; /* Stores the starting directory for self_exec */
+
+int asprintf(char **app, const char *fmt, ...)
+{
+ va_list ptr;
+ int rv;
+ char *p;
+
+ /*
+ * First iteration - find out size of buffer required and allocate it.
+ */
+ va_start(ptr, fmt);
+ rv = vsnprintf(NULL, 0, fmt, ptr);
+ va_end(ptr);
+
+ p = malloc(++rv); /* allocate the buffer */
+ *app = p;
+ if (!p) {
+ return -1;
+ }
+
+ /*
+ * Second iteration - actually produce output.
+ */
+ va_start(ptr, fmt);
+ rv = vsnprintf(p, rv, fmt, ptr);
+ va_end(ptr);
+
+ return rv;
+}
+
+void maybe_run_child(void (*child) (), const char *fmt, ...)
+{
+ va_list ap;
+ char *child_dir;
+ char *p, *tok;
+ int *iptr, i, j;
+ char *s;
+ char **sptr;
+ char *endptr;
+
+ /* Store the current directory for later use. */
+ start_cwd = getcwd(NULL, 0);
+
+ if (child_args) {
+ char *args = strdup(child_args);
+
+ child_dir = strtok(args, ",");
+ if (strlen(child_dir) == 0) {
+ tst_brkm(TBROK, NULL,
+ "Could not get directory from -C option");
+ return;
+ }
+
+ va_start(ap, fmt);
+
+ for (p = fmt; *p; p++) {
+ tok = strtok(NULL, ",");
+ if (!tok || strlen(tok) == 0) {
+ tst_brkm(TBROK, NULL,
+ "Invalid argument to -C option");
+ return;
+ }
+
+ switch (*p) {
+ case 'd':
+ iptr = va_arg(ap, int *);
+ i = strtol(tok, &endptr, 10);
+ if (*endptr != '\0') {
+ tst_brkm(TBROK, NULL,
+ "Invalid argument to -C option");
+ return;
+ }
+ *iptr = i;
+ break;
+ case 'n':
+ j = va_arg(ap, int);
+ i = strtol(tok, &endptr, 10);
+ if (*endptr != '\0') {
+ tst_brkm(TBROK, NULL,
+ "Invalid argument to -C option");
+ return;
+ }
+ if (j != i) {
+ va_end(ap);
+ free(args);
+ return;
+ }
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ if (!strncpy(s, tok, strlen(tok) + 1)) {
+ tst_brkm(TBROK, NULL,
+ "Could not strncpy for -C option");
+ return;
+ }
+ break;
+ case 'S':
+ sptr = va_arg(ap, char **);
+ *sptr = strdup(tok);
+ if (!*sptr) {
+ tst_brkm(TBROK, NULL,
+ "Could not strdup for -C option");
+ return;
+ }
+ break;
+ default:
+ tst_brkm(TBROK, NULL,
+ "Format string option %c not implemented",
+ *p);
+ return;
+ }
+ }
+
+ va_end(ap);
+ free(args);
+ SAFE_CHDIR(NULL, child_dir);
+
+ (*child) ();
+ tst_resm(TWARN, "Child function returned unexpectedly");
+ /* Exit here? or exit silently? */
+ }
+}
+
+int self_exec(const char *argv0, const char *fmt, ...)
+{
+ va_list ap;
+ char *p;
+ char *tmp_cwd;
+ char *arg;
+ int ival;
+ char *str;
+
+ if ((tmp_cwd = getcwd(NULL, 0)) == NULL) {
+ tst_resm(TBROK, "Could not getcwd()");
+ return -1;
+ }
+
+ arg = strdup(tmp_cwd);
+ if (arg == NULL) {
+ tst_resm(TBROK, "Could not produce self_exec string");
+ return -1;
+ }
+
+ va_start(ap, fmt);
+
+ for (p = fmt; *p; p++) {
+ switch (*p) {
+ case 'd':
+ case 'n':
+ ival = va_arg(ap, int);
+ if (asprintf(&arg, "%s,%d", arg, ival) < 0) {
+ tst_resm(TBROK,
+ "Could not produce self_exec string");
+ return -1;
+ }
+ break;
+ case 's':
+ case 'S':
+ str = va_arg(ap, char *);
+ if (asprintf(&arg, "%s,%s", arg, str) < 0) {
+ tst_resm(TBROK,
+ "Could not produce self_exec string");
+ return -1;
+ }
+ break;
+ default:
+ tst_resm(TBROK,
+ "Format string option %c not implemented", *p);
+ return -1;
+ break;
+ }
+ }
+
+ va_end(ap);
+
+ if (chdir(start_cwd) < 0) {
+ tst_resm(TBROK, "Could not change to %s for self_exec",
+ start_cwd);
+ return -1;
+ }
+
+ return execlp(argv0, argv0, "-C", arg, (char *)NULL);
+}
+
+#endif /* UCLINUX */
diff --git a/src/kernel/tests/lib/signame.h b/src/kernel/tests/lib/signame.h
new file mode 100644
index 0000000..d420458
--- /dev/null
+++ b/src/kernel/tests/lib/signame.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 Fujitsu Ltd.
+ * Author: Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+const char *tst_strsig(int sig)
+{
+ static const struct pair signal_pairs[] = {
+ PAIR(SIGHUP)
+ PAIR(SIGINT)
+ PAIR(SIGQUIT)
+ PAIR(SIGILL)
+ #ifdef SIGTRAP
+ PAIR(SIGTRAP)
+ #endif
+
+ #ifdef SIGIOT
+ /* SIGIOT same as SIGABRT */
+ STRPAIR(SIGABRT, "SIGIOT/SIGABRT")
+ #else
+ PAIR(SIGABRT)
+ #endif
+
+ #ifdef SIGEMT
+ PAIR(SIGEMT)
+ #endif
+ #ifdef SIGBUS
+ PAIR(SIGBUS)
+ #endif
+ PAIR(SIGFPE)
+ PAIR(SIGKILL)
+ PAIR(SIGUSR1)
+ PAIR(SIGSEGV)
+ PAIR(SIGUSR2)
+ PAIR(SIGPIPE)
+ PAIR(SIGALRM)
+ PAIR(SIGTERM)
+ #ifdef SIGSTKFLT
+ PAIR(SIGSTKFLT)
+ #endif
+ PAIR(SIGCHLD)
+ PAIR(SIGCONT)
+ PAIR(SIGSTOP)
+ PAIR(SIGTSTP)
+ PAIR(SIGTTIN)
+ PAIR(SIGTTOU)
+ #ifdef SIGURG
+ PAIR(SIGURG)
+ #endif
+ #ifdef SIGXCPU
+ PAIR(SIGXCPU)
+ #endif
+ #ifdef SIGXFSZ
+ PAIR(SIGXFSZ)
+ #endif
+ #ifdef SIGVTALRM
+ PAIR(SIGVTALRM)
+ #endif
+ #ifdef SIGPROF
+ PAIR(SIGPROF)
+ #endif
+ #ifdef SIGWINCH
+ PAIR(SIGWINCH)
+ #endif
+
+ #if defined(SIGIO) && defined(SIGPOLL)
+ /* SIGPOLL same as SIGIO */
+ STRPAIR(SIGIO, "SIGIO/SIGPOLL")
+ #elif defined(SIGIO)
+ PAIR(SIGIO)
+ #elif defined(SIGPOLL)
+ PAIR(SIGPOLL)
+ #endif
+
+ #ifdef SIGINFO
+ PAIR(SIGINFO)
+ #endif
+ #ifdef SIGLOST
+ PAIR(SIGLOST)
+ #endif
+ #ifdef SIGPWR
+ PAIR(SIGPWR)
+ #endif
+ #if defined(SIGSYS)
+ /*
+ * According to signal(7)'s manpage, SIGUNUSED is synonymous
+ * with SIGSYS on most architectures.
+ */
+ STRPAIR(SIGSYS, "SIGSYS/SIGUNUSED")
+ #endif
+ };
+
+ PAIR_LOOKUP(signal_pairs, sig);
+};
diff --git a/src/kernel/tests/lib/tlibio.c b/src/kernel/tests/lib/tlibio.c
new file mode 100644
index 0000000..cc110d1
--- /dev/null
+++ b/src/kernel/tests/lib/tlibio.c
@@ -0,0 +1,2161 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ *
+ * Lib i/o
+ *
+ * This file contains several functions to doing reads and writes.
+ * It was written so that a single function could be called in a test
+ * program and only a io type field value would have to change to
+ * do different types of io. There is even a couple of functions that
+ * will allow you to parse a string to determine the iotype.
+ *
+ * This file contains functions for writing/reading to/from open files
+ * Prototypes:
+ *
+ * Functions declared in this module - see individual function code for
+ * usage comments:
+ *
+ * int stride_bounds(int offset, int stride, int nstrides,
+ * int bytes_per_stride, int *min, int *max);
+
+ * int lio_write_buffer(int fd, int method, char *buffer, int size,
+ * char **errmsg, long wrd);
+ * int lio_read_buffer(int fd, int method, char *buffer, int size,
+ * char **errmsg, long wrd);
+ *
+ * #ifdef CRAY
+ * int lio_wait4asyncio(int method, int fd, struct iosw **statptr)
+ * int lio_check_asyncio(char *io_type, int size, struct iosw *status)
+ * #endif
+ * #ifdef sgi
+ * int lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp)
+ * int lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method)
+ * #endif
+ *
+ * int lio_parse_io_arg1(char *string)
+ * void lio_help1(char *prefix);
+ *
+ * int lio_parse_io_arg2(char *string, char **badtoken)
+ * void lio_help2(char *prefix);
+ *
+ * int lio_set_debug(int level);
+ *
+ * char Lio_SysCall[];
+ * struct lio_info_type Lio_info1[];
+ * struct lio_info_type Lio_info2[];
+ *
+ * Author : Richard Logan
+ *
+ */
+
+#ifdef __linux__
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#define _LARGEFILE64_SOURCE
+#endif
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <stdint.h>
+#ifdef CRAY
+#include <sys/secparm.h>
+#include <sys/iosw.h>
+#include <sys/listio.h>
+#else
+/* for linux or sgi */
+#include <sys/uio.h> /* readv(2)/writev(2) */
+#include <string.h>
+#endif
+#if defined(__linux__) || defined(__sun) || defined(__hpux) || defined(_AIX)
+#if !defined(UCLINUX) && !defined(__UCLIBC__)
+#include <aio.h>
+#endif
+#endif
+#include <stdlib.h> /* atoi, abs */
+
+#include "tlibio.h" /* defines LIO* marcos */
+#include "random_range.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX MAXPATHLEN
+#endif
+
+#if 0 /* disabled until it's needed -- roehrich 6/11/97 */
+#define BUG1_workaround 1 /* Work around a condition where aio_return gives
+ * a value of zero but there is no errno followup
+ * and the read/write operation actually did its
+ * job. spr/pv 705244
+ */
+#endif
+
+
+/*
+ * Define the structure as used in lio_parse_arg1 and lio_help1
+ */
+struct lio_info_type Lio_info1[] = {
+ {"s", LIO_IO_SYNC, "sync i/o"},
+ {"p", LIO_IO_ASYNC | LIO_WAIT_SIGACTIVE,
+ "async i/o using a loop to wait for a signal"},
+ {"b", LIO_IO_ASYNC | LIO_WAIT_SIGPAUSE, "async i/o using pause"},
+ {"a", LIO_IO_ASYNC | LIO_WAIT_RECALL,
+ "async i/o using recall/aio_suspend"},
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ {"r",
+ LIO_RANDOM | LIO_IO_TYPES | LIO_WAIT_TYPES,
+ "random sync i/o types and wait methods"},
+ {"R",
+ LIO_RANDOM | LIO_IO_ATYPES | LIO_WAIT_ATYPES,
+ "random i/o types and wait methods"},
+#else
+ {"r",
+ LIO_RANDOM | LIO_IO_TYPES | LIO_WAIT_TYPES,
+ "random i/o types and wait methods"},
+ {"R",
+ LIO_RANDOM | LIO_IO_TYPES | LIO_WAIT_TYPES,
+ "random i/o types and wait methods"},
+#endif
+ {"l", LIO_IO_SLISTIO | LIO_WAIT_RECALL, "single stride sync listio"},
+ {"L", LIO_IO_ALISTIO | LIO_WAIT_RECALL,
+ "single stride async listio using recall"},
+ {"X", LIO_IO_ALISTIO | LIO_WAIT_SIGPAUSE,
+ "single stride async listio using pause"},
+ {"v", LIO_IO_SYNCV, "single buffer sync readv/writev"},
+ {"P", LIO_IO_SYNCP, "sync pread/pwrite"},
+};
+
+/*
+ * Define the structure used by lio_parse_arg2 and lio_help2
+ */
+struct lio_info_type Lio_info2[] = {
+ {"sync", LIO_IO_SYNC, "sync i/o (read/write)"},
+ {"async", LIO_IO_ASYNC, "async i/o (reada/writea/aio_read/aio_write)"},
+ {"slistio", LIO_IO_SLISTIO, "single stride sync listio"},
+ {"alistio", LIO_IO_ALISTIO, "single stride async listio"},
+ {"syncv", LIO_IO_SYNCV, "single buffer sync readv/writev"},
+ {"syncp", LIO_IO_SYNCP, "pread/pwrite"},
+ {"active", LIO_WAIT_ACTIVE, "spin on status/control values"},
+ {"recall", LIO_WAIT_RECALL,
+ "use recall(2)/aio_suspend(3) to wait for i/o to complete"},
+ {"sigactive", LIO_WAIT_SIGACTIVE, "spin waiting for signal"},
+ {"sigpause", LIO_WAIT_SIGPAUSE, "call pause(2) to wait for signal"},
+/* nowait is a touchy thing, it's an accident that this implementation worked at all. 6/27/97 roehrich */
+/* { "nowait", LIO_WAIT_NONE, "do not wait for async io to complete" },*/
+ {"random", LIO_RANDOM, "set random bit"},
+ {"randomall",
+ LIO_RANDOM | LIO_IO_TYPES | LIO_WAIT_TYPES,
+ "all random i/o types and wait methods (except nowait)"},
+};
+
+char Lio_SysCall[PATH_MAX]; /* string containing last i/o system call */
+
+static volatile int Received_signal = 0; /* number of signals received */
+static volatile int Rec_signal;
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+static volatile int Received_callback = 0; /* number of callbacks received */
+static volatile int Rec_callback;
+#endif
+static char Errormsg[500];
+static int Debug_level = 0;
+
+/***********************************************************************
+ * stride_bounds()
+ *
+ * Determine the bounds of a strided request, normalized to offset. Returns
+ * the number of bytes needed to satisfy the request, and optionally sets
+ * *min and *max to the mininum and maximum bytes referenced, normalized
+ * around offset.
+ *
+ * Returns -1 on error - the only possible error conditions are illegal values
+ * for nstrides and/or bytes_per_stride - both parameters must be >= 0.
+ *
+ * (maule, 11/16/95)
+ ***********************************************************************/
+
+int stride_bounds(int offset, int stride, int nstrides, int bytes_per_stride,
+ int *min, int *max)
+{
+ int nbytes, min_byte, max_byte;
+
+ /*
+ * sanity checks ...
+ */
+
+ if (nstrides < 0 || bytes_per_stride < 0) {
+ return -1;
+ }
+
+ if (stride == 0) {
+ stride = bytes_per_stride;
+ }
+
+ /*
+ * Determine the # of bytes needed to satisfy the request. This
+ * value, along with the offset argument, determines the min and max
+ * bytes referenced.
+ */
+
+ nbytes = abs(stride) * (nstrides - 1) + bytes_per_stride;
+
+ if (stride < 0) {
+ max_byte = offset + bytes_per_stride - 1;
+ min_byte = max_byte - nbytes + 1;
+ } else {
+ min_byte = offset;
+ max_byte = min_byte + nbytes - 1;
+ }
+
+ if (min != NULL) {
+ *min = min_byte;
+ }
+
+ if (max != NULL) {
+ *max = max_byte;
+ }
+
+ return nbytes;
+}
+
+/***********************************************************************
+ * This function will allow someone to set the debug level.
+ ***********************************************************************/
+int lio_set_debug(int level)
+{
+ int old;
+
+ old = Debug_level;
+ Debug_level = level;
+ return old;
+}
+
+/***********************************************************************
+ * This function will parse a string and return desired io-method.
+ * Only the first character of the string is used.
+ *
+ * This function does not provide for meaningful option arguments,
+ * but it supports current growfiles/btlk interface.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int lio_parse_io_arg1(char *string)
+{
+ unsigned int ind;
+ int found = 0;
+ int mask = 0;
+
+ /*
+ * Determine if token is a valid string.
+ */
+ for (ind = 0; ind < sizeof(Lio_info1) / sizeof(struct lio_info_type);
+ ind++) {
+ if (strcmp(string, Lio_info1[ind].token) == 0) {
+ mask |= Lio_info1[ind].bits;
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ return -1;
+ }
+
+ return mask;
+
+}
+
+/***********************************************************************
+ * This function will print a help message describing the characters
+ * that can be parsed by lio_parse_io_arg1().
+ * They will be printed one per line.
+ * (rrl 04/96)
+ ***********************************************************************/
+void lio_help1(char *prefix)
+{
+ unsigned int ind;
+
+ for (ind = 0; ind < sizeof(Lio_info1) / sizeof(struct lio_info_type);
+ ind++) {
+ printf("%s %s : %s\n", prefix, Lio_info1[ind].token,
+ Lio_info1[ind].desc);
+ }
+
+ return;
+}
+
+/***********************************************************************
+ * This function will parse a string and return the desired io-method.
+ * This function will take a comma separated list of io type and wait
+ * method tokens as defined in Lio_info2[]. If a token does not match
+ * any of the tokens in Lio_info2[], it will be coverted to a number.
+ * If it was a number, those bits are also set.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int lio_parse_io_arg2(char *string, char **badtoken)
+{
+ char *token = string;
+ char *cc = token;
+ char savecc;
+ int found;
+ int mask = 0;
+
+ int tmp;
+ unsigned int ind;
+ char chr;
+
+ if (token == NULL)
+ return -1;
+
+ for (;;) {
+ for (; ((*cc != ',') && (*cc != '\0')); cc++) ;
+ savecc = *cc;
+ *cc = '\0';
+
+ found = 0;
+
+ /*
+ * Determine if token is a valid string or number and if
+ * so, add the bits to the mask.
+ */
+ for (ind = 0;
+ ind < sizeof(Lio_info2) / sizeof(struct lio_info_type);
+ ind++) {
+ if (strcmp(token, Lio_info2[ind].token) == 0) {
+ mask |= Lio_info2[ind].bits;
+ found = 1;
+ break;
+ }
+ }
+
+ /*
+ * If token does not match one of the defined tokens, determine
+ * if it is a number, if so, add the bits.
+ */
+ if (!found) {
+ if (sscanf(token, "%i%c", &tmp, &chr) == 1) {
+ mask |= tmp;
+ found = 1;
+ }
+ }
+
+ *cc = savecc;
+
+ if (!found) { /* token is not valid */
+ if (badtoken != NULL)
+ *badtoken = token;
+ return (-1);
+ }
+
+ if (savecc == '\0')
+ break;
+
+ token = ++cc;
+ }
+
+ return mask;
+}
+
+/***********************************************************************
+ * This function will print a help message describing the tokens
+ * that can be parsed by lio_parse_io_arg2().
+ * It will print them one per line.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+void lio_help2(char *prefix)
+{
+ unsigned int ind;
+
+ for (ind = 0; ind < sizeof(Lio_info2) / sizeof(struct lio_info_type);
+ ind++) {
+ printf("%s %s : %s\n", prefix, Lio_info2[ind].token,
+ Lio_info2[ind].desc);
+ }
+ return;
+}
+
+/***********************************************************************
+ * This is an internal signal handler.
+ * If the handler is called, it will increment the Received_signal
+ * global variable.
+ ***********************************************************************/
+static void lio_async_signal_handler(int sig)
+{
+ if (Debug_level)
+ printf
+ ("DEBUG %s/%d: received signal %d, a signal caught %d times\n",
+ __FILE__, __LINE__, sig, Received_signal + 1);
+
+ Received_signal++;
+
+ return;
+}
+
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+/***********************************************************************
+ * This is an internal callback handler.
+ * If the handler is called, it will increment the Received_callback
+ * global variable.
+ ***********************************************************************/
+static void lio_async_callback_handler(union sigval sigval)
+{
+ if (Debug_level)
+ printf
+ ("DEBUG %s/%d: received callback, nbytes=%ld, a callback called %d times\n",
+ __FILE__, __LINE__, (long)sigval.sival_int,
+ Received_callback + 1);
+
+ Received_callback++;
+
+ return;
+}
+#endif /* sgi */
+
+/***********************************************************************
+ * lio_random_methods
+ * This function will randomly choose an io type and wait method
+ * from set of io types and wait methods. Since this information
+ * is stored in a bitmask, it randomly chooses an io type from
+ * the io type bits specified and does the same for wait methods.
+ *
+ * Return Value
+ * This function will return a value with all non choosen io type
+ * and wait method bits cleared. The LIO_RANDOM bit is also
+ * cleared. All other bits are left unchanged.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int lio_random_methods(long curr_mask)
+{
+ int mask = 0;
+
+ /* remove random select, io type, and wait method bits from curr_mask */
+ mask = curr_mask & (~(LIO_IO_TYPES | LIO_WAIT_TYPES | LIO_RANDOM));
+
+ /* randomly select io type from specified io types */
+ mask = mask | random_bit(curr_mask & LIO_IO_TYPES);
+
+ /* randomly select wait methods from specified wait methods */
+ mask = mask | random_bit(curr_mask & LIO_WAIT_TYPES);
+
+ return mask;
+}
+
+static void wait4sync_io(int fd, int read)
+{
+ fd_set s;
+ FD_ZERO(&s);
+ FD_SET(fd, &s);
+
+ select(fd + 1, read ? &s : NULL, read ? NULL : &s, NULL, NULL);
+}
+
+/***********************************************************************
+ * Generic write function
+ * This function can be used to do a write using write(2), writea(2),
+ * aio_write(3), writev(2), pwrite(2),
+ * or single stride listio(2)/lio_listio(3).
+ * By setting the desired bits in the method
+ * bitmask, the caller can control the type of write and the wait method
+ * that will be used. If no io type bits are set, write will be used.
+ *
+ * If async io was attempted and no wait method bits are set then the
+ * wait method is: recall(2) for writea(2) and listio(2); aio_suspend(3) for
+ * aio_write(3) and lio_listio(3).
+ *
+ * If multiple wait methods are specified,
+ * only one wait method will be used. The order is predetermined.
+ *
+ * If the call specifies a signal and one of the two signal wait methods,
+ * a signal handler for the signal is set. This will reset an already
+ * set handler for this signal.
+ *
+ * If the LIO_RANDOM method bit is set, this function will randomly
+ * choose a io type and wait method from bits in the method argument.
+ *
+ * If an error is encountered, an error message will be generated
+ * in a internal static buffer. If errmsg is not NULL, it will
+ * be updated to point to the static buffer, allowing the caller
+ * to print the error message.
+ *
+ * Return Value
+ * If a system call fails, -errno is returned.
+ * If LIO_WAIT_NONE bit is set, the return value is the return value
+ * of the system call.
+ * If the io did not fail, the amount of data written is returned.
+ * If the size the system call say was written is different
+ * then what was asked to be written, errmsg is updated for
+ * this error condition. The return value is still the amount
+ * the system call says was written.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int lio_write_buffer(int fd, /* open file descriptor */
+ int method, /* contains io type and wait method bitmask */
+ char *buffer, /* pointer to buffer */
+ int size, /* the size of the io */
+ int sig, /* signal to use if async io */
+ char **errmsg, /* char pointer that will be updated to point to err message */
+ long wrd) /* to allow future features, use zero for now */
+{
+ int ret = 0; /* syscall return or used to get random method */
+ char *io_type; /* Holds string of type of io */
+ int omethod = method;
+ int listio_cmd; /* Holds the listio/lio_listio cmd */
+#ifdef CRAY
+ struct listreq request; /* Used when a listio is wanted */
+ struct iosw status, *statptr[1];
+#else
+ /* for linux or sgi */
+ struct iovec iov; /* iovec for writev(2) */
+#endif
+#if defined (sgi)
+ aiocb_t aiocbp; /* POSIX aio control block */
+ aiocb_t *aiolist[1]; /* list of aio control blocks for lio_listio */
+ off64_t poffset; /* pwrite(2) offset */
+#endif
+#if defined(__linux__) && !defined(__UCLIBC__)
+ struct aiocb aiocbp; /* POSIX aio control block */
+ struct aiocb *aiolist[1]; /* list of aio control blocks for lio_listio */
+ off64_t poffset; /* pwrite(2) offset */
+#endif
+ /*
+ * If LIO_RANDOM bit specified, get new method randomly.
+ */
+ if (method & LIO_RANDOM) {
+ if (Debug_level > 3)
+ printf("DEBUG %s/%d: method mask to choose from: %#o\n",
+ __FILE__, __LINE__, method);
+ method = lio_random_methods(method);
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: random chosen method %#o\n",
+ __FILE__, __LINE__, method);
+ }
+
+ if (errmsg != NULL)
+ *errmsg = Errormsg;
+
+ Rec_signal = Received_signal; /* get the current number of signals received */
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ Rec_callback = Received_callback; /* get the current number of callbacks received */
+#endif
+
+#ifdef CRAY
+ memset(&status, 0x00, sizeof(struct iosw));
+ memset(&request, 0x00, sizeof(struct listreq));
+ statptr[0] = &status;
+#else
+ /* for linux or sgi */
+ memset(&iov, 0x00, sizeof(struct iovec));
+ iov.iov_base = buffer;
+ iov.iov_len = size;
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+#if defined(sgi)
+ memset(&aiocbp, 0x00, sizeof(aiocb_t));
+#else
+ memset(&aiocbp, 0x00, sizeof(struct aiocb));
+#endif
+ aiocbp.aio_fildes = fd;
+ aiocbp.aio_nbytes = size;
+ aiocbp.aio_buf = buffer;
+/* aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE;
+ aiocbp.aio_sigevent.sigev_signo = 0;
+#ifdef sgi
+ aiocbp.aio_sigevent.sigev_func = NULL;
+ aiocbp.aio_sigevent.sigev_value.sival_int = 0;
+#elif defined(__linux__) && !defined(__UCLIBC__)
+ aiocbp.aio_sigevent.sigev_notify_function = NULL;
+ aiocbp.aio_sigevent.sigev_notify_attributes = 0;
+#endif
+ aiolist[0] = &aiocbp;
+
+ if ((ret = lseek(fd, 0, SEEK_CUR)) == -1) {
+ ret = 0;
+ /* If there is an error and it is not ESPIPE then kick out the error.
+ * If the fd is a fifo then we have to make sure that
+ * lio_random_methods() didn't select pwrite/pread; if it did then
+ * switch to write/read.
+ */
+ if (errno == ESPIPE) {
+ if (method & LIO_IO_SYNCP) {
+ if (omethod & LIO_RANDOM) {
+ method &= ~LIO_IO_SYNCP;
+ method |= LIO_IO_SYNC;
+ if (Debug_level > 2)
+ printf
+ ("DEBUG %s/%d: random chosen method switched to %#o for fifo\n",
+ __FILE__, __LINE__,
+ method);
+ } else if (Debug_level) {
+ printf
+ ("DEBUG %s/%d: pwrite will fail when it writes to a fifo\n",
+ __FILE__, __LINE__);
+ }
+ }
+ /* else: let it ride */
+ } else {
+ sprintf(Errormsg,
+ "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d %s",
+ __FILE__, __LINE__, fd, errno, strerror(errno));
+ return -errno;
+ }
+ }
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ poffset = (off64_t) ret;
+#endif
+ aiocbp.aio_offset = ret;
+
+#endif
+
+ /*
+ * If the LIO_USE_SIGNAL bit is not set, only use the signal
+ * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are bit.
+ * Otherwise there is not necessary a signal handler to trap
+ * the signal.
+ */
+ if (sig && !(method & LIO_USE_SIGNAL) && !(method & LIO_WAIT_SIGTYPES)) {
+
+ sig = 0; /* ignore signal parameter */
+ }
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ if (sig && (method & LIO_WAIT_CBTYPES))
+ sig = 0; /* ignore signal parameter */
+#endif
+
+ /*
+ * only setup signal hander if sig was specified and
+ * a sig wait method was specified.
+ * Doing this will change the handler for this signal. The
+ * old signal handler will not be restored.
+ *** restoring the signal handler could be added ***
+ */
+
+ if (sig && (method & LIO_WAIT_SIGTYPES)) {
+#ifdef CRAY
+ sigctl(SCTL_REG, sig, lio_async_signal_handler);
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ aiocbp.aio_sigevent.sigev_signo = sig;
+ sigset(sig, lio_async_signal_handler);
+#endif /* sgi */
+ }
+#if defined(sgi)
+ else if (method & LIO_WAIT_CBTYPES) {
+ /* sival_int just has to be something that I can use
+ * to identify the callback, and "size" happens to be handy...
+ */
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+ aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler;
+ aiocbp.aio_sigevent.sigev_value.sival_int = size;
+ }
+#endif
+#if defined(__linux__) && !defined(__UCLIBC__)
+ else if (method & LIO_WAIT_CBTYPES) {
+ /* sival_int just has to be something that I can use
+ * to identify the callback, and "size" happens to be handy...
+ */
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_THREAD;
+ aiocbp.aio_sigevent.sigev_notify_function =
+ lio_async_callback_handler;
+ aiocbp.aio_sigevent.sigev_notify_attributes =
+ (void *)(uintptr_t) size;
+ }
+#endif
+ /*
+ * Determine the system call that will be called and produce
+ * the string of the system call and place it in Lio_SysCall.
+ * Also update the io_type char pointer to give brief description
+ * of system call. Execute the system call and check for
+ * system call failure. If sync i/o, return the number of
+ * bytes written/read.
+ */
+
+ if ((method & LIO_IO_SYNC)
+ || (method & (LIO_IO_TYPES | LIO_IO_ATYPES)) == 0) {
+ /*
+ * write(2) is used if LIO_IO_SYNC bit is set or not none
+ * of the LIO_IO_TYPES bits are set (default).
+ */
+
+ sprintf(Lio_SysCall, "write(%d, buf, %d)", fd, size);
+ io_type = "write";
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+ while (1) {
+ if (((ret = write(fd, buffer, size)) == -1)
+ && errno != EAGAIN && errno != EINTR) {
+ sprintf(Errormsg,
+ "%s/%d write(%d, buf, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__, fd, size, errno,
+ strerror(errno));
+ return -errno;
+ }
+
+ if (ret != -1) {
+ if (ret != size) {
+ sprintf(Errormsg,
+ "%s/%d write(%d, buf, %d) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, ret);
+ size -= ret;
+ buffer += ret;
+ } else {
+ if (Debug_level > 1)
+ printf
+ ("DEBUG %s/%d: write completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ }
+ }
+ wait4sync_io(fd, 0);
+ }
+
+ }
+
+ else if (method & LIO_IO_ASYNC) {
+#ifdef CRAY
+ sprintf(Lio_SysCall,
+ "writea(%d, buf, %d, &status, %d)", fd, size, sig);
+ io_type = "writea";
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ sigoff();
+ if ((ret = writea(fd, buffer, size, &status, sig)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d writea(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ sprintf(Lio_SysCall,
+ "aio_write(fildes=%d, buf, nbytes=%d, signo=%d)", fd,
+ size, sig);
+ io_type = "aio_write";
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ if (sig)
+ sighold(sig);
+ if ((ret = aio_write(&aiocbp)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d aio_write(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, strerror(errno));
+ if (sig)
+ sigrelse(sig);
+ return -errno;
+ }
+#endif
+ }
+ /* LIO_IO_ASYNC */
+ else if (method & LIO_IO_SLISTIO) {
+#ifdef CRAY
+ request.li_opcode = LO_WRITE;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd = LC_WAIT;
+ io_type = "listio(2) sync write";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_WAIT, &req, 1) LO_WRITE, fd:%d, nbyte:%d",
+ fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ sigoff();
+ if (listio(listio_cmd, &request, 1) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+
+ if (Debug_level > 1)
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret = lio_check_asyncio(io_type, size, &status);
+ return ret;
+
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+
+ aiocbp.aio_lio_opcode = LIO_WRITE;
+ listio_cmd = LIO_WAIT;
+ io_type = "lio_listio(3) sync write";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d, sig:%d",
+ fd, size, sig);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ if (sig)
+ sighold(sig);
+ if (lio_listio(listio_cmd, aiolist, 1, NULL) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ if (sig)
+ sigrelse(sig);
+ return -errno;
+ }
+
+ if (Debug_level > 1)
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret = lio_check_asyncio(io_type, size, &aiocbp, method);
+ return ret;
+#endif
+ }
+ /* LIO_IO_SLISTIO */
+ else if (method & LIO_IO_ALISTIO) {
+#ifdef CRAY
+ request.li_opcode = LO_WRITE;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd = LC_START;
+ io_type = "listio(2) async write";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_START, &req, 1) LO_WRITE, fd:%d, nbyte:%d",
+ fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ sigoff();
+ if (listio(listio_cmd, &request, 1) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+#endif
+#if defined (sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ aiocbp.aio_lio_opcode = LIO_WRITE;
+ listio_cmd = LIO_NOWAIT;
+ io_type = "lio_listio(3) async write";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d",
+ fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ if (sig)
+ sighold(sig);
+ if (lio_listio(listio_cmd, aiolist, 1, NULL) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ if (sig)
+ sigrelse(sig);
+ return -errno;
+ }
+#endif
+ }
+ /* LIO_IO_ALISTIO */
+#ifndef CRAY
+ else if (method & LIO_IO_SYNCV) {
+ io_type = "writev(2)";
+
+ sprintf(Lio_SysCall, "writev(%d, &iov, 1) nbyte:%d", fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+ if ((ret = writev(fd, &iov, 1)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d writev(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s",
+ __FILE__, __LINE__, fd, size, errno,
+ strerror(errno));
+ return -errno;
+ }
+
+ if (ret != size) {
+ sprintf(Errormsg,
+ "%s/%d writev(%d, iov, 1) nbyte:%d returned=%d",
+ __FILE__, __LINE__, fd, size, ret);
+ } else if (Debug_level > 1)
+ printf
+ ("DEBUG %s/%d: writev completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCV */
+#endif
+
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ else if (method & LIO_IO_SYNCP) {
+ io_type = "pwrite(2)";
+
+ sprintf(Lio_SysCall,
+ "pwrite(%d, buf, %d, %lld)", fd, size,
+ (long long)poffset);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+ if ((ret = pwrite(fd, buffer, size, poffset)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d pwrite(%d, buf, %d, %lld) ret:-1, errno=%d %s",
+ __FILE__, __LINE__, fd, size,
+ (long long)poffset, errno, strerror(errno));
+ return -errno;
+ }
+
+ if (ret != size) {
+ sprintf(Errormsg,
+ "%s/%d pwrite(%d, buf, %d, %lld) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, (long long)poffset, ret);
+ } else if (Debug_level > 1)
+ printf
+ ("DEBUG %s/%d: pwrite completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCP */
+#endif
+
+ else {
+ printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__,
+ __LINE__);
+ return -1;
+ }
+
+ /*
+ * wait for async io to complete.
+ */
+#ifdef CRAY
+ ret = lio_wait4asyncio(method, fd, statptr);
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ ret = lio_wait4asyncio(method, fd, &aiocbp);
+#endif
+
+ /*
+ * If there was an error waiting for async i/o to complete,
+ * return the error value (errno) to the caller.
+ * Note: Errormsg should already have been updated.
+ */
+ if (ret < 0) {
+ return ret;
+ }
+
+ /*
+ * If i/o was not waited for (may not have been completed at this time),
+ * return the size that was requested.
+ */
+ if (ret == 1)
+ return size;
+
+ /*
+ * check that async io was successful.
+ * Note: if the there was an system call failure, -errno
+ * was returned and Errormsg should already have been updated.
+ * If amount i/o was different than size, Errormsg should already
+ * have been updated but the actual i/o size if returned.
+ */
+
+#ifdef CRAY
+ ret = lio_check_asyncio(io_type, size, &status);
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ ret = lio_check_asyncio(io_type, size, &aiocbp, method);
+#endif
+
+ return ret;
+} /* end of lio_write_buffer */
+
+/***********************************************************************
+ * Generic read function
+ * This function can be used to do a read using read(2), reada(2),
+ * aio_read(3), readv(2), pread(2),
+ * or single stride listio(2)/lio_listio(3).
+ * By setting the desired bits in the method
+ * bitmask, the caller can control the type of read and the wait method
+ * that will be used. If no io type bits are set, read will be used.
+ *
+ * If async io was attempted and no wait method bits are set then the
+ * wait method is: recall(2) for reada(2) and listio(2); aio_suspend(3) for
+ * aio_read(3) and lio_listio(3).
+ *
+ * If multiple wait methods are specified,
+ * only one wait method will be used. The order is predetermined.
+ *
+ * If the call specifies a signal and one of the two signal wait methods,
+ * a signal handler for the signal is set. This will reset an already
+ * set handler for this signal.
+ *
+ * If the LIO_RANDOM method bit is set, this function will randomly
+ * choose a io type and wait method from bits in the method argument.
+ *
+ * If an error is encountered, an error message will be generated
+ * in a internal static buffer. If errmsg is not NULL, it will
+ * be updated to point to the static buffer, allowing the caller
+ * to print the error message.
+ *
+ * Return Value
+ * If a system call fails, -errno is returned.
+ * If LIO_WAIT_NONE bit is set, the return value is the return value
+ * of the system call.
+ * If the io did not fail, the amount of data written is returned.
+ * If the size the system call say was written is different
+ * then what was asked to be written, errmsg is updated for
+ * this error condition. The return value is still the amount
+ * the system call says was written.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int lio_read_buffer(int fd, /* open file descriptor */
+ int method, /* contains io type and wait method bitmask*/
+ char *buffer, /* pointer to buffer */
+ int size, /* the size of the io */
+ int sig, /* signal to use if async io */
+ char **errmsg, /* char pointer that will be updated to point to err message */
+ long wrd) /* to allow future features, use zero for now */
+{
+ int ret = 0; /* syscall return or used to get random method */
+ char *io_type; /* Holds string of type of io */
+ int listio_cmd; /* Holds the listio/lio_listio cmd */
+ int omethod = method;
+#ifdef CRAY
+ struct listreq request; /* Used when a listio is wanted */
+ struct iosw status, *statptr[1];
+#else
+ /* for linux or sgi */
+ struct iovec iov; /* iovec for readv(2) */
+#endif
+#ifdef sgi
+ aiocb_t aiocbp; /* POSIX aio control block */
+ aiocb_t *aiolist[1]; /* list of aio control blocks for lio_listio */
+ off64_t poffset; /* pread(2) offset */
+#endif
+#if defined (__linux__) && !defined(__UCLIBC__)
+ struct aiocb aiocbp; /* POSIX aio control block */
+ struct aiocb *aiolist[1]; /* list of aio control blocks for lio_listio */
+ off64_t poffset; /* pread(2) offset */
+#endif
+
+ /*
+ * If LIO_RANDOM bit specified, get new method randomly.
+ */
+ if (method & LIO_RANDOM) {
+ if (Debug_level > 3)
+ printf("DEBUG %s/%d: method mask to choose from: %#o\n",
+ __FILE__, __LINE__, method);
+ method = lio_random_methods(method);
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: random chosen method %#o\n",
+ __FILE__, __LINE__, method);
+ }
+
+ if (errmsg != NULL)
+ *errmsg = Errormsg;
+
+ Rec_signal = Received_signal; /* get the current number of signals received */
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ Rec_callback = Received_callback; /* get the current number of callbacks received */
+#endif
+
+#ifdef CRAY
+ memset(&status, 0x00, sizeof(struct iosw));
+ memset(&request, 0x00, sizeof(struct listreq));
+ statptr[0] = &status;
+#else
+ /* for linux or sgi */
+ memset(&iov, 0x00, sizeof(struct iovec));
+ iov.iov_base = buffer;
+ iov.iov_len = size;
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+#if defined(sgi)
+ memset(&aiocbp, 0x00, sizeof(aiocb_t));
+#else
+ memset(&aiocbp, 0x00, sizeof(struct aiocb));
+#endif
+ aiocbp.aio_fildes = fd;
+ aiocbp.aio_nbytes = size;
+ aiocbp.aio_buf = buffer;
+/* aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE;
+ aiocbp.aio_sigevent.sigev_signo = 0;
+#ifdef sgi
+ aiocbp.aio_sigevent.sigev_func = NULL;
+ aiocbp.aio_sigevent.sigev_value.sival_int = 0;
+#elif defined(__linux__) && !defined(__UCLIBC__)
+ aiocbp.aio_sigevent.sigev_notify_function = NULL;
+ aiocbp.aio_sigevent.sigev_notify_attributes = 0;
+#endif
+ aiolist[0] = &aiocbp;
+
+ if ((ret = lseek(fd, 0, SEEK_CUR)) == -1) {
+ ret = 0;
+ /* If there is an error and it is not ESPIPE then kick out the error.
+ * If the fd is a fifo then we have to make sure that
+ * lio_random_methods() didn't select pwrite/pread; if it did then
+ * switch to write/read.
+ */
+ if (errno == ESPIPE) {
+ if (method & LIO_IO_SYNCP) {
+ if (omethod & LIO_RANDOM) {
+ method &= ~LIO_IO_SYNCP;
+ method |= LIO_IO_SYNC;
+ if (Debug_level > 2)
+ printf
+ ("DEBUG %s/%d: random chosen method switched to %#o for fifo\n",
+ __FILE__, __LINE__,
+ method);
+ } else if (Debug_level) {
+ printf
+ ("DEBUG %s/%d: pread will fail when it reads from a fifo\n",
+ __FILE__, __LINE__);
+ }
+ }
+ /* else: let it ride */
+ } else {
+ sprintf(Errormsg,
+ "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d %s",
+ __FILE__, __LINE__, fd, errno, strerror(errno));
+ return -errno;
+ }
+ }
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ poffset = (off64_t) ret;
+#endif
+ aiocbp.aio_offset = ret;
+
+#endif
+
+ /*
+ * If the LIO_USE_SIGNAL bit is not set, only use the signal
+ * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are set.
+ * Otherwise there is not necessarily a signal handler to trap
+ * the signal.
+ */
+ if (sig && !(method & LIO_USE_SIGNAL) && !(method & LIO_WAIT_SIGTYPES)) {
+
+ sig = 0; /* ignore signal parameter */
+ }
+#if defined(sgi) || (defined(__linux__)&& !defined(__UCLIBC__))
+ if (sig && (method & LIO_WAIT_CBTYPES))
+ sig = 0; /* ignore signal parameter */
+#endif
+
+ /*
+ * only setup signal hander if sig was specified and
+ * a sig wait method was specified.
+ * Doing this will change the handler for this signal. The
+ * old signal handler will not be restored.
+ *** restoring the signal handler could be added ***
+ */
+
+ if (sig && (method & LIO_WAIT_SIGTYPES)) {
+#ifdef CRAY
+ sigctl(SCTL_REG, sig, lio_async_signal_handler);
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ aiocbp.aio_sigevent.sigev_signo = sig;
+ sigset(sig, lio_async_signal_handler);
+#endif /* CRAY */
+ }
+#if defined(sgi)
+ else if (method & LIO_WAIT_CBTYPES) {
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+ aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler;
+ /* sival_int just has to be something that I can use
+ * to identify the callback, and "size" happens to be handy...
+ */
+ aiocbp.aio_sigevent.sigev_value.sival_int = size;
+ }
+#endif
+#if defined(__linux__) && !defined(__UCLIBC__)
+ else if (method & LIO_WAIT_CBTYPES) {
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_THREAD;
+ aiocbp.aio_sigevent.sigev_notify_function =
+ lio_async_callback_handler;
+ /* sival_int just has to be something that I can use
+ * to identify the callback, and "size" happens to be handy...
+ */
+ aiocbp.aio_sigevent.sigev_notify_attributes =
+ (void *)(uintptr_t) size;
+ }
+#endif
+
+ /*
+ * Determine the system call that will be called and produce
+ * the string of the system call and place it in Lio_SysCall.
+ * Also update the io_type char pointer to give brief description
+ * of system call. Execute the system call and check for
+ * system call failure. If sync i/o, return the number of
+ * bytes written/read.
+ */
+
+ if ((method & LIO_IO_SYNC)
+ || (method & (LIO_IO_TYPES | LIO_IO_ATYPES)) == 0) {
+ /*
+ * read(2) is used if LIO_IO_SYNC bit is set or not none
+ * of the LIO_IO_TYPES bits are set (default).
+ */
+
+ sprintf(Lio_SysCall, "read(%d, buf, %d)", fd, size);
+ io_type = "read";
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ while (1) {
+ if (((ret = read(fd, buffer, size)) == -1)
+ && errno != EINTR && errno != EAGAIN) {
+ sprintf(Errormsg,
+ "%s/%d read(%d, buf, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__, fd, size, errno,
+ strerror(errno));
+ return -errno;
+ }
+
+ if (ret == 0)
+ return 0;
+ if (ret != -1) {
+ if (ret != size) {
+ sprintf(Errormsg,
+ "%s/%d read(%d, buf, %d) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, ret);
+ size -= ret;
+ buffer += ret;
+ } else {
+ if (Debug_level > 1)
+ printf
+ ("DEBUG %s/%d: read completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ }
+ }
+ wait4sync_io(fd, 1);
+ }
+
+ }
+
+ else if (method & LIO_IO_ASYNC) {
+#ifdef CRAY
+ sprintf(Lio_SysCall,
+ "reada(%d, buf, %d, &status, %d)", fd, size, sig);
+ io_type = "reada";
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ sigoff();
+ if ((ret = reada(fd, buffer, size, &status, sig)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d reada(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ sprintf(Lio_SysCall,
+ "aio_read(fildes=%d, buf, nbytes=%d, signo=%d)", fd,
+ size, sig);
+ io_type = "aio_read";
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ if (sig)
+ sighold(sig);
+ if ((ret = aio_read(&aiocbp)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d aio_read(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, strerror(errno));
+ if (sig)
+ sigrelse(sig);
+ return -errno;
+ }
+#endif
+ }
+ /* LIO_IO_ASYNC */
+ else if (method & LIO_IO_SLISTIO) {
+#ifdef CRAY
+ request.li_opcode = LO_READ;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd = LC_WAIT;
+ io_type = "listio(2) sync read";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_WAIT, &req, 1) LO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ sigoff();
+ if (listio(listio_cmd, &request, 1) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+
+ if (Debug_level > 1)
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret = lio_check_asyncio(io_type, size, &status);
+ return ret;
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ aiocbp.aio_lio_opcode = LIO_READ;
+ listio_cmd = LIO_WAIT;
+ io_type = "lio_listio(3) sync read";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ if (sig)
+ sighold(sig);
+ if (lio_listio(listio_cmd, aiolist, 1, NULL) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ if (sig)
+ sigrelse(sig);
+ return -errno;
+ }
+
+ if (Debug_level > 1)
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret = lio_check_asyncio(io_type, size, &aiocbp, method);
+ return ret;
+#endif
+ }
+ /* LIO_IO_SLISTIO */
+ else if (method & LIO_IO_ALISTIO) {
+#ifdef CRAY
+ request.li_opcode = LO_READ;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd = LC_START;
+ io_type = "listio(2) async read";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_START, &req, 1) LO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ sigoff();
+ if (listio(listio_cmd, &request, 1) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ aiocbp.aio_lio_opcode = LIO_READ;
+ listio_cmd = LIO_NOWAIT;
+ io_type = "lio_listio(3) async read";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+
+ if (sig)
+ sighold(sig);
+ if (lio_listio(listio_cmd, aiolist, 1, NULL) == -1) {
+ sprintf(Errormsg,
+ "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__, Lio_SysCall, fd, size,
+ errno, strerror(errno));
+ if (sig)
+ sigrelse(sig);
+ return -errno;
+ }
+#endif
+ }
+ /* LIO_IO_ALISTIO */
+#ifndef CRAY
+ else if (method & LIO_IO_SYNCV) {
+ io_type = "readv(2)";
+
+ sprintf(Lio_SysCall, "readv(%d, &iov, 1) nbyte:%d", fd, size);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+ if ((ret = readv(fd, &iov, 1)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d readv(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s",
+ __FILE__, __LINE__, fd, size, errno,
+ strerror(errno));
+ return -errno;
+ }
+
+ if (ret != size) {
+ sprintf(Errormsg,
+ "%s/%d readv(%d, iov, 1) nbyte:%d returned=%d",
+ __FILE__, __LINE__, fd, size, ret);
+ } else if (Debug_level > 1)
+ printf
+ ("DEBUG %s/%d: readv completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCV */
+#endif
+
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ else if (method & LIO_IO_SYNCP) {
+ io_type = "pread(2)";
+
+ sprintf(Lio_SysCall,
+ "pread(%d, buf, %d, %lld)", fd, size,
+ (long long)poffset);
+
+ if (Debug_level) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__,
+ Lio_SysCall);
+ }
+ if ((ret = pread(fd, buffer, size, poffset)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d pread(%d, buf, %d, %lld) ret:-1, errno=%d %s",
+ __FILE__, __LINE__, fd, size,
+ (long long)poffset, errno, strerror(errno));
+ return -errno;
+ }
+
+ if (ret != size) {
+ sprintf(Errormsg,
+ "%s/%d pread(%d, buf, %d, %lld) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, (long long)poffset, ret);
+ } else if (Debug_level > 1)
+ printf
+ ("DEBUG %s/%d: pread completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCP */
+#endif
+
+ else {
+ printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__,
+ __LINE__);
+ return -1;
+ }
+
+ /*
+ * wait for async io to complete.
+ * Note: Sync io should have returned prior to getting here.
+ */
+#ifdef CRAY
+ ret = lio_wait4asyncio(method, fd, statptr);
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ ret = lio_wait4asyncio(method, fd, &aiocbp);
+#endif
+
+ /*
+ * If there was an error waiting for async i/o to complete,
+ * return the error value (errno) to the caller.
+ * Note: Errormsg should already have been updated.
+ */
+ if (ret < 0) {
+ return ret;
+ }
+
+ /*
+ * If i/o was not waited for (may not have been completed at this time),
+ * return the size that was requested.
+ */
+ if (ret == 1)
+ return size;
+
+ /*
+ * check that async io was successful.
+ * Note: if the there was an system call failure, -errno
+ * was returned and Errormsg should already have been updated.
+ * If amount i/o was different than size, Errormsg should already
+ * have been updated but the actual i/o size if returned.
+ */
+
+#ifdef CRAY
+ ret = lio_check_asyncio(io_type, size, &status);
+#endif
+#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__))
+ ret = lio_check_asyncio(io_type, size, &aiocbp, method);
+#endif
+
+ return ret;
+} /* end of lio_read_buffer */
+
+#if !defined(__sun) && !defined(__hpux) && !defined(_AIX)
+/***********************************************************************
+ * This function will check that async io was successful.
+ * It can also be used to check sync listio since it uses the
+ * same method.
+ *
+ * Return Values
+ * If status.sw_error is set, -status.sw_error is returned.
+ * Otherwise sw_count's field value is returned.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+#ifdef CRAY
+int lio_check_asyncio(char *io_type, int size, struct iosw *status)
+#elif defined(sgi)
+int lio_check_asyncio(char *io_type, int size, aiocb_t * aiocbp, int method)
+#elif defined(__linux__) && !defined(__UCLIBC__)
+int lio_check_asyncio(char *io_type, int size, struct aiocb *aiocbp, int method)
+{
+ int ret;
+
+#ifdef CRAY
+ if (status->sw_error) {
+ sprintf(Errormsg,
+ "%s/%d %s, sw_error set = %d %s, sw_count = %d",
+ __FILE__, __LINE__, io_type,
+ status->sw_error, strerror(status->sw_error),
+ status->sw_count);
+ return -status->sw_error;
+ } else if (status->sw_count != size) {
+ sprintf(Errormsg,
+ "%s/%d %s, sw_count not as expected(%d), but actual:%d",
+ __FILE__, __LINE__, io_type, size, status->sw_count);
+ } else if (Debug_level > 1) {
+ printf
+ ("DEBUG %s/%d: %s completed without error (sw_error == 0, sw_count == %d)\n",
+ __FILE__, __LINE__, io_type, status->sw_count);
+ }
+
+ return status->sw_count;
+
+#else
+
+ int cnt = 1;
+
+ /* The I/O may have been synchronous with signal completion. It doesn't
+ * make sense, but the combination could be generated. Release the
+ * completion signal here otherwise it'll hang around and bite us
+ * later.
+ */
+ if (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL)
+ sigrelse(aiocbp->aio_sigevent.sigev_signo);
+
+ ret = aio_error(aiocbp);
+
+ while (ret == EINPROGRESS) {
+ ret = aio_error(aiocbp);
+ ++cnt;
+ }
+ if (cnt > 1) {
+ sprintf(Errormsg,
+ "%s/%d %s, aio_error had to loop on EINPROGRESS, cnt=%d; random method %#o; sigev_notify=%s",
+ __FILE__, __LINE__, io_type, cnt, method,
+ (aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_SIGNAL ? "signal" : aiocbp->aio_sigevent.
+ sigev_notify == SIGEV_NONE ? "none" :
+#ifdef SIGEV_CALLBACK
+ aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_CALLBACK ? "callback" :
+#endif
+ aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_THREAD ? "thread" : "unknown"));
+ return -ret;
+ }
+
+ if (ret != 0) {
+ sprintf(Errormsg,
+ "%s/%d %s, aio_error = %d %s; random method %#o",
+ __FILE__, __LINE__, io_type,
+ ret, strerror(ret), method);
+ return -ret;
+ }
+ ret = aio_return(aiocbp);
+ if (ret != size) {
+ sprintf(Errormsg,
+ "%s/%d %s, aio_return not as expected(%d), but actual:%d",
+ __FILE__, __LINE__, io_type, size, ret);
+
+#ifdef BUG1_workaround
+ if (ret == 0) {
+ ret = size;
+ if (Debug_level > 1) {
+ printf
+ ("WARN %s/%d: %s completed with bug1_workaround (aio_error == 0, aio_return now == %d)\n",
+ __FILE__, __LINE__, io_type, ret);
+ }
+ }
+#endif /* BUG1_workaround */
+
+ } else if (Debug_level > 1) {
+ printf
+ ("DEBUG %s/%d: %s completed without error (aio_error == 0, aio_return == %d)\n",
+ __FILE__, __LINE__, io_type, ret);
+ }
+
+ return ret;
+
+#endif
+} /* end of lio_check_asyncio */
+#endif
+
+/***********************************************************************
+ *
+ * This function will wait for async io to complete.
+ * If multiple wait methods are specified, the order is predetermined
+ * to LIO_WAIT_RECALL,
+ * LIO_WAIT_ACTIVE, LIO_WAIT_SIGPAUSE, LIO_WAIT_SIGACTIVE,
+ * then LIO_WAIT_NONE.
+ *
+ * If no wait method was specified the default wait method is: recall(2)
+ * or aio_suspend(3), as appropriate.
+ *
+ * Return Values
+ * <0: errno of failed recall
+ * 0 : async io was completed
+ * 1 : async was not waited for, io may not have completed.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+#ifdef CRAY
+int lio_wait4asyncio(int method, int fd, struct iosw **statptr)
+#elif defined(sgi)
+int lio_wait4asyncio(int method, int fd, aiocb_t * aiocbp)
+#elif defined(__linux__) && !defined(__UCLIBC__)
+int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp)
+{
+ int cnt;
+#ifdef sgi
+ int ret;
+ const aiocb_t *aioary[1];
+#endif
+#if defined(__linux__)&& !defined(__UCLIBC__)
+ int ret;
+ const struct aiocb *aioary[1];
+#endif
+
+ if ((method & LIO_WAIT_RECALL)
+#if defined(sgi) || (defined(__linux__)&& !defined(__UCLIBC__))
+ || (method & LIO_WAIT_CBSUSPEND)
+ || (method & LIO_WAIT_SIGSUSPEND)
+#endif
+ || ((method & LIO_WAIT_TYPES) == 0)) {
+ /*
+ * If method has LIO_WAIT_RECALL bit set or method does
+ * not have any wait method bits set (default), use recall/aio_suspend.
+ */
+#ifdef CRAY
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: wait method : recall\n", __FILE__,
+ __LINE__);
+ sigon();
+ if (recall(fd, 1, statptr)) {
+ sprintf(Errormsg,
+ "%s/%d recall(%d, 1, stat) failed, errno:%d %s",
+ __FILE__, __LINE__, fd, errno, strerror(errno));
+ return -errno;
+ }
+#else
+ if (Debug_level > 2)
+ printf
+ ("DEBUG %s/%d: wait method : aio_suspend, sigev_notify=%s\n",
+ __FILE__, __LINE__,
+ (aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_SIGNAL ? "signal" : aiocbp->aio_sigevent.
+ sigev_notify == SIGEV_NONE ? "none" :
+#ifdef SIGEV_CALLBACK
+ aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_CALLBACK ? "callback" :
+#endif
+ aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_THREAD ? "thread" : "unknown"));
+
+ aioary[0] = aiocbp;
+ ret = aio_suspend(aioary, 1, NULL);
+ if ((ret == -1) && (errno == EINTR)) {
+ if (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL) {
+ if (Debug_level > 2) {
+ printf
+ ("DEBUG %s/%d: aio_suspend received EINTR, sigev_notify=SIGEV_SIGNAL -- ok\n",
+ __FILE__, __LINE__);
+ }
+ } else {
+ sprintf(Errormsg,
+ "%s/%d aio_suspend received EINTR, sigev_notify=%s, not ok\n",
+ __FILE__, __LINE__,
+ (aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_SIGNAL ? "signal" : aiocbp->
+ aio_sigevent.sigev_notify ==
+ SIGEV_NONE ? "none" :
+#ifdef SIGEV_CALLBACK
+ aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_CALLBACK ? "callback" :
+#endif
+ aiocbp->aio_sigevent.sigev_notify ==
+ SIGEV_THREAD ? "thread" : "unknown"));
+ return -errno;
+ }
+ } else if (ret) {
+ sprintf(Errormsg,
+ "%s/%d aio_suspend(fildes=%d, aioary, 1, NULL) failed, errno:%d %s",
+ __FILE__, __LINE__, fd, errno, strerror(errno));
+ return -errno;
+ }
+#endif
+
+ } else if (method & LIO_WAIT_ACTIVE) {
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: wait method : active\n", __FILE__,
+ __LINE__);
+#ifdef CRAY
+ sigon();
+ /*
+ * loop until sw_flag, sw_count or sw_error field elements
+ * change to non-zero.
+ */
+ cnt = 0;
+ while ((*statptr)->sw_flag == 0 &&
+ (*statptr)->sw_count == 0 && (*statptr)->sw_error == 0) {
+ cnt++;
+ }
+#else
+ /* loop while aio_error() returns EINPROGRESS */
+ cnt = 0;
+ while (1) {
+ ret = aio_error(aiocbp);
+ if (ret != EINPROGRESS) {
+ break;
+ }
+ ++cnt;
+ }
+
+#endif
+ if (Debug_level > 5 && cnt && (cnt % 50) == 0)
+ printf("DEBUG %s/%d: wait active cnt = %d\n",
+ __FILE__, __LINE__, cnt);
+
+ } else if (method & LIO_WAIT_SIGPAUSE) {
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: wait method : sigpause\n",
+ __FILE__, __LINE__);
+#ifdef sgi
+ /* note: don't do the sigon() for CRAY in this case. why? -- roehrich 6/11/97 */
+ if (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL)
+ sigrelse(aiocbp->aio_sigevent.sigev_signo);
+ else {
+ printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n",
+ __FILE__, __LINE__);
+ return -1;
+ }
+#endif
+ pause();
+
+ } else if (method & LIO_WAIT_SIGACTIVE) {
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: wait method : sigactive\n",
+ __FILE__, __LINE__);
+#ifdef CRAY
+ sigon();
+#else
+ if (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL)
+ sigrelse(aiocbp->aio_sigevent.sigev_signo);
+ else {
+ printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n",
+ __FILE__, __LINE__);
+ return -1;
+ }
+#endif
+ /* loop waiting for signal */
+ while (Received_signal == Rec_signal) {
+#ifdef CRAY
+ sigon();
+#else
+ sigrelse(aiocbp->aio_sigevent.sigev_signo);
+#endif
+ }
+
+ } else if (method & LIO_WAIT_NONE) {
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: wait method : none\n", __FILE__,
+ __LINE__);
+ /* It's broken because the aiocb/iosw is an automatic variable in
+ * lio_{read,write}_buffer, so when the function returns and the
+ * I/O completes there will be nowhere to write the I/O status.
+ * It doesn't cause a problem on unicos--probably because of some
+ * compiler quirk, or an accident. It causes POSIX async I/O
+ * to core dump some threads. spr/pv 705909. 6/27/97 roehrich
+ */
+ sprintf(Errormsg,
+ "%s/%d LIO_WAIT_NONE was selected (this is broken)\n",
+ __FILE__, __LINE__);
+#ifdef CRAY
+ sigon();
+#endif
+/* return 1;*/
+ return -1;
+ } else {
+ if (Debug_level > 2)
+ printf("DEBUG %s/%d: no wait method was chosen\n",
+ __FILE__, __LINE__);
+ return -1;
+ }
+
+ return 0;
+
+} /* end of lio_wait4asyncio */
+
+#endif /* ifndef linux */
+#endif
+
+#if UNIT_TEST
+/***********************************************************************
+ * The following code is provided as unit test.
+ * Just define add "-DUNIT_TEST=1" to the cc line.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+struct unit_info_t {
+ int method;
+ int sig;
+ char *str;
+} Unit_info[] = {
+ {
+ LIO_IO_SYNC, 0, "sync io"}, {
+ LIO_IO_SYNCV, 0, "sync readv/writev"}, {
+ LIO_IO_SYNCP, 0, "sync pread/pwrite"}, {
+ LIO_IO_ASYNC, 0, "async io, def wait"}, {
+ LIO_IO_SLISTIO, 0, "sync listio"}, {
+ LIO_IO_ALISTIO, 0, "async listio, def wait"}, {
+ LIO_IO_ASYNC | LIO_WAIT_ACTIVE, 0, "async active"}, {
+ LIO_IO_ASYNC | LIO_WAIT_RECALL, 0, "async recall/suspend"}, {
+ LIO_IO_ASYNC | LIO_WAIT_SIGPAUSE, SIGUSR1, "async sigpause"}, {
+ LIO_IO_ASYNC | LIO_WAIT_SIGACTIVE, SIGUSR1, "async sigactive"}, {
+ LIO_IO_ALISTIO | LIO_WAIT_ACTIVE, 0, "async listio active"}, {
+ LIO_IO_ALISTIO | LIO_WAIT_RECALL, 0, "async listio recall"}, {
+ LIO_IO_ALISTIO | LIO_WAIT_SIGACTIVE, SIGUSR1, "async listio sigactive"},
+ {
+ LIO_IO_ALISTIO | LIO_WAIT_SIGPAUSE, SIGUSR1, "async listio sigpause"},
+ {
+ LIO_IO_ASYNC, SIGUSR2, "async io, def wait, sigusr2"}, {
+LIO_IO_ALISTIO, SIGUSR2, "async listio, def wait, sigusr2"},};
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+
+ int fd;
+ char *err;
+ char buffer[4096];
+ int size = 4096;
+ int ret;
+ int ind;
+ int iter = 3;
+ int method;
+ int exit_status = 0;
+ int c;
+ int i;
+ char *symbols = NULL;
+ int die_on_err = 0;
+
+ while ((c = getopt(argc, argv, "s:di:")) != -1) {
+ switch (c) {
+ case 's':
+ symbols = optarg;
+ break;
+ case 'd':
+ ++die_on_err;
+ break;
+ case 'i':
+ iter = atoi(optarg);
+ break;
+ }
+ }
+
+ if ((fd =
+ open("unit_test_file", O_CREAT | O_RDWR | O_TRUNC, 0777)) == -1) {
+ perror
+ ("open(unit_test_file, O_CREAT|O_RDWR|O_TRUNC, 0777) failed");
+ exit(1);
+ }
+
+ Debug_level = 9;
+
+ if (symbols != NULL) {
+ if ((method = lio_parse_io_arg2(symbols, &err)) == -1) {
+ printf
+ ("lio_parse_io_arg2(%s, &err) failed, bad token starting at %s\n",
+ symbols, err);
+ if (die_on_err)
+ exit(1);
+ } else
+ printf("lio_parse_io_arg2(%s, &err) returned %#o\n",
+ symbols, method);
+
+ exit_status = 0;
+ for (ind = 0; ind < iter; ind++) {
+ memset(buffer, 'A', 4096);
+ if (lseek(fd, 0, 0) == -1) {
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno);
+ ++exit_status;
+ }
+ if ((ret = lio_write_buffer(fd, method, buffer,
+ size, SIGUSR1, &err,
+ 0)) != size) {
+ printf
+ ("lio_write_buffer returned -1, err = %s\n",
+ err);
+ } else
+ printf("lio_write_buffer returned %d\n", ret);
+
+ memset(buffer, 'B', 4096);
+ if (lseek(fd, 0, 0) == -1) {
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno);
+ ++exit_status;
+ }
+ if ((ret = lio_read_buffer(fd, method, buffer,
+ size, SIGUSR2, &err,
+ 0)) != size) {
+ printf
+ ("lio_read_buffer returned -1, err = %s\n",
+ err);
+ } else
+ printf("lio_read_buffer returned %d\n", ret);
+
+ for (i = 0; i < 4096; ++i) {
+ if (buffer[i] != 'A') {
+ printf(" buffer[%d] = %d\n", i,
+ buffer[i]);
+ ++exit_status;
+ break;
+ }
+ }
+
+ if (exit_status)
+ exit(exit_status);
+
+ }
+
+ unlink("unit_test_file");
+ exit(0);
+ }
+
+ for (ind = 0; ind < sizeof(Unit_info) / sizeof(struct unit_info_t);
+ ind++) {
+
+ printf("\n********* write %s ***************\n",
+ Unit_info[ind].str);
+ if (lseek(fd, 0, 0) == -1) {
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno);
+ ++exit_status;
+ }
+
+ memset(buffer, 'A', 4096);
+ if ((ret = lio_write_buffer(fd, Unit_info[ind].method, buffer,
+ size, Unit_info[ind].sig, &err,
+ 0)) != size) {
+ printf
+ (">>>>> lio_write_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n err = %s\n",
+ Unit_info[ind].method, size, Unit_info[ind].sig,
+ err);
+ ++exit_status;
+ if (die_on_err)
+ exit(exit_status);
+ } else {
+ printf("lio_write_buffer returned %d\n", ret);
+ }
+
+ printf("\n********* read %s ***************\n",
+ Unit_info[ind].str);
+ if (lseek(fd, 0, 0) == -1) {
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno);
+ ++exit_status;
+ }
+ memset(buffer, 'B', 4096);
+ if ((ret = lio_read_buffer(fd, Unit_info[ind].method, buffer,
+ size, Unit_info[ind].sig, &err,
+ 0)) != size) {
+ printf
+ (">>>>> lio_read_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n err = %s\n",
+ Unit_info[ind].method, size, Unit_info[ind].sig,
+ err);
+ ++exit_status;
+ if (die_on_err)
+ exit(exit_status);
+ } else {
+ printf("lio_read_buffer returned %d\n", ret);
+ }
+
+ for (i = 0; i < 4096; ++i) {
+ if (buffer[i] != 'A') {
+ printf(" buffer[%d] = %d\n", i, buffer[i]);
+ ++exit_status;
+ if (die_on_err)
+ exit(exit_status);
+ break;
+ }
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+ sleep(1);
+
+ }
+
+ unlink("unit_test_file");
+
+ exit(exit_status);
+}
+#endif
diff --git a/src/kernel/tests/lib/tst_af_alg.c b/src/kernel/tests/lib/tst_af_alg.c
new file mode 100644
index 0000000..d3895a8
--- /dev/null
+++ b/src/kernel/tests/lib/tst_af_alg.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_af_alg.h"
+#include "lapi/socket.h"
+
+int tst_alg_create(void)
+{
+ TEST(socket(AF_ALG, SOCK_SEQPACKET, 0));
+ if (TST_RET >= 0)
+ return TST_RET;
+ if (TST_ERR == EAFNOSUPPORT)
+ tst_brk(TCONF, "kernel doesn't support AF_ALG");
+ tst_brk(TBROK | TTERRNO, "unexpected error creating AF_ALG socket");
+ return -1;
+}
+
+void tst_alg_bind_addr(int algfd, const struct sockaddr_alg *addr)
+{
+ TEST(bind(algfd, (const struct sockaddr *)addr, sizeof(*addr)));
+ if (TST_RET == 0)
+ return;
+ if (TST_ERR == ENOENT) {
+ tst_brk(TCONF, "kernel doesn't support %s algorithm '%s'",
+ addr->salg_type, addr->salg_name);
+ }
+ tst_brk(TBROK | TTERRNO,
+ "unexpected error binding AF_ALG socket to %s algorithm '%s'",
+ addr->salg_type, addr->salg_name);
+}
+
+static void init_sockaddr_alg(struct sockaddr_alg *addr,
+ const char *algtype, const char *algname)
+{
+ memset(addr, 0, sizeof(*addr));
+
+ addr->salg_family = AF_ALG;
+
+ strncpy((char *)addr->salg_type, algtype, sizeof(addr->salg_type));
+ if (addr->salg_type[sizeof(addr->salg_type) - 1] != '\0')
+ tst_brk(TBROK, "algorithm type too long: '%s'", algtype);
+
+ strncpy((char *)addr->salg_name, algname, sizeof(addr->salg_name));
+ if (addr->salg_name[sizeof(addr->salg_name) - 1] != '\0')
+ tst_brk(TBROK, "algorithm name too long: '%s'", algname);
+}
+
+void tst_alg_bind(int algfd, const char *algtype, const char *algname)
+{
+ struct sockaddr_alg addr;
+
+ init_sockaddr_alg(&addr, algtype, algname);
+
+ tst_alg_bind_addr(algfd, &addr);
+}
+
+bool tst_have_alg(const char *algtype, const char *algname)
+{
+ int algfd;
+ struct sockaddr_alg addr;
+ bool have_alg = true;
+
+ algfd = tst_alg_create();
+
+ init_sockaddr_alg(&addr, algtype, algname);
+
+ TEST(bind(algfd, (const struct sockaddr *)&addr, sizeof(addr)));
+ if (TST_RET != 0) {
+ if (TST_ERR != ENOENT) {
+ tst_brk(TBROK | TTERRNO,
+ "unexpected error binding AF_ALG socket to %s algorithm '%s'",
+ algtype, algname);
+ }
+ have_alg = false;
+ }
+
+ close(algfd);
+ return have_alg;
+}
+
+void tst_require_alg(const char *algtype, const char *algname)
+{
+ int algfd = tst_alg_create();
+
+ tst_alg_bind(algfd, algtype, algname);
+
+ close(algfd);
+}
+
+void tst_alg_setkey(int algfd, const uint8_t *key, unsigned int keylen)
+{
+ uint8_t *keybuf = NULL;
+ unsigned int i;
+
+ if (key == NULL) {
+ /* generate a random key */
+ keybuf = SAFE_MALLOC(keylen);
+ for (i = 0; i < keylen; i++)
+ keybuf[i] = rand();
+ key = keybuf;
+ }
+ TEST(setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, keylen));
+ if (TST_RET != 0) {
+ tst_brk(TBROK | TTERRNO,
+ "unexpected error setting key (len=%u)", keylen);
+ }
+ free(keybuf);
+}
+
+int tst_alg_accept(int algfd)
+{
+ TEST(accept(algfd, NULL, NULL));
+ if (TST_RET < 0) {
+ tst_brk(TBROK | TTERRNO,
+ "unexpected error accept()ing AF_ALG request socket");
+ }
+ return TST_RET;
+}
+
+int tst_alg_setup(const char *algtype, const char *algname,
+ const uint8_t *key, unsigned int keylen)
+{
+ int algfd = tst_alg_create();
+
+ tst_alg_bind(algfd, algtype, algname);
+
+ if (keylen != 0)
+ tst_alg_setkey(algfd, key, keylen);
+
+ return algfd;
+}
+
+int tst_alg_setup_reqfd(const char *algtype, const char *algname,
+ const uint8_t *key, unsigned int keylen)
+{
+ int algfd = tst_alg_setup(algtype, algname, key, keylen);
+ int reqfd = tst_alg_accept(algfd);
+
+ close(algfd);
+ return reqfd;
+}
+
+void tst_alg_sendmsg(int reqfd, const void *data, size_t datalen,
+ const struct tst_alg_sendmsg_params *params)
+{
+ struct iovec iov = {
+ .iov_base = (void *)data,
+ .iov_len = datalen,
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_flags = params->msg_flags,
+ };
+ size_t controllen;
+ uint8_t *control;
+ struct cmsghdr *cmsg;
+ struct af_alg_iv *alg_iv;
+
+ if (params->encrypt && params->decrypt)
+ tst_brk(TBROK, "Both encrypt and decrypt are specified");
+
+ controllen = 0;
+ if (params->encrypt || params->decrypt)
+ controllen += CMSG_SPACE(sizeof(uint32_t));
+ if (params->ivlen)
+ controllen += CMSG_SPACE(sizeof(struct af_alg_iv) +
+ params->ivlen);
+ if (params->assoclen)
+ controllen += CMSG_SPACE(sizeof(uint32_t));
+
+ control = SAFE_MALLOC(controllen);
+ memset(control, 0, controllen);
+ msg.msg_control = control;
+ msg.msg_controllen = controllen;
+ cmsg = CMSG_FIRSTHDR(&msg);
+
+ if (params->encrypt || params->decrypt) {
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_OP;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
+ *(uint32_t *)CMSG_DATA(cmsg) =
+ params->encrypt ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+ if (params->ivlen) {
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_IV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) +
+ params->ivlen);
+ alg_iv = (struct af_alg_iv *)CMSG_DATA(cmsg);
+ alg_iv->ivlen = params->ivlen;
+ memcpy(alg_iv->iv, params->iv, params->ivlen);
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+ if (params->assoclen) {
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
+ *(uint32_t *)CMSG_DATA(cmsg) = params->assoclen;
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+
+ SAFE_SENDMSG(datalen, reqfd, &msg, 0);
+}
diff --git a/src/kernel/tests/lib/tst_ansi_color.c b/src/kernel/tests/lib/tst_ansi_color.c
new file mode 100644
index 0000000..1c29268
--- /dev/null
+++ b/src/kernel/tests/lib/tst_ansi_color.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Petr Vorel <pvorel@suse.cz>
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tst_res_flags.h"
+#include "tst_ansi_color.h"
+
+char* tst_ttype2color(int ttype)
+{
+ switch (TTYPE_RESULT(ttype)) {
+ case TPASS:
+ return ANSI_COLOR_GREEN;
+ break;
+ case TFAIL:
+ return ANSI_COLOR_RED;
+ break;
+ case TBROK:
+ return ANSI_COLOR_RED;
+ break;
+ case TCONF:
+ return ANSI_COLOR_YELLOW;
+ break;
+ case TWARN:
+ return ANSI_COLOR_MAGENTA;
+ break;
+ case TINFO:
+ return ANSI_COLOR_BLUE;
+ break;
+ default:
+ return "";
+ }
+}
+
+int tst_color_enabled(int fd)
+{
+ static int color;
+
+ if (color)
+ return color - 1;
+
+ char *env = getenv("LTP_COLORIZE_OUTPUT");
+
+ if (env) {
+ if (!strcmp(env, "n") || !strcmp(env, "0"))
+ color = 1;
+
+ if (!strcmp(env, "y") || !strcmp(env, "1"))
+ color = 2;
+
+ return color - 1;
+ }
+
+ if (isatty(fd) == 0)
+ color = 1;
+ else
+ color = 2;
+
+ return color - 1;
+}
diff --git a/src/kernel/tests/lib/tst_assert.c b/src/kernel/tests/lib/tst_assert.c
new file mode 100644
index 0000000..9b8ebc1
--- /dev/null
+++ b/src/kernel/tests/lib/tst_assert.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 FUJITSU LIMITED. All rights reserved.
+ * Author: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
+ * Copyright (c) 2020 Cyril Hrubis <chrubis@suse.cz>
+ */
+#include <stdio.h>
+#define TST_NO_DEFAULT_MAIN
+#include "tst_assert.h"
+#include "tst_test.h"
+
+void tst_assert_int(const char *file, const int lineno, const char *path, int val)
+{
+ int sys_val;
+
+ safe_file_scanf(file, lineno, NULL, path, "%d", &sys_val);
+
+ if (val == sys_val) {
+ tst_res_(file, lineno, TPASS, "%s = %d", path, val);
+ return;
+ }
+
+ tst_res_(file, lineno, TFAIL, "%s != %d got %d", path, val, sys_val);
+}
+
+void tst_assert_ulong(const char *file, const int lineno, const char *path, unsigned long val)
+{
+ unsigned long sys_val;
+
+ safe_file_scanf(file, lineno, NULL, path, "%lu", &sys_val);
+
+ if (val == sys_val) {
+ tst_res_(file, lineno, TPASS, "%s = %lu", path, val);
+ return;
+ }
+
+ tst_res_(file, lineno, TFAIL, "%s != %lu got %lu", path, val, sys_val);
+}
+
+void tst_assert_file_int(const char *file, const int lineno, const char *path, const char *prefix, int val)
+{
+ int sys_val;
+ char fmt[1024];
+
+ snprintf(fmt, sizeof(fmt), "%s%%d", prefix);
+ file_lines_scanf(file, lineno, NULL, 1, path, fmt, &sys_val);
+
+ if (val == sys_val) {
+ tst_res_(file, lineno, TPASS, "%s %s = %d", path, prefix, sys_val);
+ return;
+ }
+
+ tst_res_(file, lineno, TFAIL, "%s %s != %d got %d", path, prefix, val, sys_val);
+}
+
+void tst_assert_str(const char *file, const int lineno, const char *path, const char *val)
+{
+ char sys_val[1024];
+
+ safe_file_scanf(file, lineno, NULL, path, "%1024s", sys_val);
+ if (!strcmp(val, sys_val)) {
+ tst_res_(file, lineno, TPASS, "%s = '%s'", path, val);
+ return;
+ }
+
+ tst_res_(file, lineno, TFAIL, "%s != '%s' got '%s'", path, val, sys_val);
+}
+
+void tst_assert_file_str(const char *file, const int lineno, const char *path, const char *prefix, const char *val)
+{
+ char sys_val[1024];
+ char fmt[2048];
+
+ snprintf(fmt, sizeof(fmt), "%s: %%1024s", prefix);
+ file_lines_scanf(file, lineno, NULL, 1, path, fmt, sys_val);
+
+ if (!strcmp(val, sys_val)) {
+ tst_res_(file, lineno, TPASS, "%s %s = '%s'", path, prefix, sys_val);
+ return;
+ }
+
+ tst_res_(file, lineno, TFAIL, "%s %s != '%s' got '%s'", path, prefix, val, sys_val);
+}
diff --git a/src/kernel/tests/lib/tst_buffers.c b/src/kernel/tests/lib/tst_buffers.c
new file mode 100644
index 0000000..b8b597a
--- /dev/null
+++ b/src/kernel/tests/lib/tst_buffers.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <sys/mman.h>
+#include <stdlib.h>
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+
+struct map {
+ void *addr;
+ size_t size;
+ size_t buf_shift;
+ struct map *next;
+};
+
+static struct map *maps;
+
+static void setup_canary(struct map *map)
+{
+ size_t i;
+ char *buf = map->addr;
+
+ for (i = 0; i < map->buf_shift/2; i++) {
+ char c = random();
+ buf[map->buf_shift - i - 1] = c;
+ buf[i] = c;
+ }
+}
+
+static void check_canary(struct map *map)
+{
+ size_t i;
+ char *buf = map->addr;
+
+ for (i = 0; i < map->buf_shift/2; i++) {
+ if (buf[map->buf_shift - i - 1] != buf[i]) {
+ tst_res(TWARN,
+ "pid %i: buffer modified address %p[%zi]",
+ getpid(), (char*)map->addr + map->buf_shift, -i-1);
+ }
+ }
+}
+
+void *tst_alloc(size_t size)
+{
+ size_t page_size = getpagesize();
+ unsigned int pages = (size / page_size) + !!(size % page_size) + 1;
+ void *ret;
+ struct map *map = SAFE_MALLOC(sizeof(struct map));
+ static int print_msg = 1;
+
+ if (print_msg) {
+ tst_res(TINFO, "Test is using guarded buffers");
+ print_msg = 0;
+ }
+
+ ret = SAFE_MMAP(NULL, page_size * pages, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+
+ mprotect(ret + (pages-1) * page_size, page_size, PROT_NONE);
+
+ map->addr = ret;
+ map->size = pages * page_size;
+ map->next = maps;
+ maps = map;
+
+ if (size % page_size)
+ map->buf_shift = page_size - (size % page_size);
+ else
+ map->buf_shift = 0;
+
+ setup_canary(map);
+
+ return ret + map->buf_shift;
+}
+
+static int count_iovec(int *sizes)
+{
+ int ret = 0;
+
+ while (sizes[ret++] != -1);
+
+ return ret - 1;
+}
+
+struct iovec *tst_iovec_alloc(int sizes[])
+{
+ int i, cnt = count_iovec(sizes);
+ struct iovec *iovec;
+
+ if (cnt <= 0)
+ return NULL;
+
+ iovec = tst_alloc(sizeof(struct iovec) * cnt);
+
+ for (i = 0; i < cnt; i++) {
+ if (sizes[i]) {
+ iovec[i].iov_base = tst_alloc(sizes[i]);
+ iovec[i].iov_len = sizes[i];
+ } else {
+ iovec[i].iov_base = NULL;
+ iovec[i].iov_base = 0;
+ }
+ }
+
+ return iovec;
+}
+
+void tst_buffers_alloc(struct tst_buffers bufs[])
+{
+ unsigned int i;
+
+ for (i = 0; bufs[i].ptr; i++) {
+ if (bufs[i].size)
+ *((void**)bufs[i].ptr) = tst_alloc(bufs[i].size);
+ else
+ *((void**)bufs[i].ptr) = tst_iovec_alloc(bufs[i].iov_sizes);
+ }
+}
+
+char *tst_strdup(const char *str)
+{
+ size_t len = strlen(str);
+ char *ret = tst_alloc(len + 1);
+ return strcpy(ret, str);
+}
+
+void tst_free_all(void)
+{
+ struct map *i = maps;
+
+ while (i) {
+ struct map *j = i;
+ check_canary(i);
+ SAFE_MUNMAP(i->addr, i->size);
+ i = i->next;
+ free(j);
+ }
+
+ maps = NULL;
+}
diff --git a/src/kernel/tests/lib/tst_capability.c b/src/kernel/tests/lib/tst_capability.c
new file mode 100644
index 0000000..1fa0e49
--- /dev/null
+++ b/src/kernel/tests/lib/tst_capability.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 Richard Palethorpe <rpalethorpe@suse.com>
+ */
+
+#include <string.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_capability.h"
+
+#include "lapi/syscalls.h"
+
+int tst_capget(struct tst_cap_user_header *hdr,
+ struct tst_cap_user_data *data)
+{
+ return tst_syscall(__NR_capget, hdr, data);
+}
+
+int tst_capset(struct tst_cap_user_header *hdr,
+ const struct tst_cap_user_data *data)
+{
+ return tst_syscall(__NR_capset, hdr, data);
+}
+
+static void do_cap_drop(uint32_t *set, uint32_t mask, const struct tst_cap *cap)
+{
+ if (*set & mask) {
+ tst_res(TINFO, "Dropping %s(%d)", cap->name, cap->id);
+ *set &= ~mask;
+ }
+}
+
+static void do_cap_req(uint32_t *permitted, uint32_t *effective, uint32_t mask,
+ const struct tst_cap *cap)
+{
+ if (!(*permitted & mask))
+ tst_brk(TCONF, "Need %s(%d)", cap->name, cap->id);
+
+ if (!(*effective & mask)) {
+ tst_res(TINFO, "Permitting %s(%d)", cap->name, cap->id);
+ *effective |= mask;
+ }
+}
+
+void tst_cap_action(struct tst_cap *cap)
+{
+ struct tst_cap_user_header hdr = {
+ .version = 0x20080522,
+ .pid = tst_syscall(__NR_gettid),
+ };
+ struct tst_cap_user_data cur[2] = { {0} };
+ struct tst_cap_user_data new[2] = { {0} };
+ uint32_t act = cap->action;
+ uint32_t *pE = &new[CAP_TO_INDEX(cap->id)].effective;
+ uint32_t *pP = &new[CAP_TO_INDEX(cap->id)].permitted;
+ uint32_t mask = CAP_TO_MASK(cap->id);
+
+ if (tst_capget(&hdr, cur))
+ tst_brk(TBROK | TTERRNO, "tst_capget()");
+
+ memcpy(new, cur, sizeof(new));
+
+ switch (act) {
+ case TST_CAP_DROP:
+ do_cap_drop(pE, mask, cap);
+ break;
+ case TST_CAP_REQ:
+ do_cap_req(pP, pE, mask, cap);
+ break;
+ default:
+ tst_brk(TBROK, "Unrecognised action %d", cap->action);
+ }
+
+ if (!memcmp(cur, new, sizeof(new)))
+ return;
+
+ if (tst_capset(&hdr, new))
+ tst_brk(TBROK | TERRNO, "tst_capset(%s)", cap->name);
+}
+
+void tst_cap_setup(struct tst_cap *caps, unsigned int action_mask)
+{
+ struct tst_cap *cap;
+
+ for (cap = caps; cap->action; cap++) {
+ if (cap->action & action_mask)
+ tst_cap_action(cap);
+ }
+}
diff --git a/src/kernel/tests/lib/tst_cgroup.c b/src/kernel/tests/lib/tst_cgroup.c
new file mode 100644
index 0000000..ba413d8
--- /dev/null
+++ b/src/kernel/tests/lib/tst_cgroup.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 Red Hat, Inc.
+ * Copyright (c) 2020 Li Wang <liwang@redhat.com>
+ */
+
+#define TST_NO_DEFAULT_MAIN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "tst_test.h"
+#include "tst_safe_macros.h"
+#include "tst_safe_stdio.h"
+#include "tst_cgroup.h"
+#include "tst_device.h"
+
+static enum tst_cgroup_ver tst_cg_ver;
+static int clone_children;
+
+static int tst_cgroup_check(const char *cgroup)
+{
+ char line[PATH_MAX];
+ FILE *file;
+ int cg_check = 0;
+
+ file = SAFE_FOPEN("/proc/filesystems", "r");
+ while (fgets(line, sizeof(line), file)) {
+ if (strstr(line, cgroup) != NULL) {
+ cg_check = 1;
+ break;
+ }
+ }
+ SAFE_FCLOSE(file);
+
+ return cg_check;
+}
+
+enum tst_cgroup_ver tst_cgroup_version(void)
+{
+ enum tst_cgroup_ver cg_ver;
+
+ if (tst_cgroup_check("cgroup2")) {
+ if (!tst_is_mounted("cgroup2") && tst_is_mounted("cgroup"))
+ cg_ver = TST_CGROUP_V1;
+ else
+ cg_ver = TST_CGROUP_V2;
+
+ goto out;
+ }
+
+ if (tst_cgroup_check("cgroup"))
+ cg_ver = TST_CGROUP_V1;
+
+ if (!cg_ver)
+ tst_brk(TCONF, "Cgroup is not configured");
+
+out:
+ return cg_ver;
+}
+
+static void tst_cgroup1_mount(const char *name, const char *option,
+ const char *mnt_path, const char *new_path)
+{
+ char knob_path[PATH_MAX];
+ if (tst_is_mounted(mnt_path))
+ goto out;
+
+ SAFE_MKDIR(mnt_path, 0777);
+ if (mount(name, mnt_path, "cgroup", 0, option) == -1) {
+ if (errno == ENODEV) {
+ if (rmdir(mnt_path) == -1)
+ tst_res(TWARN | TERRNO, "rmdir %s failed", mnt_path);
+ tst_brk(TCONF,
+ "Cgroup v1 is not configured in kernel");
+ }
+ tst_brk(TBROK | TERRNO, "mount %s", mnt_path);
+ }
+
+ /*
+ * We should assign one or more memory nodes to cpuset.mems and
+ * cpuset.cpus, otherwise, echo $$ > tasks gives “ENOSPC: no space
+ * left on device” when trying to use cpuset.
+ *
+ * Or, setting cgroup.clone_children to 1 can help in automatically
+ * inheriting memory and node setting from parent cgroup when a
+ * child cgroup is created.
+ */
+ if (strcmp(option, "cpuset") == 0) {
+ sprintf(knob_path, "%s/cgroup.clone_children", mnt_path);
+ SAFE_FILE_SCANF(knob_path, "%d", &clone_children);
+ SAFE_FILE_PRINTF(knob_path, "%d", 1);
+ }
+out:
+ SAFE_MKDIR(new_path, 0777);
+
+ tst_res(TINFO, "Cgroup(%s) v1 mount at %s success", option, mnt_path);
+}
+
+static void tst_cgroup2_mount(const char *mnt_path, const char *new_path)
+{
+ if (tst_is_mounted(mnt_path))
+ goto out;
+
+ SAFE_MKDIR(mnt_path, 0777);
+ if (mount("cgroup2", mnt_path, "cgroup2", 0, NULL) == -1) {
+ if (errno == ENODEV) {
+ if (rmdir(mnt_path) == -1)
+ tst_res(TWARN | TERRNO, "rmdir %s failed", mnt_path);
+ tst_brk(TCONF,
+ "Cgroup v2 is not configured in kernel");
+ }
+ tst_brk(TBROK | TERRNO, "mount %s", mnt_path);
+ }
+
+out:
+ SAFE_MKDIR(new_path, 0777);
+
+ tst_res(TINFO, "Cgroup v2 mount at %s success", mnt_path);
+}
+
+static void tst_cgroupN_umount(const char *mnt_path, const char *new_path)
+{
+ FILE *fp;
+ int fd;
+ char s_new[BUFSIZ], s[BUFSIZ], value[BUFSIZ];
+ char knob_path[PATH_MAX];
+
+ if (!tst_is_mounted(mnt_path))
+ return;
+
+ /* Move all processes in task(v2: cgroup.procs) to its parent node. */
+ if (tst_cg_ver & TST_CGROUP_V1)
+ sprintf(s, "%s/tasks", mnt_path);
+ if (tst_cg_ver & TST_CGROUP_V2)
+ sprintf(s, "%s/cgroup.procs", mnt_path);
+
+ fd = open(s, O_WRONLY);
+ if (fd == -1)
+ tst_res(TWARN | TERRNO, "open %s", s);
+
+ if (tst_cg_ver & TST_CGROUP_V1)
+ snprintf(s_new, BUFSIZ, "%s/tasks", new_path);
+ if (tst_cg_ver & TST_CGROUP_V2)
+ snprintf(s_new, BUFSIZ, "%s/cgroup.procs", new_path);
+
+ fp = fopen(s_new, "r");
+ if (fp == NULL)
+ tst_res(TWARN | TERRNO, "fopen %s", s_new);
+ if ((fd != -1) && (fp != NULL)) {
+ while (fgets(value, BUFSIZ, fp) != NULL)
+ if (write(fd, value, strlen(value) - 1)
+ != (ssize_t)strlen(value) - 1)
+ tst_res(TWARN | TERRNO, "write %s", s);
+ }
+ if (tst_cg_ver & TST_CGROUP_V1) {
+ sprintf(knob_path, "%s/cpuset.cpus", mnt_path);
+ if (!access(knob_path, F_OK)) {
+ sprintf(knob_path, "%s/cgroup.clone_children", mnt_path);
+ SAFE_FILE_PRINTF(knob_path, "%d", clone_children);
+ }
+ }
+ if (fd != -1)
+ close(fd);
+ if (fp != NULL)
+ fclose(fp);
+ if (rmdir(new_path) == -1)
+ tst_res(TWARN | TERRNO, "rmdir %s", new_path);
+ if (umount(mnt_path) == -1)
+ tst_res(TWARN | TERRNO, "umount %s", mnt_path);
+ if (rmdir(mnt_path) == -1)
+ tst_res(TWARN | TERRNO, "rmdir %s", mnt_path);
+
+ if (tst_cg_ver & TST_CGROUP_V1)
+ tst_res(TINFO, "Cgroup v1 unmount success");
+ if (tst_cg_ver & TST_CGROUP_V2)
+ tst_res(TINFO, "Cgroup v2 unmount success");
+}
+
+struct tst_cgroup_path {
+ char *mnt_path;
+ char *new_path;
+ struct tst_cgroup_path *next;
+};
+
+static struct tst_cgroup_path *tst_cgroup_paths;
+
+static void tst_cgroup_set_path(const char *cgroup_dir)
+{
+ char cgroup_new_dir[PATH_MAX];
+ struct tst_cgroup_path *tst_cgroup_path, *a;
+
+ if (!cgroup_dir)
+ tst_brk(TBROK, "Invalid cgroup dir, plese check cgroup_dir");
+
+ sprintf(cgroup_new_dir, "%s/ltp_%d", cgroup_dir, rand());
+
+ /* To store cgroup path in the 'path' list */
+ tst_cgroup_path = SAFE_MALLOC(sizeof(struct tst_cgroup_path));
+ tst_cgroup_path->mnt_path = SAFE_MALLOC(strlen(cgroup_dir) + 1);
+ tst_cgroup_path->new_path = SAFE_MALLOC(strlen(cgroup_new_dir) + 1);
+ tst_cgroup_path->next = NULL;
+
+ if (!tst_cgroup_paths) {
+ tst_cgroup_paths = tst_cgroup_path;
+ } else {
+ a = tst_cgroup_paths;
+ do {
+ if (!a->next) {
+ a->next = tst_cgroup_path;
+ break;
+ }
+ a = a->next;
+ } while (a);
+ }
+
+ sprintf(tst_cgroup_path->mnt_path, "%s", cgroup_dir);
+ sprintf(tst_cgroup_path->new_path, "%s", cgroup_new_dir);
+}
+
+static char *tst_cgroup_get_path(const char *cgroup_dir)
+{
+ struct tst_cgroup_path *a;
+
+ if (!tst_cgroup_paths)
+ return NULL;
+
+ a = tst_cgroup_paths;
+
+ while (strcmp(a->mnt_path, cgroup_dir) != 0){
+ if (!a->next) {
+ tst_res(TINFO, "%s is not found", cgroup_dir);
+ return NULL;
+ }
+ a = a->next;
+ };
+
+ return a->new_path;
+}
+
+static void tst_cgroup_del_path(const char *cgroup_dir)
+{
+ struct tst_cgroup_path *a, *b;
+
+ if (!tst_cgroup_paths)
+ return;
+
+ a = b = tst_cgroup_paths;
+
+ while (strcmp(b->mnt_path, cgroup_dir) != 0) {
+ if (!b->next) {
+ tst_res(TINFO, "%s is not found", cgroup_dir);
+ return;
+ }
+ a = b;
+ b = b->next;
+ };
+
+ if (b == tst_cgroup_paths)
+ tst_cgroup_paths = b->next;
+ else
+ a->next = b->next;
+
+ free(b->mnt_path);
+ free(b->new_path);
+ free(b);
+}
+
+void tst_cgroup_mount(enum tst_cgroup_ctrl ctrl, const char *cgroup_dir)
+{
+ char *cgroup_new_dir;
+ char knob_path[PATH_MAX];
+
+ tst_cg_ver = tst_cgroup_version();
+
+ tst_cgroup_set_path(cgroup_dir);
+ cgroup_new_dir = tst_cgroup_get_path(cgroup_dir);
+
+ if (tst_cg_ver & TST_CGROUP_V1) {
+ switch(ctrl) {
+ case TST_CGROUP_MEMCG:
+ tst_cgroup1_mount("memcg", "memory", cgroup_dir, cgroup_new_dir);
+ break;
+ case TST_CGROUP_CPUSET:
+ tst_cgroup1_mount("cpusetcg", "cpuset", cgroup_dir, cgroup_new_dir);
+ break;
+ default:
+ tst_brk(TBROK, "Invalid cgroup controller: %d", ctrl);
+ }
+ }
+
+ if (tst_cg_ver & TST_CGROUP_V2) {
+ tst_cgroup2_mount(cgroup_dir, cgroup_new_dir);
+
+ switch(ctrl) {
+ case TST_CGROUP_MEMCG:
+ sprintf(knob_path, "%s/cgroup.subtree_control", cgroup_dir);
+ SAFE_FILE_PRINTF(knob_path, "%s", "+memory");
+ break;
+ case TST_CGROUP_CPUSET:
+ tst_brk(TCONF, "Cgroup v2 hasn't achieve cpuset subsystem");
+ break;
+ default:
+ tst_brk(TBROK, "Invalid cgroup controller: %d", ctrl);
+ }
+ }
+}
+
+void tst_cgroup_umount(const char *cgroup_dir)
+{
+ char *cgroup_new_dir;
+
+ cgroup_new_dir = tst_cgroup_get_path(cgroup_dir);
+ tst_cgroupN_umount(cgroup_dir, cgroup_new_dir);
+ tst_cgroup_del_path(cgroup_dir);
+}
+
+void tst_cgroup_set_knob(const char *cgroup_dir, const char *knob, long value)
+{
+ char *cgroup_new_dir;
+ char knob_path[PATH_MAX];
+
+ cgroup_new_dir = tst_cgroup_get_path(cgroup_dir);
+ sprintf(knob_path, "%s/%s", cgroup_new_dir, knob);
+ SAFE_FILE_PRINTF(knob_path, "%ld", value);
+}
+
+void tst_cgroup_move_current(const char *cgroup_dir)
+{
+ if (tst_cg_ver & TST_CGROUP_V1)
+ tst_cgroup_set_knob(cgroup_dir, "tasks", getpid());
+
+ if (tst_cg_ver & TST_CGROUP_V2)
+ tst_cgroup_set_knob(cgroup_dir, "cgroup.procs", getpid());
+}
+
+void tst_cgroup_mem_set_maxbytes(const char *cgroup_dir, long memsz)
+{
+ if (tst_cg_ver & TST_CGROUP_V1)
+ tst_cgroup_set_knob(cgroup_dir, "memory.limit_in_bytes", memsz);
+
+ if (tst_cg_ver & TST_CGROUP_V2)
+ tst_cgroup_set_knob(cgroup_dir, "memory.max", memsz);
+}
+
+int tst_cgroup_mem_swapacct_enabled(const char *cgroup_dir)
+{
+ char *cgroup_new_dir;
+ char knob_path[PATH_MAX];
+
+ cgroup_new_dir = tst_cgroup_get_path(cgroup_dir);
+
+ if (tst_cg_ver & TST_CGROUP_V1) {
+ sprintf(knob_path, "%s/%s",
+ cgroup_new_dir, "/memory.memsw.limit_in_bytes");
+
+ if ((access(knob_path, F_OK) == -1)) {
+ if (errno == ENOENT)
+ tst_res(TCONF, "memcg swap accounting is disabled");
+ else
+ tst_brk(TBROK | TERRNO, "failed to access %s", knob_path);
+ } else {
+ return 1;
+ }
+ }
+
+ if (tst_cg_ver & TST_CGROUP_V2) {
+ sprintf(knob_path, "%s/%s",
+ cgroup_new_dir, "/memory.swap.max");
+
+ if ((access(knob_path, F_OK) == -1)) {
+ if (errno == ENOENT)
+ tst_res(TCONF, "memcg swap accounting is disabled");
+ else
+ tst_brk(TBROK | TERRNO, "failed to access %s", knob_path);
+ } else {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void tst_cgroup_mem_set_maxswap(const char *cgroup_dir, long memsz)
+{
+ if (tst_cg_ver & TST_CGROUP_V1)
+ tst_cgroup_set_knob(cgroup_dir, "memory.memsw.limit_in_bytes", memsz);
+
+ if (tst_cg_ver & TST_CGROUP_V2)
+ tst_cgroup_set_knob(cgroup_dir, "memory.swap.max", memsz);
+}
+
+void tst_cgroup_cpuset_read_files(const char *cgroup_dir, const char *filename, char *retbuf)
+{
+ int fd;
+ char *cgroup_new_dir;
+ char knob_path[PATH_MAX];
+
+ cgroup_new_dir = tst_cgroup_get_path(cgroup_dir);
+
+ /*
+ * try either '/dev/cpuset/XXXX' or '/dev/cpuset/cpuset.XXXX'
+ * please see Documentation/cgroups/cpusets.txt from kernel src
+ * for details
+ */
+ sprintf(knob_path, "%s/%s", cgroup_new_dir, filename);
+ fd = open(knob_path, O_RDONLY);
+ if (fd == -1) {
+ if (errno == ENOENT) {
+ sprintf(knob_path, "%s/cpuset.%s",
+ cgroup_new_dir, filename);
+ fd = SAFE_OPEN(knob_path, O_RDONLY);
+ } else
+ tst_brk(TBROK | TERRNO, "open %s", knob_path);
+ }
+
+ if (read(fd, retbuf, sizeof(retbuf)) < 0)
+ tst_brk(TBROK | TERRNO, "read %s", knob_path);
+
+ close(fd);
+}
+
+void tst_cgroup_cpuset_write_files(const char *cgroup_dir, const char *filename, const char *buf)
+{
+ int fd;
+ char *cgroup_new_dir;
+ char knob_path[PATH_MAX];
+
+ cgroup_new_dir = tst_cgroup_get_path(cgroup_dir);
+
+ /*
+ * try either '/dev/cpuset/XXXX' or '/dev/cpuset/cpuset.XXXX'
+ * please see Documentation/cgroups/cpusets.txt from kernel src
+ * for details
+ */
+ sprintf(knob_path, "%s/%s", cgroup_new_dir, filename);
+ fd = open(knob_path, O_WRONLY);
+ if (fd == -1) {
+ if (errno == ENOENT) {
+ sprintf(knob_path, "%s/cpuset.%s", cgroup_new_dir, filename);
+ fd = SAFE_OPEN(knob_path, O_WRONLY);
+ } else
+ tst_brk(TBROK | TERRNO, "open %s", knob_path);
+ }
+
+ SAFE_WRITE(1, fd, buf, strlen(buf));
+
+ close(fd);
+}
diff --git a/src/kernel/tests/lib/tst_checkpoint.c b/src/kernel/tests/lib/tst_checkpoint.c
new file mode 100644
index 0000000..5e5b114
--- /dev/null
+++ b/src/kernel/tests/lib/tst_checkpoint.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2015 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/syscall.h>
+#include <linux/futex.h>
+
+#include "test.h"
+#include "safe_macros.h"
+#include "lapi/futex.h"
+
+#define DEFAULT_MSEC_TIMEOUT 10000
+
+futex_t *tst_futexes;
+unsigned int tst_max_futexes;
+
+void tst_checkpoint_init(const char *file, const int lineno,
+ void (*cleanup_fn)(void))
+{
+ int fd;
+ unsigned int page_size;
+
+ if (tst_futexes) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s: %d checkpoints already initialized",
+ file, lineno);
+ return;
+ }
+
+ /*
+ * The parent test process is responsible for creating the temporary
+ * directory and therefore must pass non-zero cleanup (to remove the
+ * directory if something went wrong).
+ *
+ * We cannot do this check unconditionally because if we need to init
+ * the checkpoint from a binary that was started by exec() the
+ * tst_tmpdir_created() will return false because the tmpdir was
+ * created by parent. In this case we expect the subprogram can call
+ * the init as a first function with NULL as cleanup function.
+ */
+ if (cleanup_fn && !tst_tmpdir_created()) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d You have to create test temporary directory "
+ "first (call tst_tmpdir())", file, lineno);
+ return;
+ }
+
+ page_size = getpagesize();
+
+ fd = SAFE_OPEN(cleanup_fn, "checkpoint_futex_base_file",
+ O_RDWR | O_CREAT, 0666);
+
+ SAFE_FTRUNCATE(cleanup_fn, fd, page_size);
+
+ tst_futexes = SAFE_MMAP(cleanup_fn, NULL, page_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ tst_max_futexes = page_size / sizeof(uint32_t);
+
+ SAFE_CLOSE(cleanup_fn, fd);
+}
+
+int tst_checkpoint_wait(unsigned int id, unsigned int msec_timeout)
+{
+ struct timespec timeout;
+ int ret;
+
+ if (id >= tst_max_futexes) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ timeout.tv_sec = msec_timeout/1000;
+ timeout.tv_nsec = (msec_timeout%1000) * 1000000;
+
+ do {
+ ret = syscall(SYS_futex, &tst_futexes[id], FUTEX_WAIT,
+ tst_futexes[id], &timeout);
+ } while (ret == -1 && errno == EINTR);
+
+ return ret;
+}
+
+int tst_checkpoint_wake(unsigned int id, unsigned int nr_wake,
+ unsigned int msec_timeout)
+{
+ unsigned int msecs = 0, waked = 0;
+
+ if (id >= tst_max_futexes) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ for (;;) {
+ waked += syscall(SYS_futex, &tst_futexes[id], FUTEX_WAKE,
+ INT_MAX, NULL);
+
+ if (waked == nr_wake)
+ break;
+
+ usleep(1000);
+ msecs++;
+
+ if (msecs >= msec_timeout) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void tst_safe_checkpoint_wait(const char *file, const int lineno,
+ void (*cleanup_fn)(void), unsigned int id,
+ unsigned int msec_timeout)
+{
+ int ret;
+
+ if (!msec_timeout)
+ msec_timeout = DEFAULT_MSEC_TIMEOUT;
+
+ ret = tst_checkpoint_wait(id, msec_timeout);
+
+ if (ret) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: tst_checkpoint_wait(%u, %i)",
+ file, lineno, id, msec_timeout);
+ }
+}
+
+void tst_safe_checkpoint_wake(const char *file, const int lineno,
+ void (*cleanup_fn)(void), unsigned int id,
+ unsigned int nr_wake)
+{
+ int ret = tst_checkpoint_wake(id, nr_wake, DEFAULT_MSEC_TIMEOUT);
+
+ if (ret) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "%s:%d: tst_checkpoint_wake(%u, %u, %i)",
+ file, lineno, id, nr_wake, DEFAULT_MSEC_TIMEOUT);
+ }
+}
diff --git a/src/kernel/tests/lib/tst_checksum.c b/src/kernel/tests/lib/tst_checksum.c
new file mode 100644
index 0000000..903bf3d
--- /dev/null
+++ b/src/kernel/tests/lib/tst_checksum.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2018 Oracle and/or its affiliates. All Rights Reserved. */
+
+#include "tst_checksum.h"
+
+static const uint32_t crc32c_table[] = {
+ 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4,
+ 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
+ 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
+ 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
+ 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b,
+ 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
+ 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54,
+ 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
+ 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
+ 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
+ 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5,
+ 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
+ 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45,
+ 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
+ 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
+ 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
+ 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48,
+ 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
+ 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687,
+ 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
+ 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
+ 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
+ 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8,
+ 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
+ 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096,
+ 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
+ 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
+ 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
+ 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9,
+ 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
+ 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36,
+ 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
+ 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
+ 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
+ 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043,
+ 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
+ 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3,
+ 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
+ 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
+ 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
+ 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652,
+ 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
+ 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d,
+ 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
+ 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
+ 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
+ 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2,
+ 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
+ 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530,
+ 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
+ 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
+ 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
+ 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f,
+ 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
+ 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90,
+ 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
+ 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
+ 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
+ 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321,
+ 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
+ 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81,
+ 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
+ 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
+ 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
+};
+
+uint32_t tst_crc32c(uint8_t *buf, size_t buf_len)
+{
+ uint32_t crc = 0xffffffff;
+
+ while (buf_len--)
+ crc = crc32c_table[(crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
+
+ return ~crc;
+}
diff --git a/src/kernel/tests/lib/tst_clocks.c b/src/kernel/tests/lib/tst_clocks.c
new file mode 100644
index 0000000..cdcb9fc
--- /dev/null
+++ b/src/kernel/tests/lib/tst_clocks.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <time.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_timer.h"
+#include "tst_clocks.h"
+#include "lapi/syscalls.h"
+#include "lapi/posix_clocks.h"
+
+typedef int (*mysyscall)(clockid_t clk_id, void *ts);
+
+int syscall_supported_by_kernel(long sysnr)
+{
+ int ret;
+
+ ret = syscall(sysnr, 0, NULL);
+ if (ret == -1 && errno == ENOSYS)
+ return 0;
+
+ return 1;
+}
+
+int tst_clock_getres(clockid_t clk_id, struct timespec *res)
+{
+ static struct tst_ts tts = { 0, };
+ static mysyscall func;
+ int ret;
+
+#if (__NR_clock_getres_time64 != __LTP__NR_INVALID_SYSCALL)
+ if (!func && syscall_supported_by_kernel(__NR_clock_getres_time64)) {
+ func = sys_clock_getres64;
+ tts.type = TST_KERN_TIMESPEC;
+ }
+#endif
+
+ if (!func && syscall_supported_by_kernel(__NR_clock_getres)) {
+ func = sys_clock_getres;
+ tts.type = TST_KERN_OLD_TIMESPEC;
+ }
+
+ if (!func) {
+ tst_res(TCONF, "clock_getres() not available");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ ret = func(clk_id, tst_ts_get(&tts));
+ res->tv_sec = tst_ts_get_sec(tts);
+ res->tv_nsec = tst_ts_get_nsec(tts);
+ return ret;
+}
+
+int tst_clock_gettime(clockid_t clk_id, struct timespec *ts)
+{
+ static struct tst_ts tts = { 0, };
+ static mysyscall func;
+ int ret;
+
+#if (__NR_clock_gettime64 != __LTP__NR_INVALID_SYSCALL)
+ if (!func && syscall_supported_by_kernel(__NR_clock_gettime64)) {
+ func = sys_clock_gettime64;
+ tts.type = TST_KERN_TIMESPEC;
+ }
+#endif
+
+ if (!func && syscall_supported_by_kernel(__NR_clock_gettime)) {
+ func = sys_clock_gettime;
+ tts.type = TST_KERN_OLD_TIMESPEC;
+ }
+
+ if (!func) {
+ tst_res(TCONF, "clock_gettime() not available");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ ret = func(clk_id, tst_ts_get(&tts));
+ ts->tv_sec = tst_ts_get_sec(tts);
+ ts->tv_nsec = tst_ts_get_nsec(tts);
+ return ret;
+}
+
+int tst_clock_settime(clockid_t clk_id, struct timespec *ts)
+{
+ static struct tst_ts tts = { 0, };
+ static mysyscall func;
+
+#if (__NR_clock_settime64 != __LTP__NR_INVALID_SYSCALL)
+ if (!func && syscall_supported_by_kernel(__NR_clock_settime64)) {
+ func = sys_clock_settime64;
+ tts.type = TST_KERN_TIMESPEC;
+ }
+#endif
+
+ if (!func && syscall_supported_by_kernel(__NR_clock_settime)) {
+ func = sys_clock_settime;
+ tts.type = TST_KERN_OLD_TIMESPEC;
+ }
+
+ if (!func) {
+ tst_res(TCONF, "clock_settime() not available");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ tst_ts_set_sec(&tts, ts->tv_sec);
+ tst_ts_set_nsec(&tts, ts->tv_nsec);
+ return func(clk_id, tst_ts_get(&tts));
+}
+
+const char *tst_clock_name(clockid_t clk_id)
+{
+ switch (clk_id) {
+ case CLOCK_REALTIME:
+ return "CLOCK_REALTIME";
+ case CLOCK_MONOTONIC:
+ return "CLOCK_MONOTONIC";
+ case CLOCK_PROCESS_CPUTIME_ID:
+ return "CLOCK_PROCESS_CPUTIME_ID";
+ case CLOCK_THREAD_CPUTIME_ID:
+ return "CLOCK_THREAD_CPUTIME_ID";
+ case CLOCK_MONOTONIC_RAW:
+ return "CLOCK_MONOTONIC_RAW";
+ case CLOCK_REALTIME_COARSE:
+ return "CLOCK_REALTIME_COARSE";
+ case CLOCK_MONOTONIC_COARSE:
+ return "CLOCK_MONOTONIC_COARSE";
+ case CLOCK_BOOTTIME:
+ return "CLOCK_BOOTTIME";
+ case CLOCK_REALTIME_ALARM:
+ return "CLOCK_REALTIME_ALARM";
+ case CLOCK_BOOTTIME_ALARM:
+ return "CLOCK_BOOTTIME_ALARM";
+ case CLOCK_TAI:
+ return "CLOCK_TAI";
+ default:
+ return "INVALID/UNKNOWN CLOCK";
+ }
+}
diff --git a/src/kernel/tests/lib/tst_cmd.c b/src/kernel/tests/lib/tst_cmd.c
new file mode 100644
index 0000000..7446249
--- /dev/null
+++ b/src/kernel/tests/lib/tst_cmd.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
+ * Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include "test.h"
+#include "tst_cmd.h"
+
+#define OPEN_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+#define OPEN_FLAGS (O_WRONLY | O_APPEND | O_CREAT)
+
+int tst_cmd_fds_(void (cleanup_fn)(void),
+ const char *const argv[],
+ int stdout_fd,
+ int stderr_fd,
+ enum tst_cmd_flags flags)
+{
+ int rc;
+
+ if (argv == NULL || argv[0] == NULL) {
+ tst_brkm(TBROK, cleanup_fn,
+ "argument list is empty at %s:%d", __FILE__, __LINE__);
+ return -1;
+ }
+
+ /*
+ * The tst_sig() install poisoned signal handlers for all signals the
+ * test is not expected to get.
+ *
+ * So we temporarily disable the handler for sigchild we get after our
+ * child exits so that we don't have to disable it in each test that
+ * uses this interface.
+ */
+ void *old_handler = signal(SIGCHLD, SIG_DFL);
+
+ char path[PATH_MAX];
+
+ if (tst_get_path(argv[0], path, sizeof(path))) {
+ if (flags & TST_CMD_TCONF_ON_MISSING)
+ tst_brkm(TCONF, cleanup_fn, "Couldn't find '%s' in $PATH at %s:%d", argv[0],
+ __FILE__, __LINE__);
+ else
+ return 255;
+ }
+
+ pid_t pid = vfork();
+ if (pid == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn, "vfork failed at %s:%d",
+ __FILE__, __LINE__);
+ return -1;
+ }
+ if (!pid) {
+ /* redirecting stdout and stderr if needed */
+ if (stdout_fd != -1) {
+ close(STDOUT_FILENO);
+ dup2(stdout_fd, STDOUT_FILENO);
+ }
+
+ if (stderr_fd != -1) {
+ close(STDERR_FILENO);
+ dup2(stderr_fd, STDERR_FILENO);
+ }
+
+ execvp(argv[0], (char *const *)argv);
+ _exit(254);
+ }
+
+ int ret = -1;
+ if (waitpid(pid, &ret, 0) != pid) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn, "waitpid failed at %s:%d",
+ __FILE__, __LINE__);
+ return -1;
+ }
+
+ signal(SIGCHLD, old_handler);
+
+ if (!WIFEXITED(ret)) {
+ tst_brkm(TBROK, cleanup_fn, "failed to exec cmd '%s' at %s:%d",
+ argv[0], __FILE__, __LINE__);
+ return -1;
+ }
+
+ rc = WEXITSTATUS(ret);
+
+ if (!(flags & TST_CMD_PASS_RETVAL) && rc) {
+ tst_brkm(TBROK, cleanup_fn,
+ "'%s' exited with a non-zero code %d at %s:%d",
+ argv[0], rc, __FILE__, __LINE__);
+ return -1;
+ }
+
+ return rc;
+}
+
+int tst_cmd_(void (cleanup_fn)(void),
+ const char *const argv[],
+ const char *stdout_path,
+ const char *stderr_path,
+ enum tst_cmd_flags flags)
+{
+ int stdout_fd = -1;
+ int stderr_fd = -1;
+ int rc;
+
+ if (stdout_path != NULL) {
+ stdout_fd = open(stdout_path,
+ OPEN_FLAGS, OPEN_MODE);
+
+ if (stdout_fd == -1)
+ tst_resm(TWARN | TERRNO,
+ "open() on %s failed at %s:%d",
+ stdout_path, __FILE__, __LINE__);
+ }
+
+ if (stderr_path != NULL) {
+ stderr_fd = open(stderr_path,
+ OPEN_FLAGS, OPEN_MODE);
+
+ if (stderr_fd == -1)
+ tst_resm(TWARN | TERRNO,
+ "open() on %s failed at %s:%d",
+ stderr_path, __FILE__, __LINE__);
+ }
+
+ rc = tst_cmd_fds(cleanup_fn, argv, stdout_fd, stderr_fd, flags);
+
+ if ((stdout_fd != -1) && (close(stdout_fd) == -1))
+ tst_resm(TWARN | TERRNO,
+ "close() on %s failed at %s:%d",
+ stdout_path, __FILE__, __LINE__);
+
+ if ((stderr_fd != -1) && (close(stderr_fd) == -1))
+ tst_resm(TWARN | TERRNO,
+ "close() on %s failed at %s:%d",
+ stderr_path, __FILE__, __LINE__);
+
+ return rc;
+}
+
+int tst_system(const char *command)
+{
+ int ret = 0;
+
+ /*
+ *Temporarily disable SIGCHLD of user defined handler, so the
+ *system(3) function will not cause unexpected SIGCHLD signal
+ *callback function for test cases.
+ */
+ void *old_handler = signal(SIGCHLD, SIG_DFL);
+
+ ret = system(command);
+
+ signal(SIGCHLD, old_handler);
+ return ret;
+}
diff --git a/src/kernel/tests/lib/tst_coredump.c b/src/kernel/tests/lib/tst_coredump.c
new file mode 100644
index 0000000..83aa2c3
--- /dev/null
+++ b/src/kernel/tests/lib/tst_coredump.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ */
+
+#define TST_NO_DEFAULT_MAIN
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "tst_test.h"
+#include "tst_coredump.h"
+
+void tst_no_corefile(int verbose)
+{
+ struct rlimit new_r, old_r;
+
+ SAFE_GETRLIMIT(RLIMIT_CORE, &old_r);
+ if (old_r.rlim_max >= 1 || geteuid() == 0) {
+ /*
+ * 1 is a special value, that disables core-to-pipe.
+ * At the same time it is small enough value for
+ * core-to-file, so it skips creating cores as well.
+ */
+ new_r.rlim_cur = 1;
+ new_r.rlim_max = 1;
+ SAFE_SETRLIMIT(RLIMIT_CORE, &new_r);
+
+ if (verbose) {
+ tst_res(TINFO,
+ "Avoid dumping corefile for process(pid=%d)",
+ getpid());
+ }
+ }
+}
diff --git a/src/kernel/tests/lib/tst_cpu.c b/src/kernel/tests/lib/tst_cpu.c
new file mode 100644
index 0000000..033155e
--- /dev/null
+++ b/src/kernel/tests/lib/tst_cpu.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012 Fujitsu Ltd.
+ * Author: Wanlong Gao <gaowanlong@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "test.h"
+#include "safe_macros.h"
+
+long tst_ncpus(void)
+{
+ long ncpus = -1;
+#ifdef _SC_NPROCESSORS_ONLN
+ ncpus = SAFE_SYSCONF(NULL, _SC_NPROCESSORS_ONLN);
+#else
+ tst_brkm(TBROK, NULL, "could not determine number of CPUs online");
+#endif
+ return ncpus;
+}
+
+long tst_ncpus_conf(void)
+{
+ long ncpus_conf = -1;
+#ifdef _SC_NPROCESSORS_CONF
+ ncpus_conf = SAFE_SYSCONF(NULL, _SC_NPROCESSORS_CONF);
+#else
+ tst_brkm(TBROK, NULL, "could not determine number of CPUs configured");
+#endif
+ return ncpus_conf;
+}
+
+#define KERNEL_MAX "/sys/devices/system/cpu/kernel_max"
+
+long tst_ncpus_max(void)
+{
+ long ncpus_max = -1;
+ struct stat buf;
+
+ /* sched_getaffinity() and sched_setaffinity() cares about number of
+ * possible CPUs the OS or hardware can support, which can be larger
+ * than what sysconf(_SC_NPROCESSORS_CONF) currently provides
+ * (by enumarating /sys/devices/system/cpu/cpu* entries).
+ *
+ * Use /sys/devices/system/cpu/kernel_max, if available. This
+ * represents NR_CPUS-1, a compile time option which specifies
+ * "maximum number of CPUs which this kernel will support".
+ * This should provide cpu mask size large enough for any purposes. */
+ if (stat(KERNEL_MAX, &buf) == 0) {
+ SAFE_FILE_SCANF(NULL, KERNEL_MAX, "%ld", &ncpus_max);
+ /* this is maximum CPU index allowed by the kernel
+ * configuration, so # of cpus allowed by config is +1 */
+ ncpus_max++;
+ } else {
+ /* fall back to _SC_NPROCESSORS_CONF */
+ ncpus_max = tst_ncpus_conf();
+ }
+ return ncpus_max;
+}
diff --git a/src/kernel/tests/lib/tst_crypto.c b/src/kernel/tests/lib/tst_crypto.c
new file mode 100644
index 0000000..685e087
--- /dev/null
+++ b/src/kernel/tests/lib/tst_crypto.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018 Richard Palethorpe <rpalethorpe@suse.com>
+ * Nicolai Stange <nstange@suse.de>
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_crypto.h"
+#include "tst_netlink.h"
+
+void tst_crypto_open(struct tst_crypto_session *ses)
+{
+ TEST(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CRYPTO));
+ if (TST_RET < 0 && TST_ERR == EPROTONOSUPPORT)
+ tst_brk(TCONF | TTERRNO, "NETLINK_CRYPTO is probably disabled");
+
+ if (TST_RET < 0) {
+ tst_brk(TBROK | TTERRNO,
+ "socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CRYPTO)");
+ }
+
+ ses->fd = TST_RET;
+ ses->seq_num = 0;
+}
+
+void tst_crypto_close(struct tst_crypto_session *ses)
+{
+ SAFE_CLOSE(ses->fd);
+}
+
+static int tst_crypto_recv_ack(struct tst_crypto_session *ses)
+{
+ uint32_t len;
+ char buf[BUFSIZ];
+ struct nlmsghdr *nh;
+
+ len = SAFE_NETLINK_RECV(ses->fd, buf, sizeof(buf));
+
+ for (nh = (struct nlmsghdr *) buf;
+ NLMSG_OK(nh, len);
+ nh = NLMSG_NEXT(nh, len)) {
+ if (nh->nlmsg_seq != ses->seq_num) {
+ tst_brk(TBROK,
+ "Message out of sequence; type=0%hx, seq_num=%u (not %u)",
+ nh->nlmsg_type, nh->nlmsg_seq, ses->seq_num);
+ }
+
+ /* Acks use the error message type with error number set to
+ * zero. Ofcourse we could also receive an actual error.
+ */
+ if (nh->nlmsg_type == NLMSG_ERROR)
+ return ((struct nlmsgerr *)NLMSG_DATA(nh))->error;
+
+ tst_brk(TBROK, "Unexpected message type; type=0x%hx, seq_num=%u",
+ nh->nlmsg_type, nh->nlmsg_seq);
+ }
+
+ tst_brk(TBROK, "Empty message from netlink socket?");
+
+ return ENODATA;
+}
+
+int tst_crypto_add_alg(struct tst_crypto_session *ses,
+ const struct crypto_user_alg *alg)
+{
+ struct nlmsghdr nh = {
+ .nlmsg_len = sizeof(struct nlmsghdr) + sizeof(*alg),
+ .nlmsg_type = CRYPTO_MSG_NEWALG,
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .nlmsg_seq = ++(ses->seq_num),
+ .nlmsg_pid = 0,
+ };
+
+ SAFE_NETLINK_SEND(ses->fd, &nh, alg);
+
+ return tst_crypto_recv_ack(ses);
+}
+
+int tst_crypto_del_alg(struct tst_crypto_session *ses,
+ const struct crypto_user_alg *alg)
+{
+ unsigned int i = 0;
+ struct nlmsghdr nh = {
+ .nlmsg_len = sizeof(struct nlmsghdr) + sizeof(*alg),
+ .nlmsg_type = CRYPTO_MSG_DELALG,
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .nlmsg_pid = 0,
+ };
+
+ while (1) {
+ nh.nlmsg_seq = ++(ses->seq_num),
+
+ SAFE_NETLINK_SEND(ses->fd, &nh, alg);
+
+ TEST(tst_crypto_recv_ack(ses));
+ if (TST_RET != -EBUSY || i >= ses->retries)
+ break;
+
+ if (usleep(1) && errno != EINTR)
+ tst_brk(TBROK | TERRNO, "usleep(1)");
+
+ ++i;
+ }
+
+ return TST_RET;
+}
diff --git a/src/kernel/tests/lib/tst_device.c b/src/kernel/tests/lib/tst_device.c
new file mode 100644
index 0000000..0df8efe
--- /dev/null
+++ b/src/kernel/tests/lib/tst_device.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2014 Cyril Hrubis chrubis@suse.cz
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <uapi_xloop.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/sysmacros.h>
+#include "lapi/syscalls.h"
+#include "test.h"
+#include "safe_macros.h"
+
+#ifndef XLOOP_CTL_GET_FREE
+# define XLOOP_CTL_GET_FREE 0x4C82
+#endif
+
+#define XLOOP_CONTROL_FILE "/dev/xloop-control"
+
+#define DEV_FILE "test_dev.img"
+#define DEV_SIZE_MB 256u
+
+static char dev_path[1024];
+static int device_acquired;
+static unsigned long prev_dev_sec_write;
+
+static const char *dev_variants[] = {
+ "/dev/xloop%i",
+ "/dev/xloop/%i",
+ "/dev/block/xloop%i"
+};
+
+static int set_dev_path(int dev, char *path, size_t path_len)
+{
+ unsigned int i;
+ struct stat st;
+
+ for (i = 0; i < ARRAY_SIZE(dev_variants); i++) {
+ snprintf(path, path_len, dev_variants[i], dev);
+
+ if (stat(path, &st) == 0 && S_ISBLK(st.st_mode))
+ return 1;
+ }
+
+ return 0;
+}
+
+int tst_find_free_xloopdev(char *path, size_t path_len)
+{
+ int ctl_fd, dev_fd, rc, i;
+ struct xloop_info xloopinfo;
+ char buf[1024];
+
+ /* since Linux 3.1 */
+ ctl_fd = open(XLOOP_CONTROL_FILE, O_RDWR);
+
+ if (ctl_fd > 0) {
+ rc = ioctl(ctl_fd, XLOOP_CTL_GET_FREE);
+ close(ctl_fd);
+ if (rc >= 0) {
+ if (path)
+ set_dev_path(rc, path, path_len);
+ tst_resm(TINFO, "Found free device %d '%s'",
+ rc, path ?: "");
+ return rc;
+ }
+ tst_resm(TINFO, "Couldn't find free xloop device");
+ return -1;
+ }
+
+ switch (errno) {
+ case ENOENT:
+ break;
+ case EACCES:
+ tst_resm(TINFO | TERRNO,
+ "Not allowed to open " XLOOP_CONTROL_FILE ". "
+ "Are you root?");
+ break;
+ default:
+ tst_resm(TBROK | TERRNO, "Failed to open " XLOOP_CONTROL_FILE);
+ }
+
+ /*
+ * Older way is to iterate over /dev/xloop%i and /dev/xloop/%i and try
+ * XLOOP_GET_STATUS ioctl() which fails for free xloop devices.
+ */
+ for (i = 0; i < 256; i++) {
+
+ if (!set_dev_path(i, buf, sizeof(buf)))
+ continue;
+
+ dev_fd = open(buf, O_RDONLY);
+
+ if (dev_fd < 0)
+ continue;
+
+ if (ioctl(dev_fd, XLOOP_GET_STATUS, &xloopinfo) == 0) {
+ tst_resm(TINFO, "Device '%s' in use", buf);
+ } else {
+ if (errno != ENXIO)
+ continue;
+ tst_resm(TINFO, "Found free device '%s'", buf);
+ close(dev_fd);
+ if (path != NULL) {
+ strncpy(path, buf, path_len);
+ path[path_len-1] = '\0';
+ }
+ return i;
+ }
+
+ close(dev_fd);
+ }
+
+ tst_resm(TINFO, "No free devices found");
+
+ return -1;
+}
+
+int tst_attach_device(const char *dev, const char *file)
+{
+ int dev_fd, file_fd;
+ struct xloop_info xloopinfo;
+
+ dev_fd = open(dev, O_RDWR);
+ if (dev_fd < 0) {
+ tst_resm(TWARN | TERRNO, "open('%s', O_RDWR) failed", dev);
+ return 1;
+ }
+
+ file_fd = open(file, O_RDWR);
+ if (file_fd < 0) {
+ tst_resm(TWARN | TERRNO, "open('%s', O_RDWR) failed", file);
+ close(dev_fd);
+ return 1;
+ }
+
+ if (ioctl(dev_fd, XLOOP_SET_FD, file_fd) < 0) {
+ close(dev_fd);
+ close(file_fd);
+ tst_resm(TWARN | TERRNO, "ioctl(%s, XLOOP_SET_FD, %s) failed",
+ dev, file);
+ return 1;
+ }
+
+ /* Old mkfs.btrfs use XLOOP_GET_STATUS instead of backing_file to get
+ * associated filename, so we need to set up the device by calling
+ * XLOOP_SET_FD and XLOOP_SET_STATUS.
+ */
+ memset(&xloopinfo, 0, sizeof(xloopinfo));
+ strcpy(xloopinfo.xlo_name, file);
+
+ if (ioctl(dev_fd, XLOOP_SET_STATUS, &xloopinfo)) {
+ close(dev_fd);
+ close(file_fd);
+ tst_resm(TWARN | TERRNO,
+ "ioctl(%s, XLOOP_SET_STATUS, %s) failed", dev, file);
+ return 1;
+ }
+
+ close(dev_fd);
+ close(file_fd);
+ return 0;
+}
+
+int tst_detach_device_by_fd(const char *dev, int dev_fd)
+{
+ int ret, i;
+
+ /* keep trying to clear XLOOPDEV until we get ENXIO, a quick succession
+ * of attach/detach might not give udev enough time to complete */
+ for (i = 0; i < 40; i++) {
+ ret = ioctl(dev_fd, XLOOP_CLR_FD, 0);
+
+ if (ret && (errno == ENXIO))
+ return 0;
+
+ if (ret && (errno != EBUSY)) {
+ tst_resm(TWARN,
+ "ioctl(%s, XLOOP_CLR_FD, 0) unexpectedly failed with: %s",
+ dev, tst_strerrno(errno));
+ return 1;
+ }
+
+ usleep(50000);
+ }
+
+ tst_resm(TWARN,
+ "ioctl(%s, XLOOP_CLR_FD, 0) no ENXIO for too long", dev);
+ return 1;
+}
+
+int tst_detach_device(const char *dev)
+{
+ int dev_fd, ret;
+
+ dev_fd = open(dev, O_RDONLY);
+ if (dev_fd < 0) {
+ tst_resm(TWARN | TERRNO, "open(%s) failed", dev);
+ return 1;
+ }
+
+ ret = tst_detach_device_by_fd(dev, dev_fd);
+ close(dev_fd);
+ return ret;
+}
+
+int tst_dev_sync(int fd)
+{
+ return syscall(__NR_syncfs, fd);
+}
+
+const char *tst_acquire_xloop_device(unsigned int size, const char *filename)
+{
+ unsigned int acq_dev_size = MAX(size, DEV_SIZE_MB);
+
+ if (tst_prealloc_file(filename, 1024 * 1024, acq_dev_size)) {
+ tst_resm(TWARN | TERRNO, "Failed to create %s", filename);
+ return NULL;
+ }
+
+ if (tst_find_free_xloopdev(dev_path, sizeof(dev_path)) == -1)
+ return NULL;
+
+ if (tst_attach_device(dev_path, filename))
+ return NULL;
+
+ return dev_path;
+}
+
+const char *tst_acquire_device__(unsigned int size)
+{
+ int fd;
+ const char *dev;
+ struct stat st;
+ unsigned int acq_dev_size;
+ uint64_t ltp_dev_size;
+
+ acq_dev_size = MAX(size, DEV_SIZE_MB);
+
+ dev = getenv("LTP_DEV");
+
+ if (dev) {
+ tst_resm(TINFO, "Using test device LTP_DEV='%s'", dev);
+
+ if (stat(dev, &st)) {
+ tst_resm(TWARN | TERRNO, "stat() failed");
+ return NULL;
+ }
+
+ if (!S_ISBLK(st.st_mode)) {
+ tst_resm(TWARN, "%s is not a block device", dev);
+ return NULL;
+ }
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ tst_resm(TWARN | TERRNO,
+ "open(%s, O_RDONLY) failed", dev);
+ return NULL;
+ }
+
+ if (ioctl(fd, BLKGETSIZE64, &ltp_dev_size)) {
+ tst_resm(TWARN | TERRNO,
+ "ioctl(fd, BLKGETSIZE64, ...) failed");
+ close(fd);
+ return NULL;
+ }
+
+ if (close(fd)) {
+ tst_resm(TWARN | TERRNO,
+ "close(fd) failed");
+ return NULL;
+ }
+
+ ltp_dev_size = ltp_dev_size/1024/1024;
+
+ if (acq_dev_size <= ltp_dev_size)
+ return dev;
+
+ tst_resm(TINFO, "Skipping $LTP_DEV size %"PRIu64"MB, requested size %uMB",
+ ltp_dev_size, acq_dev_size);
+ }
+
+ dev = tst_acquire_xloop_device(acq_dev_size, DEV_FILE);
+
+ if (dev)
+ device_acquired = 1;
+
+ return dev;
+}
+
+const char *tst_acquire_device_(void (cleanup_fn)(void), unsigned int size)
+{
+ const char *device;
+
+ if (device_acquired) {
+ tst_brkm(TBROK, cleanup_fn, "Device already acquired");
+ return NULL;
+ }
+
+ if (!tst_tmpdir_created()) {
+ tst_brkm(TBROK, cleanup_fn,
+ "Cannot acquire device without tmpdir() created");
+ return NULL;
+ }
+
+ device = tst_acquire_device__(size);
+
+ if (!device) {
+ tst_brkm(TBROK, cleanup_fn, "Failed to acquire device");
+ return NULL;
+ }
+
+ return device;
+}
+
+int tst_release_device(const char *dev)
+{
+ int ret;
+
+ if (!device_acquired)
+ return 0;
+
+ /*
+ * Loop device was created -> we need to detach it.
+ *
+ * The file image is deleted in tst_rmdir();
+ */
+ ret = tst_detach_device(dev);
+
+ device_acquired = 0;
+
+ return ret;
+}
+
+int tst_clear_device(const char *dev)
+{
+ if (tst_fill_file(dev, 0, 1024, 512)) {
+ tst_resm(TWARN, "Failed to clear 512k block on %s", dev);
+ return 1;
+ }
+
+ return 0;
+}
+
+int tst_umount(const char *path)
+{
+ int err, ret, i;
+
+ for (i = 0; i < 50; i++) {
+ ret = umount(path);
+ err = errno;
+
+ if (!ret)
+ return 0;
+
+ if (err != EBUSY) {
+ tst_resm(TWARN, "umount('%s') failed with %s",
+ path, tst_strerrno(err));
+ errno = err;
+ return ret;
+ }
+
+ tst_resm(TINFO, "umount('%s') failed with %s, try %2i...",
+ path, tst_strerrno(err), i+1);
+
+ if (i == 0) {
+ tst_resm(TINFO, "Likely gvfsd-trash is probing newly "
+ "mounted fs, kill it to speed up tests.");
+ }
+
+ usleep(100000);
+ }
+
+ tst_resm(TWARN, "Failed to umount('%s') after 50 retries", path);
+ errno = err;
+ return -1;
+}
+
+int tst_is_mounted(const char *path)
+{
+ char line[PATH_MAX];
+ FILE *file;
+ int ret = 0;
+
+ file = SAFE_FOPEN(NULL, "/proc/mounts", "r");
+
+ while (fgets(line, sizeof(line), file)) {
+ if (strstr(line, path) != NULL) {
+ ret = 1;
+ break;
+ }
+ }
+
+ SAFE_FCLOSE(NULL, file);
+
+ if (!ret)
+ tst_resm(TINFO, "No device is mounted at %s", path);
+
+ return ret;
+}
+
+int tst_is_mounted_at_tmpdir(const char *path)
+{
+ char cdir[PATH_MAX], mpath[PATH_MAX];
+ int ret;
+
+ if (!getcwd(cdir, PATH_MAX)) {
+ tst_resm(TWARN | TERRNO, "Failed to find current directory");
+ return 0;
+ }
+
+ ret = snprintf(mpath, PATH_MAX, "%s/%s", cdir, path);
+ if (ret < 0 || ret >= PATH_MAX) {
+ tst_resm(TWARN | TERRNO,
+ "snprintf() should have returned %d instead of %d",
+ PATH_MAX, ret);
+ return 0;
+ }
+
+ return tst_is_mounted(mpath);
+}
+
+int find_stat_file(const char *dev, char *path, size_t path_len)
+{
+ const char *devname = strrchr(dev, '/') + 1;
+
+ snprintf(path, path_len, "/sys/block/%s/stat", devname);
+
+ if (!access(path, F_OK))
+ return 1;
+
+ DIR *dir = SAFE_OPENDIR(NULL, "/sys/block/");
+ struct dirent *ent;
+
+ while ((ent = readdir(dir))) {
+ snprintf(path, path_len, "/sys/block/%s/%s/stat", ent->d_name, devname);
+
+ if (!access(path, F_OK)) {
+ SAFE_CLOSEDIR(NULL, dir);
+ return 1;
+ }
+ }
+
+ SAFE_CLOSEDIR(NULL, dir);
+ return 0;
+}
+
+unsigned long tst_dev_bytes_written(const char *dev)
+{
+ unsigned long dev_sec_write = 0, dev_bytes_written, io_ticks = 0;
+ char dev_stat_path[1024];
+
+ if (!find_stat_file(dev, dev_stat_path, sizeof(dev_stat_path)))
+ tst_brkm(TCONF, NULL, "Test device stat file: %s not found",
+ dev_stat_path);
+
+ SAFE_FILE_SCANF(NULL, dev_stat_path,
+ "%*s %*s %*s %*s %*s %*s %lu %*s %*s %lu",
+ &dev_sec_write, &io_ticks);
+
+ if (!io_ticks)
+ tst_brkm(TCONF, NULL, "Test device stat file: %s broken",
+ dev_stat_path);
+
+ dev_bytes_written = (dev_sec_write - prev_dev_sec_write) * 512;
+
+ prev_dev_sec_write = dev_sec_write;
+
+ return dev_bytes_written;
+}
+
+void tst_find_backing_dev(const char *path, char *dev)
+{
+ char fmt[20];
+ struct stat buf;
+ FILE *file;
+ char line[PATH_MAX];
+ char *pre = NULL;
+ char *next = NULL;
+
+ if (stat(path, &buf) < 0)
+ tst_brkm(TWARN | TERRNO, NULL, "stat() failed");
+
+ snprintf(fmt, sizeof(fmt), "%u:%u", major(buf.st_dev), minor(buf.st_dev));
+ file = SAFE_FOPEN(NULL, "/proc/self/mountinfo", "r");
+
+ while (fgets(line, sizeof(line), file)) {
+ if (strstr(line, fmt) != NULL) {
+ pre = strstr(line, " - ");
+ pre = strtok_r(pre, " ", &next);
+ pre = strtok_r(NULL, " ", &next);
+ pre = strtok_r(NULL, " ", &next);
+ strcpy(dev, pre);
+ break;
+ }
+ }
+
+ SAFE_FCLOSE(NULL, file);
+
+ if (stat(dev, &buf) < 0)
+ tst_brkm(TWARN | TERRNO, NULL, "stat(%s) failed", dev);
+
+ if (S_ISBLK(buf.st_mode) != 1)
+ tst_brkm(TCONF, NULL, "dev(%s) isn't a block dev", dev);
+}
diff --git a/src/kernel/tests/lib/tst_dir_is_empty.c b/src/kernel/tests/lib/tst_dir_is_empty.c
new file mode 100644
index 0000000..43764ee
--- /dev/null
+++ b/src/kernel/tests/lib/tst_dir_is_empty.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
+ *
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "test.h"
+#include "safe_macros.h"
+
+int tst_dir_is_empty_(void (cleanup_fn)(void), const char *name, int verbose)
+{
+ struct dirent *entry;
+ DIR *dir = SAFE_OPENDIR(cleanup_fn, name);
+ int ret = 1;
+
+ while ((entry = SAFE_READDIR(cleanup_fn, dir)) != NULL) {
+ const char *file = entry->d_name;
+
+ if (!strcmp(file, "..") || !strcmp(file, "."))
+ continue;
+
+ if (verbose)
+ tst_resm(TINFO, "found a file: %s", file);
+ ret = 0;
+ break;
+ }
+
+ SAFE_CLOSEDIR(cleanup_fn, dir);
+
+ return ret;
+}
diff --git a/src/kernel/tests/lib/tst_fill_file.c b/src/kernel/tests/lib/tst_fill_file.c
new file mode 100644
index 0000000..8047200
--- /dev/null
+++ b/src/kernel/tests/lib/tst_fill_file.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Stanislav Kholmanskikh <stanislav.kholmanskikh@oracle.com>
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "lapi/fallocate.h"
+
+#include "test.h"
+
+int tst_fill_fd(int fd, char pattern, size_t bs, size_t bcount)
+{
+ size_t i;
+ char *buf;
+
+ /* Filling a memory buffer with provided pattern */
+ buf = malloc(bs);
+ if (buf == NULL)
+ return -1;
+
+ for (i = 0; i < bs; i++)
+ buf[i] = pattern;
+
+ /* Filling the file */
+ for (i = 0; i < bcount; i++) {
+ if (write(fd, buf, bs) != (ssize_t)bs) {
+ free(buf);
+ return -1;
+ }
+ }
+
+ free(buf);
+
+ return 0;
+}
+
+int tst_prealloc_size_fd(int fd, size_t bs, size_t bcount)
+{
+ int ret;
+
+ errno = 0;
+ ret = fallocate(fd, 0, 0, bs * bcount);
+
+ if (ret && errno == ENOSPC)
+ return ret;
+
+ if (ret)
+ ret = tst_fill_fd(fd, 0, bs, bcount);
+
+ return ret;
+}
+
+int tst_fill_file(const char *path, char pattern, size_t bs, size_t bcount)
+{
+ int fd;
+
+ fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
+ if (fd < 0)
+ return -1;
+
+ if (tst_fill_fd(fd, pattern, bs, bcount)) {
+ close(fd);
+ unlink(path);
+ return -1;
+ }
+
+ if (close(fd) < 0) {
+ unlink(path);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int tst_prealloc_file(const char *path, size_t bs, size_t bcount)
+{
+ int fd;
+
+ fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
+ if (fd < 0)
+ return -1;
+
+ if (tst_prealloc_size_fd(fd, bs, bcount)) {
+ close(fd);
+ unlink(path);
+ return -1;
+ }
+
+ if (close(fd) < 0) {
+ unlink(path);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/kernel/tests/lib/tst_fill_fs.c b/src/kernel/tests/lib/tst_fill_fs.c
new file mode 100644
index 0000000..121dd2f
--- /dev/null
+++ b/src/kernel/tests/lib/tst_fill_fs.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/statvfs.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_fs.h"
+
+void tst_fill_fs(const char *path, int verbose)
+{
+ int i = 0;
+ char file[PATH_MAX];
+ char buf[4096];
+ size_t len;
+ ssize_t ret;
+ int fd;
+ struct statvfs fi;
+ statvfs(path, &fi);
+
+ for (;;) {
+ len = random() % (1024 * 102400);
+
+ snprintf(file, sizeof(file), "%s/file%i", path, i++);
+
+ if (verbose)
+ tst_res(TINFO, "Creating file %s size %zu", file, len);
+
+ fd = open(file, O_WRONLY | O_CREAT, 0700);
+ if (fd == -1) {
+ if (errno != ENOSPC)
+ tst_brk(TBROK | TERRNO, "open()");
+
+ tst_res(TINFO | TERRNO, "open()");
+ return;
+ }
+
+ while (len) {
+ ret = write(fd, buf, MIN(len, sizeof(buf)));
+
+ if (ret < 0) {
+ /* retry on ENOSPC to make sure filesystem is really full */
+ if (errno == ENOSPC && len >= fi.f_bsize/2) {
+ SAFE_FSYNC(fd);
+ len /= 2;
+ continue;
+ }
+
+ SAFE_CLOSE(fd);
+
+ if (errno != ENOSPC)
+ tst_brk(TBROK | TERRNO, "write()");
+
+ tst_res(TINFO | TERRNO, "write()");
+ return;
+ }
+
+ len -= ret;
+ }
+
+ SAFE_CLOSE(fd);
+ }
+}
diff --git a/src/kernel/tests/lib/tst_fs_has_free.c b/src/kernel/tests/lib/tst_fs_has_free.c
new file mode 100644
index 0000000..e82dfa8
--- /dev/null
+++ b/src/kernel/tests/lib/tst_fs_has_free.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Fujitsu Ltd.
+ * Author: Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * DESCRIPTION
+ * Check if the mounted file system has enough free space,
+ * if it is, tst_fs_has_free() returns 1, otherwise 0.
+ */
+
+#include <stdint.h>
+#include <sys/vfs.h>
+#include "test.h"
+#include "tst_fs.h"
+
+int tst_fs_has_free_(void (*cleanup)(void), const char *path,
+ unsigned int size, unsigned int mult)
+{
+ struct statfs sf;
+
+ if (statfs(path, &sf)) {
+ tst_brkm(TBROK | TERRNO, cleanup,
+ "tst_fs_has_free: failed to statfs(%s)", path);
+ return 0;
+ }
+
+ if ((uint64_t)sf.f_bavail * sf.f_bsize >= (uint64_t)size * mult)
+ return 1;
+
+ return 0;
+}
diff --git a/src/kernel/tests/lib/tst_fs_link_count.c b/src/kernel/tests/lib/tst_fs_link_count.c
new file mode 100644
index 0000000..860510d
--- /dev/null
+++ b/src/kernel/tests/lib/tst_fs_link_count.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2014 Fujitsu Ltd.
+ * Author: Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "test.h"
+#include "usctest.h"
+#include "safe_macros.h"
+
+#define MAX_SANE_HARD_LINKS 65535
+
+/*
+ * filesystems whose subdir limit is less than MAX_SANE_HARD_LINKS
+ * XXX: we cannot filter ext4 out, because ext2/ext3/ext4 have the
+ * same magic number
+ */
+const long subdir_limit_whitelist[] = {
+ TST_EXT2_OLD_MAGIC, TST_EXT234_MAGIC, TST_MINIX_MAGIC,
+ TST_MINIX_MAGIC2, TST_MINIX2_MAGIC, TST_MINIX2_MAGIC2,
+ TST_MINIX3_MAGIC, TST_UDF_MAGIC, TST_SYSV2_MAGIC,
+ TST_SYSV4_MAGIC, TST_UFS_MAGIC, TST_UFS2_MAGIC,
+ TST_F2FS_MAGIC, TST_NILFS_MAGIC, TST_EXOFS_MAGIC
+};
+
+int tst_fs_fill_hardlinks_(void (*cleanup) (void), const char *dir)
+{
+ unsigned int i, j;
+ char base_filename[PATH_MAX], link_filename[PATH_MAX];
+ struct stat s;
+
+ if (stat(dir, &s) == -1 && errno == ENOENT)
+ SAFE_MKDIR(cleanup, dir, 0744);
+
+ SAFE_STAT(cleanup, dir, &s);
+ if (!S_ISDIR(s.st_mode)) {
+ tst_brkm(TBROK, cleanup, "%s is not directory", dir);
+ return 0;
+ }
+
+ sprintf(base_filename, "%s/testfile0", dir);
+ SAFE_TOUCH(cleanup, base_filename, 0644, NULL);
+
+ for (i = 1; i < MAX_SANE_HARD_LINKS; i++) {
+ sprintf(link_filename, "%s/testfile%d", dir, i);
+
+ if (link(base_filename, link_filename) == 0)
+ continue;
+
+ switch (errno) {
+ case EMLINK:
+ SAFE_STAT(cleanup, base_filename, &s);
+ if (s.st_nlink != i) {
+ tst_brkm(TBROK, cleanup, "wrong number of "
+ "hard links for %s have %i, should be"
+ " %d", base_filename,
+ (int)s.st_nlink, i);
+ return 0;
+ } else {
+ tst_resm(TINFO, "the maximum number of hard "
+ "links to %s is hit: %d",
+ base_filename, (int)s.st_nlink);
+ return s.st_nlink;
+ }
+ case ENOSPC:
+ case EDQUOT:
+ tst_resm(TINFO | TERRNO, "link(%s, %s) failed",
+ base_filename, link_filename);
+ goto max_hardlinks_cleanup;
+ default:
+ tst_brkm(TBROK, cleanup, "link(%s, %s) failed "
+ "unexpectedly: %s", base_filename,
+ link_filename, strerror(errno));
+ return 0;
+ }
+ }
+
+ tst_resm(TINFO, "Failed reach the hardlinks limit");
+
+max_hardlinks_cleanup:
+ for (j = 0; j < i; j++) {
+ sprintf(link_filename, "%s/testfile%d", dir, j);
+ SAFE_UNLINK(cleanup, link_filename);
+ }
+
+ return 0;
+}
+
+int tst_fs_fill_subdirs_(void (*cleanup) (void), const char *dir)
+{
+ unsigned int i, j, whitelist_size;
+ char dirname[PATH_MAX];
+ struct stat s;
+ long fs_type;
+
+ if (stat(dir, &s) == -1 && errno == ENOENT)
+ SAFE_MKDIR(cleanup, dir, 0744);
+
+ SAFE_STAT(cleanup, dir, &s);
+ if (!S_ISDIR(s.st_mode)) {
+ tst_brkm(TBROK, cleanup, "%s is not directory", dir);
+ return 0;
+ }
+
+ /* for current kernel, subdir limit is not availiable for all fs */
+ fs_type = tst_fs_type(cleanup, dir);
+
+ whitelist_size = ARRAY_SIZE(subdir_limit_whitelist);
+ for (i = 0; i < whitelist_size; i++) {
+ if (fs_type == subdir_limit_whitelist[i])
+ break;
+ }
+ if (i == whitelist_size) {
+ tst_resm(TINFO, "subdir limit is not availiable for "
+ "%s filesystem", tst_fs_type_name(fs_type));
+ return 0;
+ }
+
+ for (i = 0; i < MAX_SANE_HARD_LINKS; i++) {
+ sprintf(dirname, "%s/testdir%d", dir, i);
+
+ if (mkdir(dirname, 0755) == 0)
+ continue;
+
+ switch (errno) {
+ case EMLINK:
+ SAFE_STAT(cleanup, dir, &s);
+ /*
+ * i+2 because there are two links to each newly
+ * created directory (the '.' and link from parent dir)
+ */
+ if (s.st_nlink != (i + 2)) {
+ tst_brkm(TBROK, cleanup, "%s link counts have"
+ "%d, should be %d", dir,
+ (int)s.st_nlink, i + 2);
+ return 0;
+ } else {
+ tst_resm(TINFO, "the maximum subdirectories in "
+ "%s is hit: %d", dir, (int)s.st_nlink);
+ return s.st_nlink;
+ }
+ case ENOSPC:
+ case EDQUOT:
+ tst_resm(TINFO | TERRNO, "mkdir(%s, 0755) failed",
+ dirname);
+ goto max_subdirs_cleanup;
+ default:
+ tst_brkm(TBROK, cleanup, "mkdir(%s, 0755) failed "
+ "unexpectedly: %s", dirname,
+ strerror(errno));
+ return 0;
+ }
+
+ }
+
+ tst_resm(TINFO, "Failed reach the subdirs limit on %s filesystem",
+ tst_fs_type_name(fs_type));
+
+max_subdirs_cleanup:
+ for (j = 0; j < i; j++) {
+ sprintf(dirname, "%s/testdir%d", dir, j);
+ SAFE_RMDIR(cleanup, dirname);
+ }
+
+ return 0;
+}
diff --git a/src/kernel/tests/lib/tst_fs_setup.c b/src/kernel/tests/lib/tst_fs_setup.c
new file mode 100644
index 0000000..54ea370
--- /dev/null
+++ b/src/kernel/tests/lib/tst_fs_setup.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/mount.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_fs.h"
+
+#define TST_FS_SETUP_OVERLAYFS_MSG "overlayfs is not configured in this kernel"
+#define TST_FS_SETUP_OVERLAYFS_CONFIG "lowerdir="OVL_LOWER",upperdir="OVL_UPPER",workdir="OVL_WORK
+
+void create_overlay_dirs(void)
+{
+ DIR *dir = opendir(OVL_LOWER);
+ if (dir == NULL) {
+ SAFE_MKDIR(OVL_LOWER, 0755);
+ SAFE_MKDIR(OVL_UPPER, 0755);
+ SAFE_MKDIR(OVL_WORK, 0755);
+ SAFE_MKDIR(OVL_MNT, 0755);
+ return;
+ }
+ closedir(dir);
+}
+
+int mount_overlay(const char *file, const int lineno, int skip)
+{
+ int ret;
+
+ create_overlay_dirs();
+ ret = mount("overlay", OVL_MNT, "overlay", 0,
+ TST_FS_SETUP_OVERLAYFS_CONFIG);
+ if (ret == 0)
+ return 0;
+
+ if (errno == ENODEV) {
+ if (skip) {
+ tst_brk(TCONF, "%s:%d: " TST_FS_SETUP_OVERLAYFS_MSG,
+ file, lineno);
+ } else {
+ tst_res(TINFO, "%s:%d: " TST_FS_SETUP_OVERLAYFS_MSG,
+ file, lineno);
+ }
+ } else {
+ tst_brk(TBROK | TERRNO, "overlayfs mount failed");
+ }
+ return ret;
+}
diff --git a/src/kernel/tests/lib/tst_fs_type.c b/src/kernel/tests/lib/tst_fs_type.c
new file mode 100644
index 0000000..1d0ac96
--- /dev/null
+++ b/src/kernel/tests/lib/tst_fs_type.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2005-2014 Linux Test Project
+ *
+ * Cyril Hrubis <chrubis@suse.cz> 2014
+ * Michal Simek <monstr@monstr.eu> 2009
+ * Kumar Gala <galak@kernel.crashing.org> 2007
+ * Ricky Ng-Adam <rngadam@yahoo.com> 2005
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <sys/vfs.h>
+#include "test.h"
+#include "tst_fs.h"
+
+long tst_fs_type_(void (*cleanup)(void), const char *path)
+{
+ struct statfs sbuf;
+
+ if (statfs(path, &sbuf)) {
+ tst_brkm(TBROK | TERRNO, cleanup,
+ "tst_fs_type: Failed to statfs(%s)", path);
+ return 0;
+ }
+
+ return sbuf.f_type;
+}
+
+const char *tst_fs_type_name(long f_type)
+{
+ switch (f_type) {
+ case TST_TMPFS_MAGIC:
+ return "TMPFS";
+ case TST_NFS_MAGIC:
+ return "NFS";
+ case TST_V9FS_MAGIC:
+ return "9P";
+ case TST_RAMFS_MAGIC:
+ return "RAMFS";
+ case TST_BTRFS_MAGIC:
+ return "BTRFS";
+ case TST_XFS_MAGIC:
+ return "XFS";
+ case TST_EXT2_OLD_MAGIC:
+ return "EXT2";
+ case TST_EXT234_MAGIC:
+ return "EXT2/EXT3/EXT4";
+ case TST_MINIX_MAGIC:
+ case TST_MINIX_MAGIC2:
+ case TST_MINIX2_MAGIC:
+ case TST_MINIX2_MAGIC2:
+ case TST_MINIX3_MAGIC:
+ return "MINIX";
+ case TST_UDF_MAGIC:
+ return "UDF";
+ case TST_SYSV2_MAGIC:
+ case TST_SYSV4_MAGIC:
+ return "SYSV";
+ case TST_UFS_MAGIC:
+ case TST_UFS2_MAGIC:
+ return "UFS";
+ case TST_F2FS_MAGIC:
+ return "F2FS";
+ case TST_NILFS_MAGIC:
+ return "NILFS";
+ case TST_EXOFS_MAGIC:
+ return "EXOFS";
+ case TST_OVERLAYFS_MAGIC:
+ return "OVERLAYFS";
+ default:
+ return "Unknown";
+ }
+}
diff --git a/src/kernel/tests/lib/tst_get_bad_addr.c b/src/kernel/tests/lib/tst_get_bad_addr.c
new file mode 100644
index 0000000..098e72b
--- /dev/null
+++ b/src/kernel/tests/lib/tst_get_bad_addr.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/mman.h>
+#include "test.h"
+#include "tst_get_bad_addr.h"
+
+void *tst_get_bad_addr(void (*cleanup_fn) (void))
+{
+ void *bad_addr;
+
+ bad_addr = mmap(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+ if (bad_addr == MAP_FAILED)
+ tst_brkm(TBROK, cleanup_fn, "mmap() failed to get bad address");
+
+ return bad_addr;
+}
diff --git a/src/kernel/tests/lib/tst_hugepage.c b/src/kernel/tests/lib/tst_hugepage.c
new file mode 100644
index 0000000..1d0e62e
--- /dev/null
+++ b/src/kernel/tests/lib/tst_hugepage.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ */
+
+#define TST_NO_DEFAULT_MAIN
+
+#include "tst_test.h"
+#include "tst_hugepage.h"
+
+unsigned long tst_hugepages;
+char *nr_opt;
+char *Hopt;
+
+size_t tst_get_hugepage_size(void)
+{
+ if (access(PATH_HUGEPAGES, F_OK))
+ return 0;
+
+ return SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
+}
+
+unsigned long tst_request_hugepages(unsigned long hpages)
+{
+ unsigned long val, max_hpages;
+
+ if (access(PATH_HUGEPAGES, F_OK)) {
+ tst_hugepages = 0;
+ goto out;
+ }
+
+ if (nr_opt)
+ tst_hugepages = SAFE_STRTOL(nr_opt, 1, LONG_MAX);
+ else
+ tst_hugepages = hpages;
+
+ SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "3");
+ max_hpages = SAFE_READ_MEMINFO("MemFree:") / SAFE_READ_MEMINFO("Hugepagesize:");
+
+ if (tst_hugepages > max_hpages) {
+ tst_res(TINFO, "Requested number(%lu) of hugepages is too large, "
+ "limiting to 80%% of the max hugepage count %lu",
+ tst_hugepages, max_hpages);
+ tst_hugepages = max_hpages * 0.8;
+
+ if (tst_hugepages < 1)
+ goto out;
+ }
+
+ tst_sys_conf_save("?/proc/sys/vm/nr_hugepages");
+ SAFE_FILE_PRINTF(PATH_NR_HPAGES, "%lu", tst_hugepages);
+ SAFE_FILE_SCANF(PATH_NR_HPAGES, "%lu", &val);
+ if (val != tst_hugepages)
+ tst_brk(TCONF, "nr_hugepages = %lu, but expect %lu. "
+ "Not enough hugepages for testing.",
+ val, tst_hugepages);
+
+ tst_res(TINFO, "%lu hugepage(s) reserved", tst_hugepages);
+out:
+ return tst_hugepages;
+}
diff --git a/src/kernel/tests/lib/tst_ioctl.c b/src/kernel/tests/lib/tst_ioctl.c
new file mode 100644
index 0000000..364220b
--- /dev/null
+++ b/src/kernel/tests/lib/tst_ioctl.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#define TST_NO_DEFAULT_MAIN
+
+#include "tst_test.h"
+
+int tst_fibmap(const char *filename)
+{
+ /* test if FIBMAP ioctl is supported */
+ int fd, block = 0;
+
+ fd = open(filename, O_RDWR | O_CREAT, 0666);
+ if (fd < 0) {
+ tst_res(TWARN | TERRNO,
+ "open(%s, O_RDWR | O_CREAT, 0666) failed", filename);
+ return -1;
+ }
+
+ if (ioctl(fd, FIBMAP, &block)) {
+ tst_res(TINFO | TERRNO, "FIBMAP ioctl is NOT supported");
+ close(fd);
+ return 1;
+ }
+ tst_res(TINFO, "FIBMAP ioctl is supported");
+
+ if (close(fd)) {
+ tst_res(TWARN | TERRNO, "close(fd) failed");
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/kernel/tests/lib/tst_kconfig.c b/src/kernel/tests/lib/tst_kconfig.c
new file mode 100644
index 0000000..d49187b
--- /dev/null
+++ b/src/kernel/tests/lib/tst_kconfig.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_kconfig.h"
+
+static const char *kconfig_path(char *path_buf, size_t path_buf_len)
+{
+ const char *path = getenv("KCONFIG_PATH");
+ struct utsname un;
+
+ if (path) {
+ if (!access(path, F_OK))
+ return path;
+
+ tst_res(TWARN, "KCONFIG_PATH='%s' does not exist", path);
+ }
+
+ if (!access("/proc/config.gz", F_OK))
+ return "/proc/config.gz";
+
+ uname(&un);
+
+ /* Debian and derivatives */
+ snprintf(path_buf, path_buf_len, "/boot/config-%s", un.release);
+
+ if (!access(path_buf, F_OK))
+ return path_buf;
+
+ /* Clear Linux */
+ snprintf(path_buf, path_buf_len, "/lib/kernel/config-%s", un.release);
+
+ if (!access(path_buf, F_OK))
+ return path_buf;
+
+ tst_res(TINFO, "Couldn't locate kernel config!");
+
+ return NULL;
+}
+
+static char is_gzip;
+
+static FILE *open_kconfig(void)
+{
+ FILE *fp;
+ char buf[1064];
+ char path_buf[1024];
+ const char *path = kconfig_path(path_buf, sizeof(path_buf));
+
+ if (!path)
+ return NULL;
+
+ tst_res(TINFO, "Parsing kernel config '%s'", path);
+
+ is_gzip = !!strstr(path, ".gz");
+
+ if (is_gzip) {
+ snprintf(buf, sizeof(buf), "zcat '%s'", path);
+ fp = popen(buf, "r");
+ } else {
+ fp = fopen(path, "r");
+ }
+
+ if (!fp)
+ tst_brk(TBROK | TERRNO, "Failed to open '%s'", path);
+
+ return fp;
+}
+
+static void close_kconfig(FILE *fp)
+{
+ if (is_gzip)
+ pclose(fp);
+ else
+ fclose(fp);
+}
+
+struct match {
+ /* match len, string length up to \0 or = */
+ size_t len;
+ /* if set part of conf string after = */
+ const char *val;
+ /* if set the config option was matched already */
+ int match;
+};
+
+static int is_set(const char *str, const char *val)
+{
+ size_t vlen = strlen(val);
+
+ while (isspace(*str))
+ str++;
+
+ if (strncmp(str, val, vlen))
+ return 0;
+
+ switch (str[vlen]) {
+ case ' ':
+ case '\n':
+ case '\0':
+ return 1;
+ break;
+ default:
+ return 0;
+ }
+}
+
+static inline int match(struct match *match, const char *conf,
+ struct tst_kconfig_res *result, const char *line)
+{
+ if (match->match)
+ return 0;
+
+ const char *cfg = strstr(line, "CONFIG_");
+
+ if (!cfg)
+ return 0;
+
+ if (strncmp(cfg, conf, match->len))
+ return 0;
+
+ const char *val = &cfg[match->len];
+
+ switch (cfg[match->len]) {
+ case '=':
+ break;
+ case ' ':
+ if (is_set(val, "is not set")) {
+ result->match = 'n';
+ goto match;
+ }
+ /* fall through */
+ default:
+ return 0;
+ }
+
+ if (is_set(val, "=y")) {
+ result->match = 'y';
+ goto match;
+ }
+
+ if (is_set(val, "=m")) {
+ result->match = 'm';
+ goto match;
+ }
+
+ result->match = 'v';
+ result->value = strndup(val+1, strlen(val)-2);
+
+match:
+ match->match = 1;
+ return 1;
+}
+
+void tst_kconfig_read(const char *const *kconfigs,
+ struct tst_kconfig_res results[], size_t cnt)
+{
+ struct match matches[cnt];
+ FILE *fp;
+ unsigned int i, j;
+ char buf[1024];
+
+ for (i = 0; i < cnt; i++) {
+ const char *val = strchr(kconfigs[i], '=');
+
+ if (strncmp("CONFIG_", kconfigs[i], 7))
+ tst_brk(TBROK, "Invalid config string '%s'", kconfigs[i]);
+
+ matches[i].match = 0;
+ matches[i].len = strlen(kconfigs[i]);
+
+ if (val) {
+ matches[i].val = val + 1;
+ matches[i].len -= strlen(val);
+ }
+
+ results[i].match = 0;
+ results[i].value = NULL;
+ }
+
+ fp = open_kconfig();
+ if (!fp)
+ tst_brk(TBROK, "Cannot parse kernel .config");
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ for (i = 0; i < cnt; i++) {
+ if (match(&matches[i], kconfigs[i], &results[i], buf)) {
+ for (j = 0; j < cnt; j++) {
+ if (matches[j].match)
+ break;
+ }
+
+ if (j == cnt)
+ goto exit;
+ }
+ }
+
+ }
+
+exit:
+ close_kconfig(fp);
+}
+
+static size_t array_len(const char *const kconfigs[])
+{
+ size_t i = 0;
+
+ while (kconfigs[++i]);
+
+ return i;
+}
+
+static int compare_res(struct tst_kconfig_res *res, const char *kconfig,
+ char match, const char *val)
+{
+ if (res->match != match) {
+ tst_res(TINFO, "Needs kernel %s, have %c", kconfig, res->match);
+ return 1;
+ }
+
+ if (match != 'v')
+ return 0;
+
+ if (strcmp(res->value, val)) {
+ tst_res(TINFO, "Needs kernel %s, have %s", kconfig, res->value);
+ return 1;
+ }
+
+ return 0;
+}
+
+void tst_kconfig_check(const char *const kconfigs[])
+{
+ size_t cnt = array_len(kconfigs);
+ struct tst_kconfig_res results[cnt];
+ unsigned int i;
+ int abort_test = 0;
+
+ tst_kconfig_read(kconfigs, results, cnt);
+
+ for (i = 0; i < cnt; i++) {
+ if (results[i].match == 0) {
+ tst_res(TINFO, "Missing kernel %s", kconfigs[i]);
+ abort_test = 1;
+ continue;
+ }
+
+ if (results[i].match == 'n') {
+ tst_res(TINFO, "Kernel %s is not set", kconfigs[i]);
+ abort_test = 1;
+ continue;
+ }
+
+ const char *val = strchr(kconfigs[i], '=');
+
+ if (val) {
+ char match = 'v';
+ val++;
+
+ if (!strcmp(val, "y"))
+ match = 'y';
+
+ if (!strcmp(val, "m"))
+ match = 'm';
+
+ if (compare_res(&results[i], kconfigs[i], match, val))
+ abort_test = 1;
+
+ }
+
+ free(results[i].value);
+ }
+
+ if (abort_test)
+ tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!");
+}
diff --git a/src/kernel/tests/lib/tst_kernel.c b/src/kernel/tests/lib/tst_kernel.c
new file mode 100644
index 0000000..57fa4b2
--- /dev/null
+++ b/src/kernel/tests/lib/tst_kernel.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/personality.h>
+#include <sys/utsname.h>
+#include "test.h"
+#include "tst_kernel.h"
+
+static int get_kernel_bits_from_uname(struct utsname *buf)
+{
+ if (uname(buf)) {
+ tst_brkm(TBROK | TERRNO, NULL, "uname()");
+ return -1;
+ }
+
+ return strstr(buf->machine, "64") ? 64 : 32;
+}
+
+int tst_kernel_bits(void)
+{
+ struct utsname buf;
+ int kernel_bits = get_kernel_bits_from_uname(&buf);
+
+ if (kernel_bits == -1)
+ return -1;
+
+ /*
+ * ARM64 (aarch64) defines 32-bit compatibility modes as
+ * armv8l and armv8b (little and big endian).
+ * s390x is 64bit but not contain 64 in the words.
+ */
+ if (!strcmp(buf.machine, "armv8l") || !strcmp(buf.machine, "armv8b")
+ || !strcmp(buf.machine, "s390x"))
+ kernel_bits = 64;
+
+#ifdef __ANDROID__
+ /* Android's bionic libc sets the PER_LINUX32 personality for all 32-bit
+ * programs. This will cause buf.machine to report as i686 even though
+ * the kernel itself is 64-bit.
+ */
+ if (!strcmp(buf.machine, "i686") &&
+ (personality(0xffffffff) & PER_MASK) == PER_LINUX32) {
+ /* Set the personality back to the default. */
+ if (personality(PER_LINUX) == -1) {
+ tst_brkm(TBROK | TERRNO, NULL, "personality()");
+ return -1;
+ }
+
+ /* Redo the uname check without the PER_LINUX32 personality to
+ * determine the actual kernel bits value.
+ */
+ kernel_bits = get_kernel_bits_from_uname(&buf);
+ if (kernel_bits == -1)
+ return -1;
+
+ /* Set the personality back to PER_LINUX32. */
+ if (personality(PER_LINUX32) == -1) {
+ tst_brkm(TBROK | TERRNO, NULL, "personality()");
+ return -1;
+ }
+ }
+#endif /* __ANDROID__ */
+
+ tst_resm(TINFO, "uname.machine=%s kernel is %ibit",
+ buf.machine, kernel_bits);
+
+ return kernel_bits;
+}
+
+int tst_check_driver(const char *name)
+{
+#ifndef __ANDROID__
+ const char * const argv[] = { "modprobe", "-n", name, NULL };
+ int res = tst_cmd_(NULL, argv, "/dev/null", "/dev/null",
+ TST_CMD_PASS_RETVAL);
+
+ /* 255 - it looks like modprobe not available */
+ return (res == 255) ? 0 : res;
+#else
+ /* Android modprobe may not have '-n', or properly installed
+ * module.*.bin files to determine built-in drivers. Assume
+ * all drivers are available.
+ */
+ return 0;
+#endif
+}
diff --git a/src/kernel/tests/lib/tst_kvercmp.c b/src/kernel/tests/lib/tst_kvercmp.c
new file mode 100644
index 0000000..5d56e30
--- /dev/null
+++ b/src/kernel/tests/lib/tst_kvercmp.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2003
+ * AUTHOR: Paul Larson <plars@linuxtestproject.org>
+ * Copyright (c) 2016 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/utsname.h>
+#include "test.h"
+
+#define OSRELEASE_PATH "/etc/os-release"
+
+static char *parse_digit(const char *str, int *d)
+{
+ unsigned long v;
+ char *end;
+
+ v = strtoul(str, &end, 10);
+ if (str == end)
+ return NULL;
+
+ if (v > INT_MAX)
+ return NULL;
+
+ *d = v;
+
+ return end;
+}
+
+int tst_parse_kver(const char *str_kver, int *v1, int *v2, int *v3)
+{
+ const char *str = str_kver;
+
+ *v1 = 0;
+ *v2 = 0;
+ *v3 = 0;
+
+ if (!(str = parse_digit(str, v1)))
+ return 1;
+
+ if (*(str++) != '.')
+ return 1;
+
+ if (!(str = parse_digit(str, v2)))
+ return 1;
+
+ /*
+ * Check for a short version e.g '2.4'
+ */
+ if (*str == ' ' || *str == '\0')
+ return 0;
+
+ if (*(str++) != '.')
+ return 1;
+
+ /*
+ * Ignore rest of the string in order not to break on versions as
+ * 4.8.1-52-default.
+ */
+ if (!parse_digit(str, v3))
+ return 1;
+
+ return 0;
+}
+
+int tst_kvcmp(const char *cur_kver, int r1, int r2, int r3)
+{
+ int a1, a2, a3;
+ int testver, currver;
+
+ if (tst_parse_kver(cur_kver, &a1, &a2, &a3)) {
+ tst_resm(TWARN,
+ "Invalid kernel version %s, expected %%d.%%d.%%d",
+ cur_kver);
+ }
+
+ testver = (r1 << 16) + (r2 << 8) + r3;
+ currver = (a1 << 16) + (a2 << 8) + a3;
+
+ return currver - testver;
+}
+
+int tst_kvercmp(int r1, int r2, int r3)
+{
+ struct utsname uval;
+
+ uname(&uval);
+
+ return tst_kvcmp(uval.release, r1, r2, r3);
+}
+
+int tst_kvexcmp(const char *tst_exv, const char *cur_ver)
+{
+ int c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0;
+ int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0;
+ int ret;
+
+ sscanf(cur_ver, "%d.%d.%d-%d.%d", &c1, &c2, &c3, &c4, &c5);
+ sscanf(tst_exv, "%d.%d.%d-%d.%d", &t1, &t2, &t3, &t4, &t5);
+
+ if ((ret = c1 - t1))
+ return ret;
+ if ((ret = c2 - t2))
+ return ret;
+ if ((ret = c3 - t3))
+ return ret;
+ if ((ret = c4 - t4))
+ return ret;
+
+ return c5 - t5;
+}
+
+const char *tst_kvcmp_distname(const char *kver)
+{
+ static char distname[64];
+ char *ret = distname;
+ char *p = distname;
+
+ if (strstr(kver, ".el5uek"))
+ return "OL5UEK";
+
+ if (strstr(kver, ".el5"))
+ return "RHEL5";
+
+ if (strstr(kver, ".el6uek"))
+ return "OL6UEK";
+
+ if (strstr(kver, ".el6"))
+ return "RHEL6";
+
+ if (access(OSRELEASE_PATH, F_OK) != -1) {
+ SAFE_FILE_LINES_SCANF(NULL, OSRELEASE_PATH, "ID=%s", distname);
+
+ if (p[0] == '"') {
+ ret = distname + 1;
+ p = ret;
+ }
+
+ while (*p) {
+ if (*p == '"') {
+ *p = 0;
+ break;
+ }
+ *p = toupper((unsigned char)*p);
+ p++;
+ }
+
+ return ret;
+ }
+
+ return NULL;
+}
+
+int tst_kvercmp2(int r1, int r2, int r3, struct tst_kern_exv *vers)
+{
+ int i;
+ const char *kver;
+ struct utsname uval;
+ const char *cur_dist_name;
+
+ uname(&uval);
+ kver = uval.release;
+ cur_dist_name = tst_kvcmp_distname(kver);
+
+ if (cur_dist_name == NULL)
+ return tst_kvercmp(r1, r2, r3);
+
+ for (i = 0; vers[i].dist_name; i++) {
+ if (!strcmp(vers[i].dist_name, cur_dist_name)) {
+ tst_resm(TINFO, "Detected %s using kernel version %s",
+ cur_dist_name, kver);
+ return tst_kvexcmp(vers[i].extra_ver, kver);
+ }
+ }
+
+ return tst_kvcmp(kver, r1, r2, r3);
+}
diff --git a/src/kernel/tests/lib/tst_lockdown.c b/src/kernel/tests/lib/tst_lockdown.c
new file mode 100644
index 0000000..e7c1981
--- /dev/null
+++ b/src/kernel/tests/lib/tst_lockdown.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#define TST_NO_DEFAULT_MAIN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+
+#include "tst_test.h"
+#include "tst_safe_macros.h"
+#include "tst_safe_stdio.h"
+#include "tst_lockdown.h"
+
+int tst_lockdown_enabled(void)
+{
+ char line[BUFSIZ];
+ FILE *file;
+
+ if (access(PATH_LOCKDOWN, F_OK) != 0) {
+ tst_res(TINFO, "Unable to determine system lockdown state");
+ return 0;
+ }
+
+ file = SAFE_FOPEN(PATH_LOCKDOWN, "r");
+ if (!fgets(line, sizeof(line), file))
+ tst_brk(TBROK | TERRNO, "fgets %s", PATH_LOCKDOWN);
+ SAFE_FCLOSE(file);
+
+ return (strstr(line, "[none]") == NULL);
+}
diff --git a/src/kernel/tests/lib/tst_memutils.c b/src/kernel/tests/lib/tst_memutils.c
new file mode 100644
index 0000000..f134d90
--- /dev/null
+++ b/src/kernel/tests/lib/tst_memutils.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 SUSE LLC <mdoucha@suse.cz>
+ */
+
+#include <unistd.h>
+#include <limits.h>
+#include <sys/sysinfo.h>
+#include <stdlib.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+
+#define BLOCKSIZE (16 * 1024 * 1024)
+
+void tst_pollute_memory(size_t maxsize, int fillchar)
+{
+ size_t i, map_count = 0, safety = 0, blocksize = BLOCKSIZE;
+ void **map_blocks;
+ struct sysinfo info;
+
+ SAFE_SYSINFO(&info);
+ safety = 4096 * SAFE_SYSCONF(_SC_PAGESIZE) / info.mem_unit;
+
+ if (info.freeswap > safety)
+ safety = 0;
+
+ /* Not enough free memory to avoid invoking OOM killer */
+ if (info.freeram <= safety)
+ return;
+
+ if (!maxsize)
+ maxsize = SIZE_MAX;
+
+ if (info.freeram - safety < maxsize / info.mem_unit)
+ maxsize = (info.freeram - safety) * info.mem_unit;
+
+ blocksize = MIN(maxsize, blocksize);
+ map_count = maxsize / blocksize;
+ map_blocks = SAFE_MALLOC(map_count * sizeof(void *));
+
+ /*
+ * Keep allocating until the first failure. The address space may be
+ * too fragmented or just smaller than maxsize.
+ */
+ for (i = 0; i < map_count; i++) {
+ map_blocks[i] = mmap(NULL, blocksize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (map_blocks[i] == MAP_FAILED) {
+ map_count = i;
+ break;
+ }
+
+ memset(map_blocks[i], fillchar, blocksize);
+ }
+
+ for (i = 0; i < map_count; i++)
+ SAFE_MUNMAP(map_blocks[i], blocksize);
+
+ free(map_blocks);
+}
diff --git a/src/kernel/tests/lib/tst_mkfs.c b/src/kernel/tests/lib/tst_mkfs.c
new file mode 100644
index 0000000..38b2e71
--- /dev/null
+++ b/src/kernel/tests/lib/tst_mkfs.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2013-2016 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "test.h"
+#include "ltp_priv.h"
+#include "tst_mkfs.h"
+#include "tst_device.h"
+
+#define OPTS_MAX 32
+
+void tst_mkfs_(const char *file, const int lineno, void (cleanup_fn)(void),
+ const char *dev, const char *fs_type,
+ const char *const fs_opts[], const char *const extra_opts[])
+{
+ int i, pos = 1, ret;
+ char mkfs[64];
+ const char *argv[OPTS_MAX] = {mkfs};
+ char fs_opts_str[1024] = "";
+ char extra_opts_str[1024] = "";
+
+ if (!dev) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: No device specified", file, lineno);
+ return;
+ }
+
+ if (!fs_type) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: No fs_type specified", file, lineno);
+ return;
+ }
+
+ snprintf(mkfs, sizeof(mkfs), "mkfs.%s", fs_type);
+
+ if (fs_opts) {
+ for (i = 0; fs_opts[i]; i++) {
+ argv[pos++] = fs_opts[i];
+
+ if (pos + 2 > OPTS_MAX) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: Too much mkfs options",
+ file, lineno);
+ return;
+ }
+
+ if (i)
+ strcat(fs_opts_str, " ");
+ strcat(fs_opts_str, fs_opts[i]);
+ }
+ }
+
+ argv[pos++] = dev;
+
+ if (extra_opts) {
+ for (i = 0; extra_opts[i]; i++) {
+ argv[pos++] = extra_opts[i];
+
+ if (pos + 1 > OPTS_MAX) {
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: Too much mkfs options", file, lineno);
+ return;
+ }
+
+ if (i)
+ strcat(extra_opts_str, " ");
+ strcat(extra_opts_str, extra_opts[i]);
+ }
+ }
+
+ argv[pos] = NULL;
+
+ if (tst_clear_device(dev))
+ tst_brkm(TBROK, cleanup_fn, "tst_clear_device() failed");
+
+ tst_resm(TINFO, "Formatting %s with %s opts='%s' extra opts='%s'",
+ dev, fs_type, fs_opts_str, extra_opts_str);
+ ret = tst_cmd(cleanup_fn, argv, "/dev/null", NULL, TST_CMD_PASS_RETVAL |
+ TST_CMD_TCONF_ON_MISSING);
+
+ switch (ret) {
+ case 0:
+ break;
+ case 255:
+ tst_brkm(TCONF, cleanup_fn,
+ "%s:%d: %s not found in $PATH", file, lineno, mkfs);
+ break;
+ default:
+ tst_brkm(TBROK, cleanup_fn,
+ "%s:%d: %s failed with %i", file, lineno, mkfs, ret);
+ }
+}
+
+const char *tst_dev_fs_type(void)
+{
+ const char *fs_type;
+
+ fs_type = getenv("LTP_DEV_FS_TYPE");
+
+ if (fs_type)
+ return fs_type;
+
+ return DEFAULT_FS_TYPE;
+}
diff --git a/src/kernel/tests/lib/tst_module.c b/src/kernel/tests/lib/tst_module.c
new file mode 100644
index 0000000..eda6187
--- /dev/null
+++ b/src/kernel/tests/lib/tst_module.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "test.h"
+#include "ltp_priv.h"
+#include "old_module.h"
+
+void tst_module_exists(void (cleanup_fn)(void),
+ const char *mod_name, char **mod_path)
+{
+ /* check current working directory */
+ if (access(mod_name, F_OK) == 0) {
+ if (mod_path != NULL)
+ *mod_path = strdup(mod_name);
+ return;
+ }
+ char *buf = NULL;
+ int err = -1;
+ /* check LTP installation path */
+ const char *ltproot = getenv("LTPROOT");
+ if (ltproot != NULL) {
+ if (asprintf(&buf, "%s/testcases/bin/%s",
+ ltproot, mod_name) == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "asprintf failed at %s:%d",
+ __FILE__, __LINE__);
+ return;
+ }
+ err = access(buf, F_OK);
+ }
+ /* check start working directory */
+ if (err == -1 && tst_tmpdir_created()) {
+ free(buf);
+ if (asprintf(&buf, "%s/%s", tst_get_startwd(),
+ mod_name) == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "asprintf failed at %s:%d",
+ __FILE__, __LINE__);
+ return;
+ }
+ err = access(buf, F_OK);
+ }
+
+ if (err != 0) {
+ free(buf);
+ tst_brkm(TCONF, cleanup_fn, "Failed to find module '%s'",
+ mod_name);
+ return;
+ }
+
+ if (mod_path != NULL)
+ *mod_path = buf;
+ else
+ free(buf);
+}
+
+void tst_module_load(void (cleanup_fn)(void),
+ const char *mod_name, char *const argv[])
+{
+ char *mod_path = NULL;
+ tst_module_exists(cleanup_fn, mod_name, &mod_path);
+
+ const int offset = 2; /* command name & module path */
+ int size = 0;
+ while (argv && argv[size])
+ ++size;
+ size += offset;
+ const char *mod_argv[size + 1]; /* + NULL in the end */
+ mod_argv[size] = NULL;
+ mod_argv[0] = "insmod";
+ mod_argv[1] = mod_path;
+
+ int i;
+ for (i = offset; i < size; ++i)
+ mod_argv[i] = argv[i - offset];
+
+ tst_cmd(cleanup_fn, mod_argv, NULL, NULL, 0);
+ free(mod_path);
+}
+
+void tst_module_unload(void (cleanup_fn)(void), const char *mod_name)
+{
+ int i, rc;
+
+ const char *const argv[] = { "rmmod", mod_name, NULL };
+
+ rc = 1;
+ for (i = 0; i < 50; i++) {
+ rc = tst_cmd(NULL, argv, "/dev/null", "/dev/null",
+ TST_CMD_PASS_RETVAL);
+ if (!rc)
+ break;
+
+ usleep(20000);
+ }
+
+ if (rc) {
+ tst_brkm(TBROK, cleanup_fn,
+ "could not unload %s module", mod_name);
+ }
+}
diff --git a/src/kernel/tests/lib/tst_net.c b/src/kernel/tests/lib/tst_net.c
new file mode 100644
index 0000000..8a589b0
--- /dev/null
+++ b/src/kernel/tests/lib/tst_net.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017-2019 Petr Vorel <pvorel@suse.cz>
+ * Copyright (c) 2019 Martin Doucha <mdoucha@suse.cz>
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_net.h"
+#include "tst_private.h"
+
+void tst_print_svar(const char *name, const char *val)
+{
+ if (name && val)
+ printf("export %s=\"%s\"\n", name, val);
+}
+
+void tst_print_svar_change(const char *name, const char *val)
+{
+ if (name && val)
+ printf("export %s=\"${%s:-%s}\"\n", name, name, val);
+}
+
+/*
+ * Function bit_count is from ipcalc project, ipcalc.c.
+ */
+static int tst_bit_count(uint32_t i)
+{
+ int c = 0;
+ unsigned int seen_one = 0;
+
+ while (i > 0) {
+ if (i & 1) {
+ seen_one = 1;
+ c++;
+ } else {
+ if (seen_one)
+ return -1;
+ }
+ i >>= 1;
+ }
+
+ return c;
+}
+
+/*
+ * Function mask2prefix is from ipcalc project, ipcalc.c.
+ */
+static int tst_mask2prefix(struct in_addr mask)
+{
+ return tst_bit_count(ntohl(mask.s_addr));
+}
+
+/*
+ * Function ipv4_mask_to_int is from ipcalc project, ipcalc.c.
+ */
+static int tst_ipv4_mask_to_int(const char *prefix)
+{
+ int ret;
+ struct in_addr in;
+
+ ret = inet_pton(AF_INET, prefix, &in);
+ if (ret == 0)
+ return -1;
+
+ return tst_mask2prefix(in);
+}
+
+/*
+ * Function safe_atoi is from ipcalc project, ipcalc.c.
+ */
+static int tst_safe_atoi(const char *s, int *ret_i)
+{
+ char *x = NULL;
+ long l;
+
+ errno = 0;
+ l = strtol(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((long)(int)l != l)
+ return -ERANGE;
+
+ *ret_i = (int)l;
+
+ return 0;
+}
+
+/*
+ * Function get_prefix use code from ipcalc project, str_to_prefix/ipcalc.c.
+ */
+int tst_get_prefix(const char *ip_str, int is_ipv6)
+{
+ char *prefix_str = NULL;
+ int prefix = -1, r;
+
+ prefix_str = strchr(ip_str, '/');
+ if (!prefix_str)
+ return -1;
+
+ *(prefix_str++) = '\0';
+
+ if (!is_ipv6 && strchr(prefix_str, '.'))
+ prefix = tst_ipv4_mask_to_int(prefix_str);
+ else {
+ r = tst_safe_atoi(prefix_str, &prefix);
+ if (r != 0)
+ tst_brk_comment("conversion error: '%s' is not integer",
+ prefix_str);
+ }
+
+ if (prefix < 0 || ((is_ipv6 && prefix > MAX_IPV6_PREFIX) ||
+ (!is_ipv6 && prefix > MAX_IPV4_PREFIX)))
+ tst_brk_comment("bad %s prefix: %s", is_ipv6 ? "IPv6" : "IPv4",
+ prefix_str);
+
+ return prefix;
+}
+
+void tst_get_in_addr(const char *ip_str, struct in_addr *ip)
+{
+ if (inet_pton(AF_INET, ip_str, ip) <= 0)
+ tst_brk_comment("bad IPv4 address: '%s'", ip_str);
+}
+
+void tst_get_in6_addr(const char *ip_str, struct in6_addr *ip6)
+{
+ if (inet_pton(AF_INET6, ip_str, ip6) <= 0)
+ tst_brk_comment("bad IPv6 address: '%s'", ip_str);
+}
+
+socklen_t tst_get_connect_address(int sock, struct sockaddr_storage *addr)
+{
+ struct sockaddr_in *inet_ptr;
+ struct sockaddr_in6 *inet6_ptr;
+ size_t tmp_size;
+ socklen_t ret = sizeof(*addr);
+
+ SAFE_GETSOCKNAME(sock, (struct sockaddr*)addr, &ret);
+
+ /* Sanitize wildcard addresses */
+ switch (addr->ss_family) {
+ case AF_INET:
+ inet_ptr = (struct sockaddr_in*)addr;
+
+ switch (ntohl(inet_ptr->sin_addr.s_addr)) {
+ case INADDR_ANY:
+ case INADDR_BROADCAST:
+ inet_ptr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ break;
+ }
+
+ break;
+
+ case AF_INET6:
+ inet6_ptr = (struct sockaddr_in6*)addr;
+ tmp_size = sizeof(struct in6_addr);
+
+ if (!memcmp(&inet6_ptr->sin6_addr, &in6addr_any, tmp_size)) {
+ memcpy(&inet6_ptr->sin6_addr, &in6addr_loopback,
+ tmp_size);
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+void tst_init_sockaddr_inet(struct sockaddr_in *sa, const char *ip_str, uint16_t port)
+{
+ memset(sa, 0, sizeof(struct sockaddr_in));
+ sa->sin_family = AF_INET;
+ sa->sin_port = htons(port);
+ tst_get_in_addr(ip_str, &sa->sin_addr);
+}
+
+void tst_init_sockaddr_inet_bin(struct sockaddr_in *sa, uint32_t ip_val, uint16_t port)
+{
+ memset(sa, 0, sizeof(struct sockaddr_in));
+ sa->sin_family = AF_INET;
+ sa->sin_port = htons(port);
+ sa->sin_addr.s_addr = htonl(ip_val);
+}
+
+void tst_init_sockaddr_inet6(struct sockaddr_in6 *sa, const char *ip_str, uint16_t port)
+{
+ memset(sa, 0, sizeof(struct sockaddr_in6));
+ sa->sin6_family = AF_INET6;
+ sa->sin6_port = htons(port);
+ tst_get_in6_addr(ip_str, &sa->sin6_addr);
+}
+
+void tst_init_sockaddr_inet6_bin(struct sockaddr_in6 *sa, const struct in6_addr *ip_val, uint16_t port)
+{
+ memset(sa, 0, sizeof(struct sockaddr_in6));
+ sa->sin6_family = AF_INET6;
+ sa->sin6_port = htons(port);
+ memcpy(&sa->sin6_addr, ip_val, sizeof(struct in6_addr));
+}
+
+void safe_getaddrinfo(const char *file, const int lineno, const char *src_addr,
+ const char *port, const struct addrinfo *hints,
+ struct addrinfo **addr_info)
+{
+ int err = getaddrinfo(src_addr, port, hints, addr_info);
+
+ if (err)
+ tst_brk(TBROK, "%s:%d: getaddrinfo failed, %s", file, lineno,
+ gai_strerror(err));
+
+ if (!*addr_info)
+ tst_brk(TBROK, "%s:%d: failed to get the address", file, lineno);
+}
diff --git a/src/kernel/tests/lib/tst_parse_opts.c b/src/kernel/tests/lib/tst_parse_opts.c
new file mode 100644
index 0000000..94970e1
--- /dev/null
+++ b/src/kernel/tests/lib/tst_parse_opts.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 Cyril Hrubis chrubis@suse.cz
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "test.h"
+#include "ltp_priv.h"
+
+void tst_parse_opts(int argc, char *argv[], const option_t *user_optarg,
+ void (*user_help)(void))
+{
+ const char *msg;
+
+ msg = parse_opts(argc, argv, user_optarg, user_help);
+
+ if (msg)
+ tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
+}
diff --git a/src/kernel/tests/lib/tst_path_has_mnt_flags.c b/src/kernel/tests/lib/tst_path_has_mnt_flags.c
new file mode 100644
index 0000000..154bf41
--- /dev/null
+++ b/src/kernel/tests/lib/tst_path_has_mnt_flags.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 Fujitsu Ltd.
+ * Author: Xing Gu <gux.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <unistd.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <string.h>
+#include "test.h"
+
+/*
+ * Check whether a path is on a filesystem that is mounted with
+ * specified flags.
+ */
+int tst_path_has_mnt_flags_(void (cleanup_fn)(void),
+ const char *path, const char *flags[])
+{
+ struct mntent *mnt;
+ size_t prefix_max = 0, prefix_len;
+ int flags_matched = 0;
+ FILE *f;
+ int i;
+ char *tmpdir = NULL;
+
+ /*
+ * Default parameter is test temporary directory
+ */
+ if (path == NULL)
+ path = tmpdir = tst_get_tmpdir();
+
+ if (access(path, F_OK) == -1) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "tst_path_has_mnt_flags: path %s doesn't exist", path);
+ return -1;
+ }
+
+ f = setmntent("/proc/mounts", "r");
+ if (f == NULL) {
+ tst_brkm(TBROK | TERRNO, cleanup_fn,
+ "tst_path_has_mnt_flags: failed to open /proc/mounts");
+ return -1;
+ }
+
+ while ((mnt = getmntent(f))) {
+ /* ignore duplicit record for root fs */
+ if (!strcmp(mnt->mnt_fsname, "rootfs"))
+ continue;
+
+ prefix_len = strlen(mnt->mnt_dir);
+
+ if (strncmp(path, mnt->mnt_dir, prefix_len) == 0
+ && prefix_len > prefix_max) {
+ prefix_max = prefix_len;
+ flags_matched = 0;
+ i = 0;
+
+ while (flags[i] != NULL) {
+ if (hasmntopt(mnt, flags[i]) != NULL)
+ flags_matched++;
+ i++;
+ }
+ }
+ }
+
+ endmntent(f);
+
+ free(tmpdir);
+
+ return flags_matched;
+}
diff --git a/src/kernel/tests/lib/tst_pid.c b/src/kernel/tests/lib/tst_pid.c
new file mode 100644
index 0000000..9568cc9
--- /dev/null
+++ b/src/kernel/tests/lib/tst_pid.c
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2009
+ * Copyright (c) 2014 Oracle and/or its affiliates. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include "test.h"
+#include "tst_pid.h"
+#include "old_safe_file_ops.h"
+
+#define PID_MAX_PATH "/proc/sys/kernel/pid_max"
+
+pid_t tst_get_unused_pid_(void (*cleanup_fn) (void))
+{
+ pid_t pid;
+
+ SAFE_FILE_SCANF(cleanup_fn, PID_MAX_PATH, "%d", &pid);
+
+ return pid;
+}
+
+int tst_get_free_pids_(void (*cleanup_fn) (void))
+{
+ FILE *f;
+ int rc, used_pids, max_pids;
+
+ f = popen("ps -eT | wc -l", "r");
+ if (!f) {
+ tst_resm(TBROK, "Could not run 'ps' to calculate used " "pids");
+ return -1;
+ }
+ rc = fscanf(f, "%i", &used_pids);
+ pclose(f);
+
+ if (rc != 1 || used_pids < 0) {
+ tst_resm(TBROK, "Could not read output of 'ps' to "
+ "calculate used pids");
+ return -1;
+ }
+
+ SAFE_FILE_SCANF(cleanup_fn, PID_MAX_PATH, "%d", &max_pids);
+
+ /* max_pids contains the maximum PID + 1,
+ * used_pids contains used PIDs + 1,
+ * so this additional '1' is eliminated by the substraction */
+ return max_pids - used_pids;
+}
diff --git a/src/kernel/tests/lib/tst_process_state.c b/src/kernel/tests/lib/tst_process_state.c
new file mode 100644
index 0000000..11790c9
--- /dev/null
+++ b/src/kernel/tests/lib/tst_process_state.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012-2014 Cyril Hrubis chrubis@suse.cz
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "test.h"
+#include "tst_process_state.h"
+
+int tst_process_state_wait(const char *file, const int lineno,
+ void (*cleanup_fn)(void), pid_t pid,
+ const char state, unsigned int msec_timeout)
+{
+ char proc_path[128], cur_state;
+ unsigned int msecs = 0;
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/%i/stat", pid);
+
+ for (;;) {
+ safe_file_scanf(file, lineno, cleanup_fn, proc_path,
+ "%*i %*s %c", &cur_state);
+
+ if (state == cur_state)
+ break;
+
+ usleep(1000);
+ msecs += 1;
+
+ if (msec_timeout && msecs >= msec_timeout) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int tst_process_state_wait2(pid_t pid, const char state)
+{
+ char proc_path[128], cur_state;
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/%i/stat", pid);
+
+ for (;;) {
+ FILE *f = fopen(proc_path, "r");
+ if (!f) {
+ fprintf(stderr, "Failed to open '%s': %s\n",
+ proc_path, strerror(errno));
+ return 1;
+ }
+
+ if (fscanf(f, "%*i %*s %c", &cur_state) != 1) {
+ fclose(f);
+ fprintf(stderr, "Failed to read '%s': %s\n",
+ proc_path, strerror(errno));
+ return 1;
+ }
+ fclose(f);
+
+ if (state == cur_state)
+ return 0;
+
+ usleep(10000);
+ }
+}
diff --git a/src/kernel/tests/lib/tst_res.c b/src/kernel/tests/lib/tst_res.c
new file mode 100644
index 0000000..c35f41b
--- /dev/null
+++ b/src/kernel/tests/lib/tst_res.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ * AUTHOR : Kent Rogers (from Dave Fenner's original)
+ * CO-PILOT : Rich Logan
+ * DATE STARTED : 05/01/90 (rewritten 1/96)
+ * Copyright (c) 2009-2016 Cyril Hrubis <chrubis@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "test.h"
+#include "safe_macros.h"
+#include "usctest.h"
+#include "ltp_priv.h"
+#include "tst_ansi_color.h"
+
+long TEST_RETURN;
+int TEST_ERRNO;
+void *TST_RET_PTR;
+
+#define VERBOSE 1
+#define NOPASS 3
+#define DISCARD 4
+
+#define MAXMESG 80 /* max length of internal messages */
+#define USERMESG 2048 /* max length of user message */
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * EXPAND_VAR_ARGS - Expand the variable portion (arg_fmt) of a result
+ * message into the specified string.
+ *
+ * NOTE (garrcoop): arg_fmt _must_ be the last element in each function
+ * argument list that employs this.
+ */
+#define EXPAND_VAR_ARGS(buf, arg_fmt, buf_len) do {\
+ va_list ap; \
+ assert(arg_fmt != NULL); \
+ va_start(ap, arg_fmt); \
+ vsnprintf(buf, buf_len, arg_fmt, ap); \
+ va_end(ap); \
+ assert(strlen(buf) > 0); \
+} while (0)
+
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+# ifdef __ANDROID__
+# define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \
+ PTHREAD_RECURSIVE_MUTEX_INITIALIZER
+# else
+/* MUSL: http://www.openwall.com/lists/musl/2017/02/20/5 */
+# define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP { {PTHREAD_MUTEX_RECURSIVE} }
+# endif
+#endif
+
+static pthread_mutex_t tmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+static void check_env(void);
+static void tst_condense(int tnum, int ttype, const char *tmesg);
+static void tst_print(const char *tcid, int tnum, int ttype, const char *tmesg);
+
+static int T_exitval = 0; /* exit value used by tst_exit() */
+static int passed_cnt;
+static int T_mode = VERBOSE; /* flag indicating print mode: VERBOSE, */
+ /* NOPASS, DISCARD */
+
+static char Warn_mesg[MAXMESG]; /* holds warning messages */
+
+/*
+ * These are used for condensing output when NOT in verbose mode.
+ */
+static int Buffered = FALSE; /* TRUE if condensed output is currently */
+ /* buffered (i.e. not yet printed) */
+static char *Last_tcid; /* previous test case id */
+static int Last_num; /* previous test case number */
+static int Last_type; /* previous test result type */
+static char *Last_mesg; /* previous test result message */
+
+int tst_count = 0;
+
+/*
+ * These globals must be defined in the test.
+ */
+extern char *TCID; /* Test case identifier from the test source */
+extern int TST_TOTAL; /* Total number of test cases from the test */
+
+
+struct pair {
+ const char *name;
+ int val;
+};
+
+#define PAIR(def) [def] = {.name = #def, .val = def},
+#define STRPAIR(key, value) [key] = {.name = value, .val = key},
+
+#define PAIR_LOOKUP(pair_arr, idx) do { \
+ if (idx < 0 || (size_t)idx >= ARRAY_SIZE(pair_arr) || \
+ pair_arr[idx].name == NULL) \
+ return "???"; \
+ return pair_arr[idx].name; \
+} while (0)
+
+const char *strttype(int ttype)
+{
+ static const struct pair ttype_pairs[] = {
+ PAIR(TPASS)
+ PAIR(TFAIL)
+ PAIR(TBROK)
+ PAIR(TCONF)
+ PAIR(TWARN)
+ PAIR(TINFO)
+ };
+
+ PAIR_LOOKUP(ttype_pairs, TTYPE_RESULT(ttype));
+}
+
+#include "errnos.h"
+#include "signame.h"
+
+static void tst_res__(const char *file, const int lineno, int ttype,
+ const char *arg_fmt, ...)
+{
+ pthread_mutex_lock(&tmutex);
+
+ char tmesg[USERMESG];
+ int len = 0;
+ int ttype_result = TTYPE_RESULT(ttype);
+
+ if (file && (ttype_result != TPASS && ttype_result != TINFO))
+ len = sprintf(tmesg, "%s:%d: ", file, lineno);
+ EXPAND_VAR_ARGS(tmesg + len, arg_fmt, USERMESG - len);
+
+ /*
+ * Save the test result type by ORing ttype into the current exit
+ * value (used by tst_exit()).
+ */
+ T_exitval |= ttype_result;
+
+ if (ttype_result == TPASS)
+ passed_cnt++;
+
+ check_env();
+
+ /*
+ * Set the test case number and print the results, depending on the
+ * display type.
+ */
+ if (ttype_result == TWARN || ttype_result == TINFO) {
+ tst_print(TCID, 0, ttype, tmesg);
+ } else {
+ if (tst_count < 0)
+ tst_print(TCID, 0, TWARN,
+ "tst_res(): tst_count < 0 is not valid");
+
+ /*
+ * Process each display type.
+ */
+ switch (T_mode) {
+ case DISCARD:
+ break;
+ case NOPASS: /* filtered by tst_print() */
+ tst_condense(tst_count + 1, ttype, tmesg);
+ break;
+ default: /* VERBOSE */
+ tst_print(TCID, tst_count + 1, ttype, tmesg);
+ break;
+ }
+
+ tst_count++;
+ }
+
+ pthread_mutex_unlock(&tmutex);
+}
+
+static void tst_condense(int tnum, int ttype, const char *tmesg)
+{
+ int ttype_result = TTYPE_RESULT(ttype);
+
+ /*
+ * If this result is the same as the previous result, return.
+ */
+ if (Buffered == TRUE) {
+ if (strcmp(Last_tcid, TCID) == 0 && Last_type == ttype_result &&
+ strcmp(Last_mesg, tmesg) == 0)
+ return;
+
+ /*
+ * This result is different from the previous result. First,
+ * print the previous result.
+ */
+ tst_print(Last_tcid, Last_num, Last_type, Last_mesg);
+ free(Last_tcid);
+ free(Last_mesg);
+ }
+
+ /*
+ * If a file was specified, print the current result since we have no
+ * way of retaining the file contents for comparing with future
+ * results. Otherwise, buffer the current result info for next time.
+ */
+ Last_tcid = malloc(strlen(TCID) + 1);
+ strcpy(Last_tcid, TCID);
+ Last_num = tnum;
+ Last_type = ttype_result;
+ Last_mesg = malloc(strlen(tmesg) + 1);
+ strcpy(Last_mesg, tmesg);
+ Buffered = TRUE;
+}
+
+void tst_old_flush(void)
+{
+ NO_NEWLIB_ASSERT("Unknown", 0);
+
+ pthread_mutex_lock(&tmutex);
+
+ /*
+ * Print out last line if in NOPASS mode.
+ */
+ if (Buffered == TRUE && T_mode == NOPASS) {
+ tst_print(Last_tcid, Last_num, Last_type, Last_mesg);
+ Buffered = FALSE;
+ }
+
+ fflush(stdout);
+
+ pthread_mutex_unlock(&tmutex);
+}
+
+static void tst_print(const char *tcid, int tnum, int ttype, const char *tmesg)
+{
+ int err = errno;
+ const char *type;
+ int ttype_result = TTYPE_RESULT(ttype);
+ char message[USERMESG];
+ size_t size = 0;
+
+ /*
+ * Save the test result type by ORing ttype into the current exit value
+ * (used by tst_exit()). This is already done in tst_res(), but is
+ * also done here to catch internal warnings. For internal warnings,
+ * tst_print() is called directly with a case of TWARN.
+ */
+ T_exitval |= ttype_result;
+
+ /*
+ * If output mode is DISCARD, or if the output mode is NOPASS and this
+ * result is not one of FAIL, BROK, or WARN, just return. This check
+ * is necessary even though we check for DISCARD mode inside of
+ * tst_res(), since occasionally we get to this point without going
+ * through tst_res() (e.g. internal TWARN messages).
+ */
+ if (T_mode == DISCARD || (T_mode == NOPASS && ttype_result != TFAIL &&
+ ttype_result != TBROK
+ && ttype_result != TWARN))
+ return;
+
+ /*
+ * Build the result line and print it.
+ */
+ type = strttype(ttype);
+
+ if (T_mode == VERBOSE) {
+ size += snprintf(message + size, sizeof(message) - size,
+ "%-8s %4d ", tcid, tnum);
+ } else {
+ size += snprintf(message + size, sizeof(message) - size,
+ "%-8s %4d ", tcid, tnum);
+ }
+
+ if (size >= sizeof(message)) {
+ printf("%s: %i: line too long\n", __func__, __LINE__);
+ abort();
+ }
+
+ if (tst_color_enabled(STDOUT_FILENO))
+ size += snprintf(message + size, sizeof(message) - size,
+ "%s%s%s : %s", tst_ttype2color(ttype), type, ANSI_COLOR_RESET, tmesg);
+ else
+ size += snprintf(message + size, sizeof(message) - size,
+ "%s : %s", type, tmesg);
+
+ if (size >= sizeof(message)) {
+ printf("%s: %i: line too long\n", __func__, __LINE__);
+ abort();
+ }
+
+ if (ttype & TERRNO) {
+ size += snprintf(message + size, sizeof(message) - size,
+ ": errno=%s(%i): %s", tst_strerrno(err),
+ err, strerror(err));
+ }
+
+ if (size >= sizeof(message)) {
+ printf("%s: %i: line too long\n", __func__, __LINE__);
+ abort();
+ }
+
+ if (ttype & TTERRNO) {
+ size += snprintf(message + size, sizeof(message) - size,
+ ": TEST_ERRNO=%s(%i): %s",
+ tst_strerrno(TEST_ERRNO), (int)TEST_ERRNO,
+ strerror(TEST_ERRNO));
+ }
+
+ if (size >= sizeof(message)) {
+ printf("%s: %i: line too long\n", __func__, __LINE__);
+ abort();
+ }
+
+ if (ttype & TRERRNO) {
+ err = TEST_RETURN < 0 ? -(int)TEST_RETURN : (int)TEST_RETURN;
+ size += snprintf(message + size, sizeof(message) - size,
+ ": TEST_RETURN=%s(%i): %s",
+ tst_strerrno(err), err, strerror(err));
+ }
+
+ if (size + 1 >= sizeof(message)) {
+ printf("%s: %i: line too long\n", __func__, __LINE__);
+ abort();
+ }
+
+ message[size] = '\n';
+ message[size + 1] = '\0';
+
+ fputs(message, stdout);
+}
+
+static void check_env(void)
+{
+ static int first_time = 1;
+ char *value;
+
+ if (!first_time)
+ return;
+
+ first_time = 0;
+
+ /* BTOUTPUT not defined, use default */
+ if ((value = getenv(TOUTPUT)) == NULL) {
+ T_mode = VERBOSE;
+ return;
+ }
+
+ if (strcmp(value, TOUT_NOPASS_S) == 0) {
+ T_mode = NOPASS;
+ return;
+ }
+
+ if (strcmp(value, TOUT_DISCARD_S) == 0) {
+ T_mode = DISCARD;
+ return;
+ }
+
+ T_mode = VERBOSE;
+ return;
+}
+
+void tst_exit(void)
+{
+ NO_NEWLIB_ASSERT("Unknown", 0);
+
+ pthread_mutex_lock(&tmutex);
+
+ tst_old_flush();
+
+ T_exitval &= ~TINFO;
+
+ if (T_exitval == TCONF && passed_cnt)
+ T_exitval &= ~TCONF;
+
+ exit(T_exitval);
+}
+
+pid_t tst_fork(void)
+{
+ pid_t child;
+
+ NO_NEWLIB_ASSERT("Unknown", 0);
+
+ tst_old_flush();
+
+ child = fork();
+ if (child == 0)
+ T_exitval = 0;
+
+ return child;
+}
+
+void tst_record_childstatus(void (*cleanup)(void), pid_t child)
+{
+ int status, ttype_result;
+
+ NO_NEWLIB_ASSERT("Unknown", 0);
+
+ SAFE_WAITPID(cleanup, child, &status, 0);
+
+ if (WIFEXITED(status)) {
+ ttype_result = WEXITSTATUS(status);
+ ttype_result = TTYPE_RESULT(ttype_result);
+ T_exitval |= ttype_result;
+
+ if (ttype_result == TPASS)
+ tst_resm(TINFO, "Child process returned TPASS");
+
+ if (ttype_result & TFAIL)
+ tst_resm(TINFO, "Child process returned TFAIL");
+
+ if (ttype_result & TBROK)
+ tst_resm(TINFO, "Child process returned TBROK");
+
+ if (ttype_result & TCONF)
+ tst_resm(TINFO, "Child process returned TCONF");
+
+ } else {
+ tst_brkm(TBROK, cleanup, "child process(%d) killed by "
+ "unexpected signal %s(%d)", child,
+ tst_strsig(WTERMSIG(status)), WTERMSIG(status));
+ }
+}
+
+pid_t tst_vfork(void)
+{
+ NO_NEWLIB_ASSERT("Unknown", 0);
+
+ tst_old_flush();
+ return vfork();
+}
+
+/*
+ * Make tst_brk reentrant so that one can call the SAFE_* macros from within
+ * user-defined cleanup functions.
+ */
+static int tst_brk_entered = 0;
+
+static void tst_brk__(const char *file, const int lineno, int ttype,
+ void (*func)(void), const char *arg_fmt, ...)
+{
+ pthread_mutex_lock(&tmutex);
+
+ char tmesg[USERMESG];
+ int ttype_result = TTYPE_RESULT(ttype);
+
+ EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
+
+ /*
+ * Only FAIL, BROK, CONF, and RETR are supported by tst_brk().
+ */
+ if (ttype_result != TFAIL && ttype_result != TBROK &&
+ ttype_result != TCONF) {
+ sprintf(Warn_mesg, "%s: Invalid Type: %d. Using TBROK",
+ __func__, ttype_result);
+ tst_print(TCID, 0, TWARN, Warn_mesg);
+ /* Keep TERRNO, TTERRNO, etc. */
+ ttype = (ttype & ~ttype_result) | TBROK;
+ }
+
+ tst_res__(file, lineno, ttype, "%s", tmesg);
+ if (tst_brk_entered == 0) {
+ if (ttype_result == TCONF) {
+ tst_res__(file, lineno, ttype,
+ "Remaining cases not appropriate for "
+ "configuration");
+ } else if (ttype_result == TBROK) {
+ tst_res__(file, lineno, TBROK,
+ "Remaining cases broken");
+ }
+ }
+
+ /*
+ * If no cleanup function was specified, just return to the caller.
+ * Otherwise call the specified function.
+ */
+ if (func != NULL) {
+ tst_brk_entered++;
+ (*func) ();
+ tst_brk_entered--;
+ }
+ if (tst_brk_entered == 0)
+ tst_exit();
+
+ pthread_mutex_unlock(&tmutex);
+}
+
+void tst_resm_(const char *file, const int lineno, int ttype,
+ const char *arg_fmt, ...)
+{
+ char tmesg[USERMESG];
+
+ EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
+
+ if (tst_test)
+ tst_res_(file, lineno, ttype, "%s", tmesg);
+ else
+ tst_res__(file, lineno, ttype, "%s", tmesg);
+}
+
+typedef void (*tst_res_func_t)(const char *file, const int lineno,
+ int ttype, const char *fmt, ...);
+
+void tst_resm_hexd_(const char *file, const int lineno, int ttype,
+ const void *buf, size_t size, const char *arg_fmt, ...)
+{
+ char tmesg[USERMESG];
+ static const size_t symb_num = 2; /* xx */
+ static const size_t size_max = 16;
+ size_t offset;
+ size_t i;
+ char *pmesg = tmesg;
+ tst_res_func_t res_func;
+
+ if (tst_test)
+ res_func = tst_res_;
+ else
+ res_func = tst_res__;
+
+ EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
+ offset = strlen(tmesg);
+
+ if (size > size_max || size == 0 ||
+ (offset + size * (symb_num + 1)) >= USERMESG)
+ res_func(file, lineno, ttype, "%s", tmesg);
+ else
+ pmesg += offset;
+
+ for (i = 0; i < size; ++i) {
+ /* add space before byte except first one */
+ if (pmesg != tmesg)
+ *(pmesg++) = ' ';
+
+ sprintf(pmesg, "%02x", ((unsigned char *)buf)[i]);
+ pmesg += symb_num;
+ if ((i + 1) % size_max == 0 || i + 1 == size) {
+ res_func(file, lineno, ttype, "%s", tmesg);
+ pmesg = tmesg;
+ }
+ }
+}
+
+void tst_brkm_(const char *file, const int lineno, int ttype,
+ void (*func)(void), const char *arg_fmt, ...)
+{
+ char tmesg[USERMESG];
+
+ EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
+
+ if (tst_test) {
+ if (func) {
+ tst_brk_(file, lineno, TBROK,
+ "Non-NULL cleanup in newlib!");
+ }
+
+ tst_brk_(file, lineno, ttype, "%s", tmesg);
+ } else {
+ tst_brk__(file, lineno, ttype, func, "%s", tmesg);
+ }
+
+ /* Shouldn't be reached, but fixes build time warnings about noreturn. */
+ abort();
+}
+
+void tst_require_root(void)
+{
+ NO_NEWLIB_ASSERT("Unknown", 0);
+
+ if (geteuid() != 0)
+ tst_brkm(TCONF, NULL, "Test needs to be run as root");
+}
diff --git a/src/kernel/tests/lib/tst_resource.c b/src/kernel/tests/lib/tst_resource.c
new file mode 100644
index 0000000..0b9b381
--- /dev/null
+++ b/src/kernel/tests/lib/tst_resource.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 Cyril Hrubis chrubis@suse.cz
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <pthread.h>
+#include "test.h"
+#include "old_resource.h"
+#include "ltp_priv.h"
+
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX 1024
+#endif
+#endif
+
+static pthread_mutex_t tmutex = PTHREAD_MUTEX_INITIALIZER;
+static char dataroot[PATH_MAX];
+extern char *TCID;
+
+static void tst_dataroot_init(void)
+{
+ const char *ltproot = getenv("LTPROOT");
+ char curdir[PATH_MAX];
+ const char *startdir;
+ int ret;
+
+ /* 1. if LTPROOT is set, use $LTPROOT/testcases/data/$TCID
+ * 2. else if startwd is set by tst_tmpdir(), use $STARWD/datafiles
+ * 3. else use $CWD/datafiles */
+ if (ltproot) {
+ ret = snprintf(dataroot, PATH_MAX, "%s/testcases/data/%s",
+ ltproot, TCID);
+ } else {
+ startdir = tst_get_startwd();
+ if (startdir[0] == 0) {
+ if (getcwd(curdir, PATH_MAX) == NULL) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "tst_dataroot getcwd");
+ return;
+ }
+ startdir = curdir;
+ }
+ ret = snprintf(dataroot, PATH_MAX, "%s/datafiles", startdir);
+ }
+
+ if (ret < 0 || ret >= PATH_MAX)
+ tst_brkm(TBROK, NULL, "tst_dataroot snprintf: %d", ret);
+}
+
+const char *tst_dataroot(void)
+{
+ if (dataroot[0] == 0) {
+ pthread_mutex_lock(&tmutex);
+ if (dataroot[0] == 0)
+ tst_dataroot_init();
+ pthread_mutex_unlock(&tmutex);
+ }
+ return dataroot;
+}
+
+static int file_copy(const char *file, const int lineno,
+ void (*cleanup_fn)(void), const char *path,
+ const char *filename, const char *dest)
+{
+ size_t len = strlen(path) + strlen(filename) + 2;
+ char buf[len];
+
+ snprintf(buf, sizeof(buf), "%s/%s", path, filename);
+
+ /* check if file exists */
+ if (access(buf, R_OK))
+ return 0;
+
+ safe_cp(file, lineno, cleanup_fn, buf, dest);
+
+ return 1;
+}
+
+void tst_resource_copy(const char *file, const int lineno,
+ void (*cleanup_fn)(void),
+ const char *filename, const char *dest)
+{
+ if (!tst_tmpdir_created()) {
+ tst_brkm(TBROK, cleanup_fn,
+ "Temporary directory doesn't exist at %s:%d",
+ file, lineno);
+ return;
+ }
+
+ if (dest == NULL)
+ dest = ".";
+
+ const char *ltproot = getenv("LTPROOT");
+ const char *dataroot = tst_dataroot();
+
+ /* look for data files in $LTP_DATAROOT, $LTPROOT/testcases/bin
+ * and $CWD */
+ if (file_copy(file, lineno, cleanup_fn, dataroot, filename, dest))
+ return;
+
+ if (ltproot != NULL) {
+ char buf[strlen(ltproot) + 64];
+
+ snprintf(buf, sizeof(buf), "%s/testcases/bin", ltproot);
+
+ if (file_copy(file, lineno, cleanup_fn, buf, filename, dest))
+ return;
+ }
+
+ /* try directory test started in as last resort */
+ const char *startwd = tst_get_startwd();
+ if (file_copy(file, lineno, cleanup_fn, startwd, filename, dest))
+ return;
+
+ tst_brkm(TBROK, cleanup_fn, "Failed to copy resource '%s' at %s:%d",
+ filename, file, lineno);
+}
diff --git a/src/kernel/tests/lib/tst_safe_macros.c b/src/kernel/tests/lib/tst_safe_macros.c
new file mode 100644
index 0000000..25c37df
--- /dev/null
+++ b/src/kernel/tests/lib/tst_safe_macros.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <errno.h>
+#include <sched.h>
+#include <sys/ptrace.h>
+#include "config.h"
+#ifdef HAVE_SYS_FANOTIFY_H
+# include <sys/fanotify.h>
+#endif
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "lapi/setns.h"
+#include "tst_safe_macros.h"
+#include "lapi/personality.h"
+
+int safe_setpgid(const char *file, const int lineno, pid_t pid, pid_t pgid)
+{
+ int rval;
+
+ rval = setpgid(pid, pgid);
+ if (rval) {
+ tst_brk(TBROK | TERRNO,
+ "%s:%d: setpgid(%i, %i) failed",
+ file, lineno, pid, pgid);
+ }
+
+ return rval;
+}
+
+pid_t safe_getpgid(const char *file, const int lineno, pid_t pid)
+{
+ pid_t pgid;
+
+ pgid = getpgid(pid);
+ if (pgid == -1) {
+ tst_brk(TBROK | TERRNO,
+ "%s:%d: getpgid(%i) failed", file, lineno, pid);
+ }
+
+ return pgid;
+}
+
+int safe_fanotify_init(const char *file, const int lineno,
+ unsigned int flags, unsigned int event_f_flags)
+{
+ int rval;
+
+#ifdef HAVE_SYS_FANOTIFY_H
+ rval = fanotify_init(flags, event_f_flags);
+
+ if (rval == -1) {
+ if (errno == ENOSYS) {
+ tst_brk(TCONF,
+ "fanotify is not configured in this kernel.");
+ }
+ tst_brk(TBROK | TERRNO,
+ "%s:%d: fanotify_init() failed", file, lineno);
+ }
+#else
+ tst_brk(TCONF, "Header <sys/fanotify.h> is not present");
+#endif /* HAVE_SYS_FANOTIFY_H */
+
+ return rval;
+}
+
+int safe_personality(const char *filename, unsigned int lineno,
+ unsigned long persona)
+{
+ int prev_persona = personality(persona);
+
+ if (prev_persona < 0) {
+ tst_brk_(filename, lineno, TBROK | TERRNO,
+ "persona(%ld) failed", persona);
+ }
+
+ return prev_persona;
+}
+
+int safe_setregid(const char *file, const int lineno,
+ gid_t rgid, gid_t egid)
+{
+ int rval;
+
+ rval = setregid(rgid, egid);
+ if (rval == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "setregid(%li, %li) failed",
+ (long)rgid, (long)egid);
+ }
+
+ return rval;
+}
+
+
+int safe_setreuid(const char *file, const int lineno,
+ uid_t ruid, uid_t euid)
+{
+ int rval;
+
+ rval = setreuid(ruid, euid);
+ if (rval == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "setreuid(%li, %li) failed",
+ (long)ruid, (long)euid);
+ }
+
+ return rval;
+}
+
+
+int safe_sigaction(const char *file, const int lineno,
+ int signum, const struct sigaction *act,
+ struct sigaction *oldact)
+{
+ int rval;
+
+ rval = sigaction(signum, act, oldact);
+
+ if (rval == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "sigaction(%s (%d), %p, %p) failed",
+ tst_strsig(signum), signum, act, oldact);
+ }
+
+ return rval;
+}
+
+void safe_sigaddset(const char *file, const int lineno,
+ sigset_t *sigs, int signo)
+{
+ int rval;
+
+ rval = sigaddset(sigs, signo);
+ if (rval == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "sigaddset() %s (%i) failed",
+ tst_strsig(signo), signo);
+ }
+}
+
+void safe_sigdelset(const char *file, const int lineno,
+ sigset_t *sigs, int signo)
+{
+ int rval;
+
+ rval = sigdelset(sigs, signo);
+ if (rval == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "sigdelset() %s (%i) failed",
+ tst_strsig(signo), signo);
+ }
+}
+
+void safe_sigemptyset(const char *file, const int lineno,
+ sigset_t *sigs)
+{
+ int rval;
+
+ rval = sigemptyset(sigs);
+ if (rval == -1)
+ tst_brk_(file, lineno, TBROK | TERRNO, "sigemptyset() failed");
+}
+
+void safe_sigfillset(const char *file, const int lineno,
+ sigset_t *sigs)
+{
+ int rval;
+
+ rval = sigfillset(sigs);
+ if (rval == -1)
+ tst_brk_(file, lineno, TBROK | TERRNO, "sigfillset() failed");
+}
+
+static const char *strhow(int how)
+{
+ switch (how) {
+ case SIG_BLOCK:
+ return "SIG_BLOCK";
+ case SIG_UNBLOCK:
+ return "SIG_UNBLOCK";
+ case SIG_SETMASK:
+ return "SIG_SETMASK";
+ default:
+ return "???";
+ }
+}
+
+void safe_sigprocmask(const char *file, const int lineno,
+ int how, sigset_t *set, sigset_t *oldset)
+{
+ int rval;
+
+ rval = sigprocmask(how, set, oldset);
+ if (rval == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "sigprocmask(%s, %p, %p)", strhow(how), set, oldset);
+ }
+}
+
+void safe_sigwait(const char *file, const int lineno,
+ sigset_t *set, int *sig)
+{
+ int rval;
+
+ rval = sigwait(set, sig);
+ if (rval != 0) {
+ errno = rval;
+ tst_brk_(file, lineno, TBROK, "sigwait(%p, %p)", set, sig);
+ }
+}
+
+struct group *safe_getgrnam(const char *file, const int lineno,
+ const char *name)
+{
+ struct group *rval;
+
+ errno = 0;
+ rval = getgrnam(name);
+ if (rval == NULL) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "getgrnam(%s) failed", name);
+ }
+
+ return rval;
+}
+
+struct group *safe_getgrnam_fallback(const char *file, const int lineno,
+ const char *name, const char *fallback)
+{
+ struct group *rval;
+
+ errno = 0;
+ rval = getgrnam(name);
+ if (rval == NULL) {
+ tst_res_(file, lineno, TINFO,
+ "getgrnam(%s) failed - try fallback %s",
+ name, fallback);
+ rval = safe_getgrnam(file, lineno, fallback);
+ }
+
+ return rval;
+}
+
+struct group *safe_getgrgid(const char *file, const int lineno, gid_t gid)
+{
+ struct group *rval;
+
+ errno = 0;
+ rval = getgrgid(gid);
+ if (rval == NULL) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "getgrgid(%li) failed", (long)gid);
+ }
+
+ return rval;
+}
+
+int safe_chroot(const char *file, const int lineno, const char *path)
+{
+ int rval;
+
+ rval = chroot(path);
+ if (rval == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "chroot(%s) failed", path);
+ }
+
+ return rval;
+}
+
+void safe_unshare(const char *file, const int lineno, int flags)
+{
+ int res;
+
+ res = unshare(flags);
+ if (res == -1) {
+ if (errno == EINVAL) {
+ tst_brk_(file, lineno, TCONF | TERRNO,
+ "unshare(%d) unsupported", flags);
+ } else {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "unshare(%d) failed", flags);
+ }
+ }
+}
+
+void safe_setns(const char *file, const int lineno, int fd, int nstype)
+{
+ int ret;
+
+ ret = setns(fd, nstype);
+ if (ret == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO, "setns(%i, %i) failed",
+ fd, nstype);
+ }
+}
+
+long tst_safe_ptrace(const char *file, const int lineno, int req, pid_t pid,
+ void *addr, void *data)
+{
+ long ret;
+
+ errno = 0;
+ ret = ptrace(req, pid, addr, data);
+
+ if (ret == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO, "ptrace() failed");
+ } else if (ret) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "Invalid ptrace() return value %ld", ret);
+ }
+
+ return ret;
+}
+
+int safe_pipe2(const char *file, const int lineno, int fildes[2], int flags)
+{
+ int ret;
+
+ ret = pipe2(fildes, flags);
+ if (ret == -1) {
+ tst_brk_(file, lineno, TBROK | TERRNO,
+ "pipe2({%d,%d}) failed with flag(%d)",
+ fildes[0], fildes[1], flags);
+ }
+
+ return ret;
+}
diff --git a/src/kernel/tests/lib/tst_safe_sysv_ipc.c b/src/kernel/tests/lib/tst_safe_sysv_ipc.c
new file mode 100644
index 0000000..30b5f6e
--- /dev/null
+++ b/src/kernel/tests/lib/tst_safe_sysv_ipc.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Xiao yang <yangx.jy@cn.fujitsu.com>
+ */
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_safe_sysv_ipc.h"
+
+/*
+ * The IPC_STAT, IPC_SET and IPC_RMID can return either 0 or -1.
+ *
+ * Linux specific cmds either returns -1 on failure or positive integer
+ * either index into an kernel array or shared primitive indentifier.
+ */
+static int ret_check(int cmd, int ret)
+{
+ switch (cmd) {
+ case IPC_STAT:
+ case IPC_SET:
+ case IPC_RMID:
+ return ret != 0;
+ default:
+ return ret == -1;
+ }
+}
+
+int safe_msgget(const char *file, const int lineno, key_t key, int msgflg)
+{
+ int rval;
+
+ rval = msgget(key, msgflg);
+ if (rval == -1) {
+ tst_brk(TBROK | TERRNO, "%s:%d: msgget(%i, %x) failed",
+ file, lineno, (int)key, msgflg);
+ }
+
+ return rval;
+}
+
+int safe_msgsnd(const char *file, const int lineno, int msqid, const void *msgp,
+ size_t msgsz, int msgflg)
+{
+ int rval;
+
+ rval = msgsnd(msqid, msgp, msgsz, msgflg);
+ if (rval == -1) {
+ tst_brk(TBROK | TERRNO,
+ "%s:%d: msgsnd(%i, %p, %zu, %x) failed",
+ file, lineno, msqid, msgp, msgsz, msgflg);
+ }
+
+ return rval;
+}
+
+ssize_t safe_msgrcv(const char *file, const int lineno, int msqid, void *msgp,
+ size_t msgsz, long msgtyp, int msgflg)
+{
+ ssize_t rval;
+
+ rval = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
+ if (rval == -1) {
+ tst_brk(TBROK | TERRNO,
+ "%s:%d: msgrcv(%i, %p, %zu, %li, %x) failed",
+ file, lineno, msqid, msgp, msgsz, msgtyp, msgflg);
+ }
+
+ return rval;
+}
+
+int safe_msgctl(const char *file, const int lineno, int msqid, int cmd,
+ struct msqid_ds *buf)
+{
+ int rval;
+
+ rval = msgctl(msqid, cmd, buf);
+ if (ret_check(cmd, rval)) {
+ tst_brk(TBROK | TERRNO,
+ "%s:%d: msgctl(%i, %i, %p) = %i failed",
+ file, lineno, msqid, cmd, buf, rval);
+ }
+
+
+ return rval;
+}
+
+int safe_shmget(const char *file, const int lineno, key_t key, size_t size,
+ int shmflg)
+{
+ int rval;
+
+ rval = shmget(key, size, shmflg);
+ if (rval == -1) {
+ tst_brk(TBROK | TERRNO, "%s:%d: shmget(%i, %zu, %x) failed",
+ file, lineno, (int)key, size, shmflg);
+ }
+
+ return rval;
+}
+
+void *safe_shmat(const char *file, const int lineno, int shmid,
+ const void *shmaddr, int shmflg)
+{
+ void *rval;
+
+ rval = shmat(shmid, shmaddr, shmflg);
+ if (rval == (void *)-1) {
+ tst_brk(TBROK | TERRNO, "%s:%d: shmat(%i, %p, %x) failed",
+ file, lineno, shmid, shmaddr, shmflg);
+ }
+
+ return rval;
+}
+
+int safe_shmdt(const char *file, const int lineno, const void *shmaddr)
+{
+ int rval;
+
+ rval = shmdt(shmaddr);
+ if (rval == -1) {
+ tst_brk(TBROK | TERRNO, "%s:%d: shmdt(%p) failed",
+ file, lineno, shmaddr);
+ }
+
+ return rval;
+}
+
+int safe_shmctl(const char *file, const int lineno, int shmid, int cmd,
+ struct shmid_ds *buf)
+{
+ int rval;
+
+ rval = shmctl(shmid, cmd, buf);
+ if (ret_check(cmd, rval)) {
+ tst_brk(TBROK | TERRNO,
+ "%s:%d: shmctl(%i, %i, %p) = %i failed",
+ file, lineno, shmid, cmd, buf, rval);
+ }
+
+ return rval;
+}
diff --git a/src/kernel/tests/lib/tst_safe_timerfd.c b/src/kernel/tests/lib/tst_safe_timerfd.c
new file mode 100644
index 0000000..ffe7b2e
--- /dev/null
+++ b/src/kernel/tests/lib/tst_safe_timerfd.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
+ */
+
+#include "tst_safe_timerfd.h"
+#include "lapi/timerfd.h"
+#include "tst_clocks.h"
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+
+#define TTYPE (errno == ENOTSUP ? TCONF : TBROK)
+
+int safe_timerfd_create(const char *file, const int lineno,
+ int clockid, int flags)
+{
+ int fd;
+
+ fd = timerfd_create(clockid, flags);
+ if (fd < 0) {
+ tst_brk(TTYPE | TERRNO, "%s:%d timerfd_create(%s) failed",
+ file, lineno, tst_clock_name(clockid));
+ }
+
+ return fd;
+}
+
+int safe_timerfd_gettime(const char *file, const int lineno,
+ int fd, struct itimerspec *curr_value)
+{
+ int rval;
+
+ rval = timerfd_gettime(fd, curr_value);
+ if (rval != 0) {
+ tst_brk(TTYPE | TERRNO, "%s:%d timerfd_gettime() failed",
+ file, lineno);
+ }
+
+ return rval;
+}
+
+int safe_timerfd_settime(const char *file, const int lineno,
+ int fd, int flags,
+ const struct itimerspec *new_value,
+ struct itimerspec *old_value)
+{
+ int rval;
+
+ rval = timerfd_settime(fd, flags, new_value, old_value);
+ if (rval != 0) {
+ tst_brk(TTYPE | TERRNO, "%s:%d timerfd_settime() failed",
+ file, lineno);
+ }
+
+ return rval;
+}
diff --git a/src/kernel/tests/lib/tst_sig.c b/src/kernel/tests/lib/tst_sig.c
new file mode 100644
index 0000000..6d77aea
--- /dev/null
+++ b/src/kernel/tests/lib/tst_sig.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+
+/* $Id: tst_sig.c,v 1.13 2009/08/28 09:29:01 vapier Exp $ */
+
+/*****************************************************************************
+ OS Testing - Silicon Graphics, Inc.
+
+ FUNCTION IDENTIFIER : tst_sig Set up for unexpected signals.
+
+ AUTHOR : David D. Fenner
+
+ CO-PILOT : Bill Roske
+
+ DATE STARTED : 06/06/90
+
+ This module may be linked with c-modules requiring unexpected
+ signal handling. The parameters to tst_sig are as follows:
+
+ fork_flag - set to FORK or NOFORK depending upon whether the
+ calling program executes a fork() system call. It
+ is normally the case that the calling program treats
+ SIGCHLD as an expected signal if fork() is being used.
+
+ handler - a pointer to the unexpected signal handler to
+ be executed after an unexpected signal has been
+ detected. If handler is set to DEF_HANDLER, a
+ default handler is used. This routine should be
+ declared as function returning an int.
+
+ cleanup - a pointer to a cleanup routine to be executed
+ by the unexpected signal handler before tst_exit is
+ called. This parameter is set to NULL if no cleanup
+ routine is required. An external variable, T_cleanup
+ is set so that other user-defined handlers have
+ access to the cleanup routine. This routine should be
+ declared as returning type void.
+
+***************************************************************************/
+
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include "test.h"
+#include "lapi/signal.h"
+
+#define MAXMESG 150 /* size of mesg string sent to tst_res */
+
+static void (*T_cleanup) ();
+
+static void def_handler(); /* default signal handler */
+static void (*tst_setup_signal(int, void (*)(int))) (int);
+
+/****************************************************************************
+ * tst_sig() : set-up to catch unexpected signals. fork_flag is set to NOFORK
+ * if SIGCHLD is to be an "unexpected signal", otherwise it is set to
+ * FORK. cleanup points to a cleanup routine to be executed before
+ * tst_exit is called (cleanup is set to NULL if no cleanup is desired).
+ * handler is a pointer to the signal handling routine (if handler is
+ * set to NULL, a default handler is used).
+ ***************************************************************************/
+
+void tst_sig(int fork_flag, void (*handler) (), void (*cleanup) ())
+{
+ int sig;
+#ifdef _SC_SIGRT_MIN
+ long sigrtmin, sigrtmax;
+#endif
+
+ /*
+ * save T_cleanup and handler function pointers
+ */
+ T_cleanup = cleanup; /* used by default handler */
+
+ if (handler == DEF_HANDLER) {
+ /* use default handler */
+ handler = def_handler;
+ }
+#ifdef _SC_SIGRT_MIN
+ sigrtmin = sysconf(_SC_SIGRT_MIN);
+ sigrtmax = sysconf(_SC_SIGRT_MAX);
+#endif
+
+ /*
+ * now loop through all signals and set the handlers
+ */
+
+ for (sig = 1; sig < NSIG; sig++) {
+ /*
+ * SIGKILL is never unexpected.
+ * SIGCHLD is only unexpected when
+ * no forking is being done.
+ * SIGINFO is used for file quotas and should be expected
+ */
+
+#ifdef _SC_SIGRT_MIN
+ if (sig >= sigrtmin && sig <= sigrtmax)
+ continue;
+#endif
+
+ switch (sig) {
+ case SIGKILL:
+ case SIGSTOP:
+ case SIGCONT:
+#if !defined(_SC_SIGRT_MIN) && defined(__SIGRTMIN) && defined(__SIGRTMAX)
+ /* Ignore all real-time signals */
+ case __SIGRTMIN:
+ case __SIGRTMIN + 1:
+ case __SIGRTMIN + 2:
+ case __SIGRTMIN + 3:
+ case __SIGRTMIN + 4:
+ case __SIGRTMIN + 5:
+ case __SIGRTMIN + 6:
+ case __SIGRTMIN + 7:
+ case __SIGRTMIN + 8:
+ case __SIGRTMIN + 9:
+ case __SIGRTMIN + 10:
+ case __SIGRTMIN + 11:
+ case __SIGRTMIN + 12:
+ case __SIGRTMIN + 13:
+ case __SIGRTMIN + 14:
+ case __SIGRTMIN + 15:
+/* __SIGRTMIN is 37 on HPPA rather than 32 *
+ * as on i386, etc. */
+#if !defined(__hppa__)
+ case __SIGRTMAX - 15:
+ case __SIGRTMAX - 14:
+ case __SIGRTMAX - 13:
+ case __SIGRTMAX - 12:
+ case __SIGRTMAX - 11:
+#endif
+ case __SIGRTMAX - 10:
+ case __SIGRTMAX - 9:
+ case __SIGRTMAX - 8:
+ case __SIGRTMAX - 7:
+ case __SIGRTMAX - 6:
+ case __SIGRTMAX - 5:
+ case __SIGRTMAX - 4:
+ case __SIGRTMAX - 3:
+ case __SIGRTMAX - 2:
+ case __SIGRTMAX - 1:
+ case __SIGRTMAX:
+#endif
+#ifdef SIGSWAP
+ case SIGSWAP:
+#endif /* SIGSWAP */
+
+#ifdef SIGCKPT
+ case SIGCKPT:
+#endif
+#ifdef SIGRESTART
+ case SIGRESTART:
+#endif
+ /*
+ * pthread-private signals SIGPTINTR and SIGPTRESCHED.
+ * Setting a handler for these signals is disallowed when
+ * the binary is linked against libpthread.
+ */
+#ifdef SIGPTINTR
+ case SIGPTINTR:
+#endif /* SIGPTINTR */
+#ifdef SIGPTRESCHED
+ case SIGPTRESCHED:
+#endif /* SIGPTRESCHED */
+#ifdef _SIGRESERVE
+ case _SIGRESERVE:
+#endif
+#ifdef _SIGDIL
+ case _SIGDIL:
+#endif
+#ifdef _SIGCANCEL
+ case _SIGCANCEL:
+#endif
+#ifdef _SIGGFAULT
+ case _SIGGFAULT:
+#endif
+ break;
+
+ case SIGCHLD:
+ if (fork_flag == FORK)
+ continue;
+
+ default:
+ if (tst_setup_signal(sig, handler) == SIG_ERR)
+ tst_resm(TWARN | TERRNO,
+ "signal failed for signal %d", sig);
+ break;
+ }
+ }
+}
+
+/****************************************************************************
+ * def_handler() : default signal handler that is invoked when
+ * an unexpected signal is caught.
+ ***************************************************************************/
+
+static void def_handler(int sig)
+{
+ /*
+ * Break remaining test cases, do any cleanup, then exit
+ */
+ tst_brkm(TBROK, T_cleanup,
+ "unexpected signal %s(%d) received (pid = %d).",
+ tst_strsig(sig), sig, getpid());
+}
+
+/*
+ * tst_setup_signal - A function like signal(), but we have
+ * control over its personality.
+ */
+static void (*tst_setup_signal(int sig, void (*handler) (int))) (int) {
+ struct sigaction my_act, old_act;
+ int ret;
+
+ my_act.sa_handler = handler;
+ my_act.sa_flags = SA_RESTART;
+ sigemptyset(&my_act.sa_mask);
+
+ ret = sigaction(sig, &my_act, &old_act);
+
+ if (ret == 0)
+ return old_act.sa_handler;
+ else
+ return SIG_ERR;
+}
diff --git a/src/kernel/tests/lib/tst_sig_proc.c b/src/kernel/tests/lib/tst_sig_proc.c
new file mode 100644
index 0000000..509418a
--- /dev/null
+++ b/src/kernel/tests/lib/tst_sig_proc.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2016 Linux Test Project
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "tst_sig_proc.h"
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+
+pid_t create_sig_proc(int sig, int count, unsigned int usec)
+{
+ pid_t pid, cpid;
+
+ pid = getpid();
+ cpid = SAFE_FORK();
+
+ if (cpid == 0) {
+ while (count-- > 0) {
+ usleep(usec);
+ if (kill(pid, sig) == -1)
+ break;
+ }
+ exit(0);
+ }
+
+ return cpid;
+}
diff --git a/src/kernel/tests/lib/tst_status.c b/src/kernel/tests/lib/tst_status.c
new file mode 100644
index 0000000..f1affea
--- /dev/null
+++ b/src/kernel/tests/lib/tst_status.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+
+static char buf[32];
+
+const char *exited(int status)
+{
+ snprintf(buf, sizeof(buf), "exited with %i", WEXITSTATUS(status));
+
+ return buf;
+}
+
+const char *signaled(int status)
+{
+ snprintf(buf, sizeof(buf), "killed by %s", tst_strsig(status));
+
+ return buf;
+}
+
+const char *invalid(int status)
+{
+ snprintf(buf, sizeof(buf), "invalid status 0x%x", status);
+
+ return buf;
+}
+
+const char *tst_strstatus(int status)
+{
+ if (WIFEXITED(status))
+ return exited(status);
+
+ if (WIFSIGNALED(status))
+ return signaled(status);
+
+ if (WIFSTOPPED(status))
+ return "is stopped";
+
+ if (WIFCONTINUED(status))
+ return "is resumed";
+
+ return invalid(status);
+}
diff --git a/src/kernel/tests/lib/tst_supported_fs_types.c b/src/kernel/tests/lib/tst_supported_fs_types.c
new file mode 100644
index 0000000..00ede54
--- /dev/null
+++ b/src/kernel/tests/lib/tst_supported_fs_types.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_fs.h"
+
+static const char *const fs_type_whitelist[] = {
+ "ext2",
+ "ext3",
+ "ext4",
+ "xfs",
+ "btrfs",
+ "vfat",
+ "exfat",
+ "ntfs",
+ NULL
+};
+
+static const char *fs_types[ARRAY_SIZE(fs_type_whitelist)];
+
+static int has_mkfs(const char *fs_type)
+{
+ char buf[128];
+ int ret;
+
+ sprintf(buf, "mkfs.%s >/dev/null 2>&1", fs_type);
+
+ ret = tst_system(buf);
+
+ if (WEXITSTATUS(ret) == 127) {
+ tst_res(TINFO, "mkfs.%s does not exist", fs_type);
+ return 0;
+ }
+
+ tst_res(TINFO, "mkfs.%s does exist", fs_type);
+ return 1;
+}
+
+static int has_kernel_support(const char *fs_type, int flags)
+{
+ static int fuse_supported = -1;
+ const char *tmpdir = getenv("TMPDIR");
+ char buf[128];
+ int ret;
+
+ if (!tmpdir)
+ tmpdir = "/tmp";
+
+ mount("/dev/zero", tmpdir, fs_type, 0, NULL);
+ if (errno != ENODEV) {
+ tst_res(TINFO, "Kernel supports %s", fs_type);
+ return 1;
+ }
+
+ /* Is FUSE supported by kernel? */
+ if (fuse_supported == -1) {
+ ret = open("/dev/fuse", O_RDWR);
+ if (ret < 0) {
+ fuse_supported = 0;
+ } else {
+ fuse_supported = 1;
+ SAFE_CLOSE(ret);
+ }
+ }
+
+ if (!fuse_supported)
+ return 0;
+
+ /* Is FUSE implementation installed? */
+ sprintf(buf, "mount.%s >/dev/null 2>&1", fs_type);
+
+ ret = tst_system(buf);
+ if (WEXITSTATUS(ret) == 127) {
+ tst_res(TINFO, "Filesystem %s is not supported", fs_type);
+ return 0;
+ }
+
+ if (flags & TST_FS_SKIP_FUSE) {
+ tst_res(TINFO, "Skipping FUSE as requested by the test");
+ return 0;
+ }
+
+ tst_res(TINFO, "FUSE does support %s", fs_type);
+ return 1;
+}
+
+int tst_fs_is_supported(const char *fs_type, int flags)
+{
+ return has_kernel_support(fs_type, flags) && has_mkfs(fs_type);
+}
+
+const char **tst_get_supported_fs_types(int flags)
+{
+ unsigned int i, j = 0;
+
+ for (i = 0; fs_type_whitelist[i]; i++) {
+ if (tst_fs_is_supported(fs_type_whitelist[i], flags))
+ fs_types[j++] = fs_type_whitelist[i];
+ }
+
+ return fs_types;
+}
diff --git a/src/kernel/tests/lib/tst_sys_conf.c b/src/kernel/tests/lib/tst_sys_conf.c
new file mode 100644
index 0000000..4ad9f8b
--- /dev/null
+++ b/src/kernel/tests/lib/tst_sys_conf.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018 Jan Stancek <jstancek@redhat.com>
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_sys_conf.h"
+
+static struct tst_sys_conf *save_restore_data;
+
+void tst_sys_conf_dump(void)
+{
+ struct tst_sys_conf *i;
+
+ for (i = save_restore_data; i; i = i->next)
+ tst_res(TINFO, "%s = %s", i->path, i->value);
+}
+
+int tst_sys_conf_save_str(const char *path, const char *value)
+{
+ struct tst_sys_conf *n = SAFE_MALLOC(sizeof(*n));
+
+ strncpy(n->path, path, sizeof(n->path)-1);
+ strncpy(n->value, value, sizeof(n->value)-1);
+
+ n->path[sizeof(n->path) - 1] = 0;
+ n->value[sizeof(n->value) - 1] = 0;
+
+ n->next = save_restore_data;
+ save_restore_data = n;
+
+ return 0;
+}
+
+int tst_sys_conf_save(const char *path)
+{
+ char line[PATH_MAX];
+ FILE *fp;
+ void *ret;
+ char flag;
+
+ if (!path)
+ tst_brk(TBROK, "path is empty");
+
+ flag = path[0];
+ if (flag == '?' || flag == '!')
+ path++;
+
+ if (access(path, F_OK) != 0) {
+ switch (flag) {
+ case '?':
+ tst_res(TINFO, "Path not found: '%s'", path);
+ break;
+ case '!':
+ tst_brk(TBROK|TERRNO, "Path not found: '%s'", path);
+ break;
+ default:
+ tst_brk(TCONF|TERRNO, "Path not found: '%s'", path);
+ }
+ return 1;
+ }
+
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ if (flag == '?')
+ return 1;
+
+ tst_brk(TBROK | TERRNO, "Failed to open FILE '%s' for reading",
+ path);
+ return 1;
+ }
+
+ ret = fgets(line, sizeof(line), fp);
+ fclose(fp);
+
+ if (ret == NULL) {
+ if (flag == '?')
+ return 1;
+
+ tst_brk(TBROK | TERRNO, "Failed to read anything from '%s'",
+ path);
+ }
+
+ return tst_sys_conf_save_str(path, line);
+}
+
+void tst_sys_conf_restore(int verbose)
+{
+ struct tst_sys_conf *i;
+
+ for (i = save_restore_data; i; i = i->next) {
+ if (verbose) {
+ tst_res(TINFO, "Restoring conf.: %s -> %s\n",
+ i->path, i->value);
+ }
+ FILE_PRINTF(i->path, "%s", i->value);
+ }
+}
+
diff --git a/src/kernel/tests/lib/tst_taint.c b/src/kernel/tests/lib/tst_taint.c
new file mode 100644
index 0000000..49146aa
--- /dev/null
+++ b/src/kernel/tests/lib/tst_taint.c
@@ -0,0 +1,107 @@
+#define TST_NO_DEFAULT_MAIN
+
+#include "tst_test.h"
+#include "tst_taint.h"
+#include "tst_safe_stdio.h"
+
+#define TAINT_FILE "/proc/sys/kernel/tainted"
+
+static unsigned int taint_mask = -1;
+
+static unsigned int tst_taint_read(void)
+{
+ unsigned int val;
+
+ SAFE_FILE_SCANF(TAINT_FILE, "%u", &val);
+
+ return val;
+}
+
+static int tst_taint_check_kver(unsigned int mask)
+{
+ int r1;
+ int r2;
+ int r3 = 0;
+
+ if (mask & TST_TAINT_X) {
+ r1 = 4;
+ r2 = 15;
+ } else if (mask & TST_TAINT_K) {
+ r1 = 4;
+ r2 = 0;
+ } else if (mask & TST_TAINT_L) {
+ r1 = 3;
+ r2 = 17;
+ } else if (mask & TST_TAINT_E) {
+ r1 = 3;
+ r2 = 15;
+ } else if (mask & TST_TAINT_O) {
+ r1 = 3;
+ r2 = 2;
+ } else if (mask & TST_TAINT_I) {
+ r1 = 2;
+ r2 = 6;
+ r3 = 35;
+ } else if (mask & TST_TAINT_C) {
+ r1 = 2;
+ r2 = 6;
+ r3 = 28;
+ } else if (mask & TST_TAINT_W) {
+ r1 = 2;
+ r2 = 6;
+ r3 = 26;
+ } else if (mask & TST_TAINT_A) {
+ r1 = 2;
+ r2 = 6;
+ r3 = 25;
+ } else if (mask & TST_TAINT_D) {
+ r1 = 2;
+ r2 = 6;
+ r3 = 23;
+ } else if (mask & TST_TAINT_U) {
+ r1 = 2;
+ r2 = 6;
+ r3 = 21;
+ } else {
+ r1 = 2;
+ r2 = 6;
+ r3 = 16;
+ }
+
+ return tst_kvercmp(r1, r2, r3);
+}
+
+void tst_taint_init(unsigned int mask)
+{
+ unsigned int taint = -1;
+
+ if (mask == 0)
+ tst_brk(TBROK, "mask is not allowed to be 0");
+
+ if (tst_taint_check_kver(mask) < 0)
+ tst_res(TCONF, "Kernel is too old for requested mask");
+
+ taint_mask = mask;
+ taint = tst_taint_read();
+
+ if (taint & TST_TAINT_W) {
+ tst_res(TCONF, "Ignoring already set kernel warning taint");
+ taint_mask &= ~TST_TAINT_W;
+ }
+
+ if ((taint & taint_mask) != 0)
+ tst_brk(TBROK, "Kernel is already tainted: %u", taint);
+}
+
+
+unsigned int tst_taint_check(void)
+{
+ unsigned int taint = -1;
+
+ if (taint_mask == (unsigned int) -1)
+ tst_brk(TBROK, "need to call tst_taint_init() first");
+
+ taint = tst_taint_read();
+
+ return (taint & taint_mask);
+}
diff --git a/src/kernel/tests/lib/tst_test.c b/src/kernel/tests/lib/tst_test.c
new file mode 100644
index 0000000..135cd4e
--- /dev/null
+++ b/src/kernel/tests/lib/tst_test.c
@@ -0,0 +1,1387 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2015-2016 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_device.h"
+#include "lapi/futex.h"
+#include "lapi/syscalls.h"
+#include "tst_ansi_color.h"
+#include "tst_safe_stdio.h"
+#include "tst_timer_test.h"
+#include "tst_clocks.h"
+#include "tst_timer.h"
+#include "tst_wallclock.h"
+#include "tst_sys_conf.h"
+#include "tst_kconfig.h"
+
+#include "old_resource.h"
+#include "old_device.h"
+#include "old_tmpdir.h"
+
+/*
+ * Hack to get TCID defined in newlib tests
+ */
+const char *TCID __attribute__((weak));
+
+#define LINUX_GIT_URL "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id="
+#define CVE_DB_URL "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-"
+
+struct tst_test *tst_test;
+
+static const char *tid;
+static int iterations = 1;
+static float duration = -1;
+static float timeout_mul = -1;
+static pid_t main_pid, lib_pid;
+static int mntpoint_mounted;
+static int ovl_mounted;
+static struct timespec tst_start_time; /* valid only for test pid */
+
+struct results {
+ int passed;
+ int skipped;
+ int failed;
+ int warnings;
+ unsigned int timeout;
+};
+
+static struct results *results;
+
+static int ipc_fd;
+
+extern void *tst_futexes;
+extern unsigned int tst_max_futexes;
+
+#define IPC_ENV_VAR "LTP_IPC_PATH"
+
+static char ipc_path[1064];
+const char *tst_ipc_path = ipc_path;
+
+static char shm_path[1024];
+
+int TST_ERR;
+long TST_RET;
+
+static void do_cleanup(void);
+static void do_exit(int ret) __attribute__ ((noreturn));
+
+static void setup_ipc(void)
+{
+ size_t size = getpagesize();
+
+ if (access("/dev/shm", F_OK) == 0) {
+ snprintf(shm_path, sizeof(shm_path), "/dev/shm/ltp_%s_%d",
+ tid, getpid());
+ } else {
+ char *tmpdir;
+
+ if (!tst_tmpdir_created())
+ tst_tmpdir();
+
+ tmpdir = tst_get_tmpdir();
+ snprintf(shm_path, sizeof(shm_path), "%s/ltp_%s_%d",
+ tmpdir, tid, getpid());
+ free(tmpdir);
+ }
+
+ ipc_fd = open(shm_path, O_CREAT | O_EXCL | O_RDWR, 0600);
+ if (ipc_fd < 0)
+ tst_brk(TBROK | TERRNO, "open(%s)", shm_path);
+ SAFE_CHMOD(shm_path, 0666);
+
+ SAFE_FTRUNCATE(ipc_fd, size);
+
+ results = SAFE_MMAP(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ipc_fd, 0);
+
+ /* Checkpoints needs to be accessible from processes started by exec() */
+ if (tst_test->needs_checkpoints || tst_test->child_needs_reinit) {
+ sprintf(ipc_path, IPC_ENV_VAR "=%s", shm_path);
+ putenv(ipc_path);
+ } else {
+ SAFE_UNLINK(shm_path);
+ }
+
+ SAFE_CLOSE(ipc_fd);
+
+ if (tst_test->needs_checkpoints) {
+ tst_futexes = (char*)results + sizeof(struct results);
+ tst_max_futexes = (size - sizeof(struct results))/sizeof(futex_t);
+ }
+}
+
+static void cleanup_ipc(void)
+{
+ size_t size = getpagesize();
+
+ if (ipc_fd > 0 && close(ipc_fd))
+ tst_res(TWARN | TERRNO, "close(ipc_fd) failed");
+
+ if (shm_path[0] && !access(shm_path, F_OK) && unlink(shm_path))
+ tst_res(TWARN | TERRNO, "unlink(%s) failed", shm_path);
+
+ if (results) {
+ msync((void*)results, size, MS_SYNC);
+ munmap((void*)results, size);
+ results = NULL;
+ }
+}
+
+void tst_reinit(void)
+{
+ const char *path = getenv(IPC_ENV_VAR);
+ size_t size = getpagesize();
+ int fd;
+
+ if (!path)
+ tst_brk(TBROK, IPC_ENV_VAR" is not defined");
+
+ if (access(path, F_OK))
+ tst_brk(TBROK, "File %s does not exist!", path);
+
+ fd = SAFE_OPEN(path, O_RDWR);
+
+ results = SAFE_MMAP(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ tst_futexes = (char*)results + sizeof(struct results);
+ tst_max_futexes = (size - sizeof(struct results))/sizeof(futex_t);
+
+ SAFE_CLOSE(fd);
+}
+
+static void update_results(int ttype)
+{
+ if (!results)
+ return;
+
+ switch (ttype) {
+ case TCONF:
+ tst_atomic_inc(&results->skipped);
+ break;
+ case TPASS:
+ tst_atomic_inc(&results->passed);
+ break;
+ case TWARN:
+ tst_atomic_inc(&results->warnings);
+ break;
+ case TFAIL:
+ tst_atomic_inc(&results->failed);
+ break;
+ }
+}
+
+static void print_result(const char *file, const int lineno, int ttype,
+ const char *fmt, va_list va)
+{
+ char buf[1024];
+ char *str = buf;
+ int ret, size = sizeof(buf), ssize, int_errno, buflen;
+ const char *str_errno = NULL;
+ const char *res;
+
+ switch (TTYPE_RESULT(ttype)) {
+ case TPASS:
+ res = "PASS";
+ break;
+ case TFAIL:
+ res = "FAIL";
+ break;
+ case TBROK:
+ res = "BROK";
+ break;
+ case TCONF:
+ res = "CONF";
+ break;
+ case TWARN:
+ res = "WARN";
+ break;
+ case TINFO:
+ res = "INFO";
+ break;
+ default:
+ tst_brk(TBROK, "Invalid ttype value %i", ttype);
+ abort();
+ }
+
+ if (ttype & TERRNO) {
+ str_errno = tst_strerrno(errno);
+ int_errno = errno;
+ }
+
+ if (ttype & TTERRNO) {
+ str_errno = tst_strerrno(TST_ERR);
+ int_errno = TST_ERR;
+ }
+
+ if (ttype & TRERRNO) {
+ int_errno = TST_RET < 0 ? -(int)TST_RET : (int)TST_RET;
+ str_errno = tst_strerrno(int_errno);
+ }
+
+ ret = snprintf(str, size, "%s:%i: ", file, lineno);
+ str += ret;
+ size -= ret;
+
+ if (tst_color_enabled(STDERR_FILENO))
+ ret = snprintf(str, size, "%s%s: %s", tst_ttype2color(ttype),
+ res, ANSI_COLOR_RESET);
+ else
+ ret = snprintf(str, size, "%s: ", res);
+ str += ret;
+ size -= ret;
+
+ ssize = size - 2;
+ ret = vsnprintf(str, size, fmt, va);
+ str += MIN(ret, ssize);
+ size -= MIN(ret, ssize);
+ if (ret >= ssize) {
+ tst_res_(file, lineno, TWARN,
+ "Next message is too long and truncated:");
+ } else if (str_errno) {
+ ssize = size - 2;
+ ret = snprintf(str, size, ": %s (%d)", str_errno, int_errno);
+ str += MIN(ret, ssize);
+ size -= MIN(ret, ssize);
+ if (ret >= ssize)
+ tst_res_(file, lineno, TWARN,
+ "Next message is too long and truncated:");
+ }
+
+ snprintf(str, size, "\n");
+
+ /* we might be called from signal handler, so use write() */
+ buflen = str - buf + 1;
+ str = buf;
+ while (buflen) {
+ ret = write(STDERR_FILENO, str, buflen);
+ if (ret <= 0)
+ break;
+
+ str += ret;
+ buflen -= ret;
+ }
+}
+
+void tst_vres_(const char *file, const int lineno, int ttype,
+ const char *fmt, va_list va)
+{
+ print_result(file, lineno, ttype, fmt, va);
+
+ update_results(TTYPE_RESULT(ttype));
+}
+
+void tst_vbrk_(const char *file, const int lineno, int ttype,
+ const char *fmt, va_list va);
+
+static void (*tst_brk_handler)(const char *file, const int lineno, int ttype,
+ const char *fmt, va_list va) = tst_vbrk_;
+
+static void tst_cvres(const char *file, const int lineno, int ttype,
+ const char *fmt, va_list va)
+{
+ if (TTYPE_RESULT(ttype) == TBROK) {
+ ttype &= ~TTYPE_MASK;
+ ttype |= TWARN;
+ }
+
+ print_result(file, lineno, ttype, fmt, va);
+ update_results(TTYPE_RESULT(ttype));
+}
+
+static void do_test_cleanup(void)
+{
+ tst_brk_handler = tst_cvres;
+
+ if (tst_test->cleanup)
+ tst_test->cleanup();
+
+ tst_free_all();
+
+ tst_brk_handler = tst_vbrk_;
+}
+
+void tst_vbrk_(const char *file, const int lineno, int ttype,
+ const char *fmt, va_list va)
+{
+ print_result(file, lineno, ttype, fmt, va);
+ update_results(TTYPE_RESULT(ttype));
+
+ /*
+ * The getpid implementation in some C library versions may cause cloned
+ * test threads to show the same pid as their parent when CLONE_VM is
+ * specified but CLONE_THREAD is not. Use direct syscall to avoid
+ * cleanup running in the child.
+ */
+ if (syscall(SYS_getpid) == main_pid)
+ do_test_cleanup();
+
+ if (getpid() == lib_pid)
+ do_exit(TTYPE_RESULT(ttype));
+
+ exit(TTYPE_RESULT(ttype));
+}
+
+void tst_res_(const char *file, const int lineno, int ttype,
+ const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ tst_vres_(file, lineno, ttype, fmt, va);
+ va_end(va);
+}
+
+void tst_brk_(const char *file, const int lineno, int ttype,
+ const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ tst_brk_handler(file, lineno, ttype, fmt, va);
+ va_end(va);
+}
+
+static void check_child_status(pid_t pid, int status)
+{
+ int ret;
+
+ if (WIFSIGNALED(status)) {
+ tst_brk(TBROK, "Child (%i) killed by signal %s",
+ pid, tst_strsig(WTERMSIG(status)));
+ }
+
+ if (!(WIFEXITED(status)))
+ tst_brk(TBROK, "Child (%i) exited abnormally", pid);
+
+ ret = WEXITSTATUS(status);
+ switch (ret) {
+ case TPASS:
+ break;
+ case TBROK:
+ case TCONF:
+ tst_brk(ret, "Reported by child (%i)", pid);
+ break;
+ default:
+ tst_brk(TBROK, "Invalid child (%i) exit value %i", pid, ret);
+ }
+}
+
+void tst_reap_children(void)
+{
+ int status;
+ pid_t pid;
+
+ for (;;) {
+ pid = wait(&status);
+
+ if (pid > 0) {
+ check_child_status(pid, status);
+ continue;
+ }
+
+ if (errno == ECHILD)
+ break;
+
+ if (errno == EINTR)
+ continue;
+
+ tst_brk(TBROK | TERRNO, "wait() failed");
+ }
+}
+
+
+pid_t safe_fork(const char *filename, unsigned int lineno)
+{
+ pid_t pid;
+
+ if (!tst_test->forks_child)
+ tst_brk(TBROK, "test.forks_child must be set!");
+
+ tst_flush();
+
+ pid = fork();
+ if (pid < 0)
+ tst_brk_(filename, lineno, TBROK | TERRNO, "fork() failed");
+
+ if (!pid)
+ atexit(tst_free_all);
+
+ return pid;
+}
+
+static struct option {
+ char *optstr;
+ char *help;
+} options[] = {
+ {"h", "-h Prints this help"},
+ {"i:", "-i n Execute test n times"},
+ {"I:", "-I x Execute test for n seconds"},
+ {"C:", "-C ARG Run child process with ARG arguments (used internally)"},
+};
+
+static void print_help(void)
+{
+ unsigned int i;
+
+ fprintf(stderr, "Options\n");
+ fprintf(stderr, "-------\n");
+
+ for (i = 0; i < ARRAY_SIZE(options); i++)
+ fprintf(stderr, "%s\n", options[i].help);
+
+ if (!tst_test->options)
+ return;
+
+ for (i = 0; tst_test->options[i].optstr; i++)
+ fprintf(stderr, "%s\n", tst_test->options[i].help);
+}
+
+static void print_test_tags(void)
+{
+ unsigned int i;
+ const struct tst_tag *tags = tst_test->tags;
+
+ if (!tags)
+ return;
+
+ printf("\nTags\n");
+ printf("----\n");
+
+ for (i = 0; tags[i].name; i++) {
+ if (!strcmp(tags[i].name, "CVE"))
+ printf(CVE_DB_URL "%s\n", tags[i].value);
+ else if (!strcmp(tags[i].name, "linux-git"))
+ printf(LINUX_GIT_URL "%s\n", tags[i].value);
+ else
+ printf("%s: %s\n", tags[i].name, tags[i].value);
+ }
+
+ printf("\n");
+}
+
+static void check_option_collision(void)
+{
+ unsigned int i, j;
+ struct tst_option *toptions = tst_test->options;
+
+ if (!toptions)
+ return;
+
+ for (i = 0; toptions[i].optstr; i++) {
+ for (j = 0; j < ARRAY_SIZE(options); j++) {
+ if (toptions[i].optstr[0] == options[j].optstr[0]) {
+ tst_brk(TBROK, "Option collision '%s'",
+ options[j].help);
+ }
+ }
+ }
+}
+
+static unsigned int count_options(void)
+{
+ unsigned int i;
+
+ if (!tst_test->options)
+ return 0;
+
+ for (i = 0; tst_test->options[i].optstr; i++);
+
+ return i;
+}
+
+static void parse_topt(unsigned int topts_len, int opt, char *optarg)
+{
+ unsigned int i;
+ struct tst_option *toptions = tst_test->options;
+
+ for (i = 0; i < topts_len; i++) {
+ if (toptions[i].optstr[0] == opt)
+ break;
+ }
+
+ if (i >= topts_len)
+ tst_brk(TBROK, "Invalid option '%c' (should not happen)", opt);
+
+ if (*toptions[i].arg)
+ tst_res(TWARN, "Option -%c passed multiple times", opt);
+
+ *(toptions[i].arg) = optarg ? optarg : "";
+}
+
+/* see self_exec.c */
+#ifdef UCLINUX
+extern char *child_args;
+#endif
+
+static void parse_opts(int argc, char *argv[])
+{
+ unsigned int i, topts_len = count_options();
+ char optstr[2 * ARRAY_SIZE(options) + 2 * topts_len];
+ int opt;
+
+ check_option_collision();
+
+ optstr[0] = 0;
+
+ for (i = 0; i < ARRAY_SIZE(options); i++)
+ strcat(optstr, options[i].optstr);
+
+ for (i = 0; i < topts_len; i++)
+ strcat(optstr, tst_test->options[i].optstr);
+
+ while ((opt = getopt(argc, argv, optstr)) > 0) {
+ switch (opt) {
+ case '?':
+ print_help();
+ tst_brk(TBROK, "Invalid option");
+ break;
+ case 'h':
+ print_help();
+ print_test_tags();
+ exit(0);
+ case 'i':
+ iterations = atoi(optarg);
+ break;
+ case 'I':
+ duration = atof(optarg);
+ break;
+ case 'C':
+#ifdef UCLINUX
+ child_args = optarg;
+#endif
+ break;
+ default:
+ parse_topt(topts_len, opt, optarg);
+ }
+ }
+
+ if (optind < argc)
+ tst_brk(TBROK, "Unexpected argument(s) '%s'...", argv[optind]);
+}
+
+int tst_parse_int(const char *str, int *val, int min, int max)
+{
+ long rval;
+
+ if (!str)
+ return 0;
+
+ int ret = tst_parse_long(str, &rval, min, max);
+
+ if (ret)
+ return ret;
+
+ *val = (int)rval;
+ return 0;
+}
+
+int tst_parse_long(const char *str, long *val, long min, long max)
+{
+ long rval;
+ char *end;
+
+ if (!str)
+ return 0;
+
+ errno = 0;
+ rval = strtol(str, &end, 10);
+
+ if (str == end || *end != '\0')
+ return EINVAL;
+
+ if (errno)
+ return errno;
+
+ if (rval > max || rval < min)
+ return ERANGE;
+
+ *val = rval;
+ return 0;
+}
+
+int tst_parse_float(const char *str, float *val, float min, float max)
+{
+ double rval;
+ char *end;
+
+ if (!str)
+ return 0;
+
+ errno = 0;
+ rval = strtod(str, &end);
+
+ if (str == end || *end != '\0')
+ return EINVAL;
+
+ if (errno)
+ return errno;
+
+ if (rval > (double)max || rval < (double)min)
+ return ERANGE;
+
+ *val = (float)rval;
+ return 0;
+}
+
+static void print_colored(const char *str)
+{
+ if (tst_color_enabled(STDOUT_FILENO))
+ printf("%s%s%s", ANSI_COLOR_YELLOW, str, ANSI_COLOR_RESET);
+ else
+ printf("%s", str);
+}
+
+static void print_failure_hints(void)
+{
+ unsigned int i;
+ const struct tst_tag *tags = tst_test->tags;
+
+ if (!tags)
+ return;
+
+ int hint_printed = 0;
+ for (i = 0; tags[i].name; i++) {
+ if (!strcmp(tags[i].name, "linux-git")) {
+ if (!hint_printed) {
+ hint_printed = 1;
+ printf("\n");
+ print_colored("HINT: ");
+ printf("You _MAY_ be missing kernel fixes, see:\n\n");
+ }
+
+ printf(LINUX_GIT_URL "%s\n", tags[i].value);
+ }
+
+ }
+
+ hint_printed = 0;
+ for (i = 0; tags[i].name; i++) {
+ if (!strcmp(tags[i].name, "CVE")) {
+ if (!hint_printed) {
+ hint_printed = 1;
+ printf("\n");
+ print_colored("HINT: ");
+ printf("You _MAY_ be vulnerable to CVE(s), see:\n\n");
+ }
+
+ printf(CVE_DB_URL "%s\n", tags[i].value);
+ }
+ }
+}
+
+static void do_exit(int ret)
+{
+ if (results) {
+ if (results->passed && ret == TCONF)
+ ret = 0;
+
+ if (results->failed) {
+ ret |= TFAIL;
+ print_failure_hints();
+ }
+
+ if (results->skipped && !results->passed)
+ ret |= TCONF;
+
+ if (results->warnings)
+ ret |= TWARN;
+
+ printf("\nSummary:\n");
+ printf("passed %d\n", results->passed);
+ printf("failed %d\n", results->failed);
+ printf("skipped %d\n", results->skipped);
+ printf("warnings %d\n", results->warnings);
+ }
+
+ do_cleanup();
+
+ exit(ret);
+}
+
+void check_kver(void)
+{
+ int v1, v2, v3;
+
+ if (tst_parse_kver(tst_test->min_kver, &v1, &v2, &v3)) {
+ tst_res(TWARN,
+ "Invalid kernel version %s, expected %%d.%%d.%%d",
+ tst_test->min_kver);
+ }
+
+ if (tst_kvercmp(v1, v2, v3) < 0) {
+ tst_brk(TCONF, "The test requires kernel %s or newer",
+ tst_test->min_kver);
+ }
+}
+
+static int results_equal(struct results *a, struct results *b)
+{
+ if (a->passed != b->passed)
+ return 0;
+
+ if (a->failed != b->failed)
+ return 0;
+
+ if (a->skipped != b->skipped)
+ return 0;
+
+ return 1;
+}
+
+static int needs_tmpdir(void)
+{
+ return tst_test->needs_tmpdir ||
+ tst_test->needs_device ||
+ tst_test->mntpoint ||
+ tst_test->resource_files ||
+ tst_test->needs_checkpoints;
+}
+
+static void copy_resources(void)
+{
+ unsigned int i;
+
+ for (i = 0; tst_test->resource_files[i]; i++)
+ TST_RESOURCE_COPY(NULL, tst_test->resource_files[i], NULL);
+}
+
+static const char *get_tid(char *argv[])
+{
+ char *p;
+
+ if (!argv[0] || !argv[0][0]) {
+ tst_res(TINFO, "argv[0] is empty!");
+ return "ltp_empty_argv";
+ }
+
+ p = strrchr(argv[0], '/');
+ if (p)
+ return p+1;
+
+ return argv[0];
+}
+
+static struct tst_device tdev;
+struct tst_device *tst_device;
+
+static void assert_test_fn(void)
+{
+ int cnt = 0;
+
+ if (tst_test->test)
+ cnt++;
+
+ if (tst_test->test_all)
+ cnt++;
+
+ if (tst_test->sample)
+ cnt++;
+
+ if (!cnt)
+ tst_brk(TBROK, "No test function specified");
+
+ if (cnt != 1)
+ tst_brk(TBROK, "You can define only one test function");
+
+ if (tst_test->test && !tst_test->tcnt)
+ tst_brk(TBROK, "Number of tests (tcnt) must be > 0");
+
+ if (!tst_test->test && tst_test->tcnt)
+ tst_brk(TBROK, "You can define tcnt only for test()");
+}
+
+static int prepare_and_mount_ro_fs(const char *dev,
+ const char *mntpoint,
+ const char *fs_type)
+{
+ char buf[PATH_MAX];
+
+ if (mount(dev, mntpoint, fs_type, 0, NULL)) {
+ tst_res(TINFO | TERRNO, "Can't mount %s at %s (%s)",
+ dev, mntpoint, fs_type);
+ return 1;
+ }
+
+ mntpoint_mounted = 1;
+
+ snprintf(buf, sizeof(buf), "%s/dir/", mntpoint);
+ SAFE_MKDIR(buf, 0777);
+
+ snprintf(buf, sizeof(buf), "%s/file", mntpoint);
+ SAFE_FILE_PRINTF(buf, "file content");
+ SAFE_CHMOD(buf, 0777);
+
+ SAFE_MOUNT(dev, mntpoint, fs_type, MS_REMOUNT | MS_RDONLY, NULL);
+
+ return 0;
+}
+
+static void prepare_and_mount_dev_fs(const char *mntpoint)
+{
+ const char *flags[] = {"nodev", NULL};
+ int mounted_nodev;
+
+ mounted_nodev = tst_path_has_mnt_flags(NULL, flags);
+ if (mounted_nodev) {
+ tst_res(TINFO, "tmpdir isn't suitable for creating devices, "
+ "mounting tmpfs without nodev on %s", mntpoint);
+ SAFE_MOUNT(NULL, mntpoint, "tmpfs", 0, NULL);
+ mntpoint_mounted = 1;
+ }
+}
+
+static void prepare_device(void)
+{
+ if (tst_test->format_device) {
+ SAFE_MKFS(tdev.dev, tdev.fs_type, tst_test->dev_fs_opts,
+ tst_test->dev_extra_opts);
+ }
+
+ if (tst_test->needs_rofs) {
+ prepare_and_mount_ro_fs(tdev.dev, tst_test->mntpoint,
+ tdev.fs_type);
+ return;
+ }
+
+ if (tst_test->mount_device) {
+ SAFE_MOUNT(tdev.dev, tst_test->mntpoint, tdev.fs_type,
+ tst_test->mnt_flags, tst_test->mnt_data);
+ mntpoint_mounted = 1;
+ }
+}
+
+static void do_setup(int argc, char *argv[])
+{
+ if (!tst_test)
+ tst_brk(TBROK, "No tests to run");
+
+ if (tst_test->tconf_msg)
+ tst_brk(TCONF, "%s", tst_test->tconf_msg);
+
+ if (tst_test->needs_kconfigs)
+ tst_kconfig_check(tst_test->needs_kconfigs);
+
+ assert_test_fn();
+
+ tid = get_tid(argv);
+
+ if (tst_test->sample)
+ tst_test = tst_timer_test_setup(tst_test);
+
+ parse_opts(argc, argv);
+
+ if (tst_test->needs_root && geteuid() != 0)
+ tst_brk(TCONF, "Test needs to be run as root");
+
+ if (tst_test->min_kver)
+ check_kver();
+
+ if (tst_test->needs_cmds) {
+ const char *cmd;
+ char path[PATH_MAX];
+ int i;
+
+ for (i = 0; (cmd = tst_test->needs_cmds[i]); ++i)
+ if (tst_get_path(cmd, path, sizeof(path)))
+ tst_brk(TCONF, "Couldn't find '%s' in $PATH", cmd);
+ }
+
+ if (tst_test->needs_drivers) {
+ const char *name;
+ int i;
+
+ for (i = 0; (name = tst_test->needs_drivers[i]); ++i)
+ if (tst_check_driver(name))
+ tst_brk(TCONF, "%s driver not available", name);
+ }
+
+ if (tst_test->format_device)
+ tst_test->needs_device = 1;
+
+ if (tst_test->mount_device) {
+ tst_test->needs_device = 1;
+ tst_test->format_device = 1;
+ }
+
+ if (tst_test->all_filesystems)
+ tst_test->needs_device = 1;
+
+ if (tst_test->request_hugepages)
+ tst_request_hugepages(tst_test->request_hugepages);
+
+ setup_ipc();
+
+ if (tst_test->bufs)
+ tst_buffers_alloc(tst_test->bufs);
+
+ if (needs_tmpdir() && !tst_tmpdir_created())
+ tst_tmpdir();
+
+ if (tst_test->save_restore) {
+ const char * const *name = tst_test->save_restore;
+
+ while (*name) {
+ tst_sys_conf_save(*name);
+ name++;
+ }
+ }
+
+ if (tst_test->mntpoint)
+ SAFE_MKDIR(tst_test->mntpoint, 0777);
+
+ if ((tst_test->needs_devfs || tst_test->needs_rofs ||
+ tst_test->mount_device || tst_test->all_filesystems) &&
+ !tst_test->mntpoint) {
+ tst_brk(TBROK, "tst_test->mntpoint must be set!");
+ }
+
+ if (!!tst_test->needs_rofs + !!tst_test->needs_devfs +
+ !!tst_test->needs_device > 1) {
+ tst_brk(TBROK,
+ "Two or more of needs_{rofs, devfs, device} are set");
+ }
+
+ if (tst_test->needs_devfs)
+ prepare_and_mount_dev_fs(tst_test->mntpoint);
+
+ if (tst_test->needs_rofs) {
+ /* If we failed to mount read-only tmpfs. Fallback to
+ * using a device with read-only filesystem.
+ */
+ if (prepare_and_mount_ro_fs(NULL, tst_test->mntpoint, "tmpfs")) {
+ tst_res(TINFO, "Can't mount tmpfs read-only, "
+ "falling back to block device...");
+ tst_test->needs_device = 1;
+ tst_test->format_device = 1;
+ }
+ }
+
+ if (tst_test->needs_device && !mntpoint_mounted) {
+ tdev.dev = tst_acquire_device_(NULL, tst_test->dev_min_size);
+
+ if (!tdev.dev)
+ tst_brk(TCONF, "Failed to acquire device");
+
+ tst_device = &tdev;
+
+ if (tst_test->dev_fs_type)
+ tdev.fs_type = tst_test->dev_fs_type;
+ else
+ tdev.fs_type = tst_dev_fs_type();
+
+ if (!tst_test->all_filesystems)
+ prepare_device();
+ }
+
+ if (tst_test->needs_overlay && !tst_test->mount_device) {
+ tst_brk(TBROK, "tst_test->mount_device must be set");
+ }
+ if (tst_test->needs_overlay && !mntpoint_mounted) {
+ tst_brk(TBROK, "tst_test->mntpoint must be mounted");
+ }
+ if (tst_test->needs_overlay && !ovl_mounted) {
+ SAFE_MOUNT_OVERLAY();
+ ovl_mounted = 1;
+ }
+
+ if (tst_test->resource_files)
+ copy_resources();
+
+ if (tst_test->restore_wallclock)
+ tst_wallclock_save();
+
+ if (tst_test->taint_check)
+ tst_taint_init(tst_test->taint_check);
+}
+
+static void do_test_setup(void)
+{
+ main_pid = getpid();
+
+ if (tst_test->caps)
+ tst_cap_setup(tst_test->caps, TST_CAP_REQ);
+
+ if (tst_test->setup)
+ tst_test->setup();
+
+ if (main_pid != getpid())
+ tst_brk(TBROK, "Runaway child in setup()!");
+
+ if (tst_test->caps)
+ tst_cap_setup(tst_test->caps, TST_CAP_DROP);
+}
+
+static void do_cleanup(void)
+{
+ if (ovl_mounted)
+ SAFE_UMOUNT(OVL_MNT);
+
+ if (mntpoint_mounted)
+ tst_umount(tst_test->mntpoint);
+
+ if (tst_test->needs_device && tdev.dev)
+ tst_release_device(tdev.dev);
+
+ if (tst_tmpdir_created()) {
+ /* avoid munmap() on wrong pointer in tst_rmdir() */
+ tst_futexes = NULL;
+ tst_rmdir();
+ }
+
+ tst_sys_conf_restore(0);
+
+ if (tst_test->restore_wallclock)
+ tst_wallclock_restore();
+
+ cleanup_ipc();
+}
+
+static void run_tests(void)
+{
+ unsigned int i;
+ struct results saved_results;
+
+ if (!tst_test->test) {
+ saved_results = *results;
+ tst_test->test_all();
+
+ if (getpid() != main_pid) {
+ exit(0);
+ }
+
+ tst_reap_children();
+
+ if (results_equal(&saved_results, results))
+ tst_brk(TBROK, "Test haven't reported results!");
+ return;
+ }
+
+ for (i = 0; i < tst_test->tcnt; i++) {
+ saved_results = *results;
+ tst_test->test(i);
+
+ if (getpid() != main_pid) {
+ exit(0);
+ }
+
+ tst_reap_children();
+
+ if (results_equal(&saved_results, results))
+ tst_brk(TBROK, "Test %i haven't reported results!", i);
+ }
+}
+
+static unsigned long long get_time_ms(void)
+{
+ struct timespec ts;
+
+ if (tst_clock_gettime(CLOCK_MONOTONIC, &ts))
+ tst_brk(TBROK | TERRNO, "tst_clock_gettime()");
+
+ return tst_timespec_to_ms(ts);
+}
+
+static void add_paths(void)
+{
+ char *old_path = getenv("PATH");
+ const char *start_dir;
+ char *new_path;
+
+ start_dir = tst_get_startwd();
+
+ if (old_path)
+ SAFE_ASPRINTF(&new_path, "%s::%s", old_path, start_dir);
+ else
+ SAFE_ASPRINTF(&new_path, "::%s", start_dir);
+
+ SAFE_SETENV("PATH", new_path, 1);
+ free(new_path);
+}
+
+static void heartbeat(void)
+{
+ if (tst_clock_gettime(CLOCK_MONOTONIC, &tst_start_time))
+ tst_res(TWARN | TERRNO, "tst_clock_gettime() failed");
+
+ kill(getppid(), SIGUSR1);
+}
+
+static void testrun(void)
+{
+ unsigned int i = 0;
+ unsigned long long stop_time = 0;
+ int cont = 1;
+
+ heartbeat();
+ add_paths();
+ do_test_setup();
+
+ if (duration > 0)
+ stop_time = get_time_ms() + (unsigned long long)(duration * 1000);
+
+ for (;;) {
+ cont = 0;
+
+ if (i < (unsigned int)iterations) {
+ i++;
+ cont = 1;
+ }
+
+ if (stop_time && get_time_ms() < stop_time)
+ cont = 1;
+
+ if (!cont)
+ break;
+
+ run_tests();
+ heartbeat();
+ }
+
+ do_test_cleanup();
+ exit(0);
+}
+
+static pid_t test_pid;
+
+
+static volatile sig_atomic_t sigkill_retries;
+
+#define WRITE_MSG(msg) do { \
+ if (write(2, msg, sizeof(msg) - 1)) { \
+ /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 */ \
+ } \
+} while (0)
+
+static void alarm_handler(int sig LTP_ATTRIBUTE_UNUSED)
+{
+ WRITE_MSG("Test timeouted, sending SIGKILL!\n");
+ kill(-test_pid, SIGKILL);
+ alarm(5);
+
+ if (++sigkill_retries > 10) {
+ WRITE_MSG("Cannot kill test processes!\n");
+ WRITE_MSG("Congratulation, likely test hit a kernel bug.\n");
+ WRITE_MSG("Exitting uncleanly...\n");
+ _exit(TFAIL);
+ }
+}
+
+static void heartbeat_handler(int sig LTP_ATTRIBUTE_UNUSED)
+{
+ alarm(results->timeout);
+ sigkill_retries = 0;
+}
+
+static void sigint_handler(int sig LTP_ATTRIBUTE_UNUSED)
+{
+ if (test_pid > 0) {
+ WRITE_MSG("Sending SIGKILL to test process...\n");
+ kill(-test_pid, SIGKILL);
+ }
+}
+
+unsigned int tst_timeout_remaining(void)
+{
+ static struct timespec now;
+ unsigned int elapsed;
+
+ if (tst_clock_gettime(CLOCK_MONOTONIC, &now))
+ tst_res(TWARN | TERRNO, "tst_clock_gettime() failed");
+
+ elapsed = (tst_timespec_diff_ms(now, tst_start_time) + 500) / 1000;
+ if (results->timeout > elapsed)
+ return results->timeout - elapsed;
+
+ return 0;
+}
+
+unsigned int tst_multiply_timeout(unsigned int timeout)
+{
+ char *mul;
+ int ret;
+
+ if (timeout_mul == -1) {
+ mul = getenv("LTP_TIMEOUT_MUL");
+ if (mul) {
+ if ((ret = tst_parse_float(mul, &timeout_mul, 1, 10000))) {
+ tst_brk(TBROK, "Failed to parse LTP_TIMEOUT_MUL: %s",
+ tst_strerrno(ret));
+ }
+ } else {
+ timeout_mul = 1;
+ }
+ }
+ if (timeout_mul < 1)
+ tst_brk(TBROK, "LTP_TIMEOUT_MUL must to be int >= 1! (%.2f)",
+ timeout_mul);
+
+ if (timeout < 1)
+ tst_brk(TBROK, "timeout must to be >= 1! (%d)", timeout);
+
+ return timeout * timeout_mul;
+}
+
+void tst_set_timeout(int timeout)
+{
+ if (timeout == -1) {
+ tst_res(TINFO, "Timeout per run is disabled");
+ return;
+ }
+
+ if (timeout < 1)
+ tst_brk(TBROK, "timeout must to be >= 1! (%d)", timeout);
+
+ results->timeout = tst_multiply_timeout(timeout);
+
+ tst_res(TINFO, "Timeout per run is %uh %02um %02us",
+ results->timeout/3600, (results->timeout%3600)/60,
+ results->timeout % 60);
+
+ if (getpid() == lib_pid)
+ alarm(results->timeout);
+ else
+ heartbeat();
+}
+
+static int fork_testrun(void)
+{
+ int status;
+
+ if (tst_test->timeout)
+ tst_set_timeout(tst_test->timeout);
+ else
+ tst_set_timeout(300);
+
+ SAFE_SIGNAL(SIGINT, sigint_handler);
+
+ test_pid = fork();
+ if (test_pid < 0)
+ tst_brk(TBROK | TERRNO, "fork()");
+
+ if (!test_pid) {
+ SAFE_SIGNAL(SIGALRM, SIG_DFL);
+ SAFE_SIGNAL(SIGUSR1, SIG_DFL);
+ SAFE_SIGNAL(SIGINT, SIG_DFL);
+ SAFE_SETPGID(0, 0);
+ testrun();
+ }
+
+ SAFE_WAITPID(test_pid, &status, 0);
+ alarm(0);
+ SAFE_SIGNAL(SIGINT, SIG_DFL);
+
+ if (tst_test->taint_check && tst_taint_check()) {
+ tst_res(TFAIL, "Kernel is now tainted.");
+ return TFAIL;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status))
+ return WEXITSTATUS(status);
+
+ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) {
+ tst_res(TINFO, "If you are running on slow machine, "
+ "try exporting LTP_TIMEOUT_MUL > 1");
+ tst_brk(TBROK, "Test killed! (timeout?)");
+ }
+
+ if (WIFSIGNALED(status))
+ tst_brk(TBROK, "Test killed by %s!", tst_strsig(WTERMSIG(status)));
+
+ return 0;
+}
+
+static int run_tcases_per_fs(void)
+{
+ int ret = 0;
+ unsigned int i;
+ const char *const *filesystems = tst_get_supported_fs_types(tst_test->dev_fs_flags);
+
+ if (!filesystems[0])
+ tst_brk(TCONF, "There are no supported filesystems");
+
+ for (i = 0; filesystems[i]; i++) {
+
+ tst_res(TINFO, "Testing on %s", filesystems[i]);
+ tdev.fs_type = filesystems[i];
+
+ prepare_device();
+
+ ret = fork_testrun();
+
+ if (mntpoint_mounted) {
+ tst_umount(tst_test->mntpoint);
+ mntpoint_mounted = 0;
+ }
+
+ if (ret == TCONF)
+ continue;
+
+ if (ret == 0)
+ continue;
+
+ do_exit(ret);
+ }
+
+ return ret;
+}
+
+unsigned int tst_variant;
+
+void tst_run_tcases(int argc, char *argv[], struct tst_test *self)
+{
+ int ret = 0;
+ unsigned int test_variants = 1;
+
+ lib_pid = getpid();
+ tst_test = self;
+
+ do_setup(argc, argv);
+
+ TCID = tid;
+
+ SAFE_SIGNAL(SIGALRM, alarm_handler);
+ SAFE_SIGNAL(SIGUSR1, heartbeat_handler);
+
+ if (tst_test->test_variants)
+ test_variants = tst_test->test_variants;
+
+ for (tst_variant = 0; tst_variant < test_variants; tst_variant++) {
+ if (tst_test->all_filesystems)
+ ret |= run_tcases_per_fs();
+ else
+ ret |= fork_testrun();
+
+ if (ret & ~(TCONF))
+ goto exit;
+ }
+
+exit:
+ do_exit(ret);
+}
+
+
+void tst_flush(void)
+{
+ int rval;
+
+ rval = fflush(stderr);
+ if (rval != 0)
+ tst_brk(TBROK | TERRNO, "fflush(stderr) failed");
+
+ rval = fflush(stdout);
+ if (rval != 0)
+ tst_brk(TBROK | TERRNO, "fflush(stdout) failed");
+
+}
diff --git a/src/kernel/tests/lib/tst_timer.c b/src/kernel/tests/lib/tst_timer.c
new file mode 100644
index 0000000..62d8f90
--- /dev/null
+++ b/src/kernel/tests/lib/tst_timer.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <errno.h>
+
+#define TST_NO_DEFAULT_MAIN
+
+#include "tst_test.h"
+#include "tst_timer.h"
+#include "tst_clocks.h"
+#include "lapi/posix_clocks.h"
+
+static struct timespec start_time, stop_time;
+static clockid_t clock_id;
+
+void tst_timer_check(clockid_t clk_id)
+{
+ if (tst_clock_gettime(clk_id, &start_time)) {
+ if (errno == EINVAL) {
+ tst_brk(TCONF,
+ "Clock id %s(%u) not supported by kernel",
+ tst_clock_name(clk_id), clk_id);
+ return;
+ }
+
+ tst_brk(TBROK | TERRNO, "tst_clock_gettime() failed");
+ }
+}
+
+void tst_timer_start(clockid_t clk_id)
+{
+ clock_id = clk_id;
+
+ if (tst_clock_gettime(clock_id, &start_time))
+ tst_res(TWARN | TERRNO, "tst_clock_gettime() failed");
+}
+
+int tst_timer_expired_ms(long long ms)
+{
+ struct timespec cur_time;
+
+ if (tst_clock_gettime(clock_id, &cur_time))
+ tst_res(TWARN | TERRNO, "tst_clock_gettime() failed");
+
+ return tst_timespec_diff_ms(cur_time, start_time) >= ms;
+}
+
+void tst_timer_stop(void)
+{
+ if (tst_clock_gettime(clock_id, &stop_time))
+ tst_res(TWARN | TERRNO, "tst_clock_gettime() failed");
+}
+
+struct timespec tst_timer_elapsed(void)
+{
+ return tst_timespec_diff(stop_time, start_time);
+}
diff --git a/src/kernel/tests/lib/tst_timer_test.c b/src/kernel/tests/lib/tst_timer_test.c
new file mode 100644
index 0000000..196c512
--- /dev/null
+++ b/src/kernel/tests/lib/tst_timer_test.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+#include <sys/prctl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_clocks.h"
+#include "tst_timer_test.h"
+
+#define MAX_SAMPLES 500
+
+static const char *scall;
+static void (*setup)(void);
+static void (*cleanup)(void);
+static int (*sample)(int clk_id, long long usec);
+static struct tst_test *test;
+
+static long long *samples;
+static unsigned int cur_sample;
+static unsigned int monotonic_resolution;
+static unsigned int timerslack;
+static int virt_env;
+
+static char *print_frequency_plot;
+static char *file_name;
+static char *str_sleep_time;
+static char *str_sample_cnt;
+static int sleep_time = -1;
+static int sample_cnt;
+
+static void print_line(char c, int len)
+{
+ while (len-- > 0)
+ fputc(c, stderr);
+}
+
+static unsigned int ceilu(float f)
+{
+ if (f - (int)f > 0)
+ return (unsigned int)f + 1;
+
+ return (unsigned int)f;
+}
+
+static unsigned int flooru(float f)
+{
+ return (unsigned int)f;
+}
+
+static float bucket_len(unsigned int bucket, unsigned int max_bucket,
+ unsigned int cols)
+{
+ return 1.00 * bucket * cols / max_bucket;
+}
+
+static const char *table_heading = " Time: us ";
+
+/*
+ * Line Header: '10023 | '
+ */
+static unsigned int header_len(long long max_sample)
+{
+ unsigned int l = 1;
+
+ while (max_sample/=10)
+ l++;
+
+ return MAX(strlen(table_heading) + 2, l + 3);
+}
+
+static void frequency_plot(void)
+{
+ unsigned int cols = 80;
+ unsigned int rows = 20;
+ unsigned int i, buckets[rows];
+ long long max_sample = samples[0];
+ long long min_sample = samples[cur_sample-1];
+ unsigned int line_header_len = header_len(max_sample);
+ unsigned int plot_line_len = cols - line_header_len;
+ unsigned int bucket_size;
+
+ memset(buckets, 0, sizeof(buckets));
+
+ /*
+ * We work with discrete data buckets smaller than 1 does not make
+ * sense as well as it's a good idea to keep buckets integer sized
+ * to avoid scaling artifacts.
+ */
+ bucket_size = MAX(1u, ceilu(1.00 * (max_sample - min_sample)/(rows-1)));
+
+ for (i = 0; i < cur_sample; i++) {
+ unsigned int bucket;
+ bucket = flooru(1.00 * (samples[i] - min_sample)/bucket_size);
+ buckets[bucket]++;
+ }
+
+ unsigned int max_bucket = buckets[0];
+ for (i = 1; i < rows; i++)
+ max_bucket = MAX(max_bucket, buckets[i]);
+
+ fprintf(stderr, "\n%*s| Frequency\n", line_header_len - 2, table_heading);
+
+ print_line('-', cols);
+ fputc('\n', stderr);
+
+ unsigned int l, r;
+
+ for (l = 0; l < rows; l++) {
+ if (buckets[l])
+ break;
+ }
+
+ for (r = rows-1; r > l; r--) {
+ if (buckets[r])
+ break;
+ }
+
+ for (i = l; i <= r; i++) {
+ float len = bucket_len(buckets[i], max_bucket, plot_line_len);
+
+ fprintf(stderr, "%*lli | ",
+ line_header_len - 3, min_sample + bucket_size*i);
+ print_line('*', len);
+
+ if ((len - (int)len) >= 0.5)
+ fputc('+', stderr);
+ else if ((len - (int)len) >= 0.25)
+ fputc('-', stderr);
+ else if (len < 0.25 && buckets[i])
+ fputc('.', stderr);
+
+ fputc('\n', stderr);
+ }
+
+ print_line('-', cols);
+ fputc('\n', stderr);
+
+ float scale = 1.00 * plot_line_len / max_bucket;
+
+ fprintf(stderr,
+ "%*uus | 1 sample = %.5f '*', %.5f '+', %.5f '-', non-zero '.'\n",
+ line_header_len - 5, bucket_size, scale, scale * 2, scale * 4);
+
+ fputc('\n', stderr);
+}
+
+void tst_timer_sample(void)
+{
+ samples[cur_sample++] = tst_timer_elapsed_us();
+}
+
+static int cmp(const void *a, const void *b)
+{
+ const long long *aa = a, *bb = b;
+
+ return (*bb - *aa);
+}
+
+/*
+ * The threshold per one syscall is computed as a sum of:
+ *
+ * 400 us - accomodates for context switches, process
+ * migrations between CPUs on SMP, etc.
+ * 2*monotonic_resolution - accomodates for granurality of the CLOCK_MONOTONIC
+ * slack_per_scall - max of 0.1% of the sleep capped on 100ms or
+ * current->timer_slack_ns, which is slack allowed
+ * in kernel
+ *
+ * The formula for slack_per_scall applies to select() and *poll*() syscalls,
+ * the futex and *nanosleep() use only the timer_slack_ns, so we are a bit
+ * less strict here that we could be for these two for longer sleep times...
+ *
+ * We also allow for outliners, i.e. add some number to the threshold in case
+ * that the number of iteration is small. For large enoung number of iterations
+ * outliners are discarded and averaged out.
+ */
+static long long compute_threshold(long long requested_us,
+ unsigned int nsamples)
+{
+ unsigned int slack_per_scall = MIN(100000, requested_us / 1000);
+
+ slack_per_scall = MAX(slack_per_scall, timerslack);
+
+ return (400 + 2 * monotonic_resolution + slack_per_scall) * nsamples
+ + 3000/nsamples;
+}
+
+/*
+ * Returns number of samples to discard.
+ *
+ * We set it to either at least 1 if number of samples > 1 or 5%.
+ */
+static unsigned int compute_discard(unsigned int nsamples)
+{
+ if (nsamples == 1)
+ return 0;
+
+ return MAX(1u, nsamples / 20);
+}
+
+static void write_to_file(void)
+{
+ unsigned int i;
+ FILE *f;
+
+ if (!file_name)
+ return;
+
+ f = fopen(file_name, "w");
+
+ if (!f) {
+ tst_res(TWARN | TERRNO,
+ "Failed to open '%s'", file_name);
+ return;
+ }
+
+ for (i = 0; i < cur_sample; i++)
+ fprintf(f, "%lli\n", samples[i]);
+
+ if (fclose(f)) {
+ tst_res(TWARN | TERRNO,
+ "Failed to close file '%s'", file_name);
+ }
+}
+
+
+/*
+ * Timer testing function.
+ *
+ * What we do here is:
+ *
+ * * Take nsamples measurements of the timer function, the function
+ * to be sampled is defined in the the actual test.
+ *
+ * * We sort the array of samples, then:
+ *
+ * - look for outliners which are samples where the sleep time has exceeded
+ * requested sleep time by an order of magnitude and, at the same time, are
+ * greater than clock resolution multiplied by three.
+ *
+ * - check for samples where the call has woken up too early which is a plain
+ * old bug
+ *
+ * - then we compute truncated mean and compare that with the requested sleep
+ * time increased by a threshold
+ */
+void do_timer_test(long long usec, unsigned int nsamples)
+{
+ long long trunc_mean, median;
+ unsigned int discard = compute_discard(nsamples);
+ unsigned int keep_samples = nsamples - discard;
+ long long threshold = compute_threshold(usec, keep_samples);
+ int i;
+ int failed = 0;
+
+ tst_res(TINFO,
+ "%s sleeping for %llius %u iterations, threshold %.2fus",
+ scall, usec, nsamples, 1.00 * threshold / (keep_samples));
+
+ cur_sample = 0;
+ for (i = 0; i < (int)nsamples; i++) {
+ if (sample(CLOCK_MONOTONIC, usec)) {
+ tst_res(TINFO, "sampling function failed, exitting");
+ return;
+ }
+ }
+
+ qsort(samples, nsamples, sizeof(samples[0]), cmp);
+
+ write_to_file();
+
+ for (i = 0; samples[i] > 10 * usec && i < (int)nsamples; i++) {
+ if (samples[i] <= 3 * monotonic_resolution)
+ break;
+ }
+
+ if (i > 0) {
+ tst_res(TINFO, "Found %i outliners in [%lli,%lli] range",
+ i, samples[0], samples[i-1]);
+ }
+
+ for (i = nsamples - 1; samples[i] < usec && i > -1; i--);
+
+ if (i < (int)nsamples - 1) {
+ tst_res(TFAIL, "%s woken up early %u times range: [%lli,%lli]",
+ scall, nsamples - 1 - i,
+ samples[i+1], samples[nsamples-1]);
+ failed = 1;
+ }
+
+ median = samples[nsamples/2];
+
+ trunc_mean = 0;
+
+ for (i = discard; i < (int)nsamples; i++)
+ trunc_mean += samples[i];
+
+ tst_res(TINFO,
+ "min %llius, max %llius, median %llius, trunc mean %.2fus (discarded %u)",
+ samples[nsamples-1], samples[0], median,
+ 1.00 * trunc_mean / keep_samples, discard);
+
+ if (virt_env) {
+ tst_res(TINFO,
+ "Virtualisation detected, skipping oversleep checks");
+ } else if (trunc_mean > (nsamples - discard) * usec + threshold) {
+ tst_res(TFAIL, "%s slept for too long", scall);
+
+ if (!print_frequency_plot)
+ frequency_plot();
+
+ failed = 1;
+ }
+
+ if (print_frequency_plot)
+ frequency_plot();
+
+ if (!failed)
+ tst_res(TPASS, "Measured times are within thresholds");
+}
+
+static void parse_timer_opts(void);
+
+static int set_latency(void)
+{
+ int fd, latency = 0;
+
+ fd = open("/dev/cpu_dma_latency", O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ return write(fd, &latency, sizeof(latency));
+}
+
+static void timer_setup(void)
+{
+ struct timespec t;
+ int ret;
+
+ if (setup)
+ setup();
+
+ /*
+ * Running tests in VM may cause timing issues, disable upper bound
+ * checks if any hypervisor is detected.
+ */
+ virt_env = tst_is_virt(VIRT_ANY);
+ tst_clock_getres(CLOCK_MONOTONIC, &t);
+
+ tst_res(TINFO, "CLOCK_MONOTONIC resolution %lins", (long)t.tv_nsec);
+
+ monotonic_resolution = t.tv_nsec / 1000;
+ timerslack = 50;
+
+#ifdef PR_GET_TIMERSLACK
+ ret = prctl(PR_GET_TIMERSLACK);
+ if (ret < 0) {
+ tst_res(TINFO, "prctl(PR_GET_TIMERSLACK) = -1, using %uus",
+ timerslack);
+ } else {
+ timerslack = ret / 1000;
+ tst_res(TINFO, "prctl(PR_GET_TIMERSLACK) = %ius", timerslack);
+ }
+#else
+ tst_res(TINFO, "PR_GET_TIMERSLACK not defined, using %uus",
+ timerslack);
+#endif /* PR_GET_TIMERSLACK */
+ parse_timer_opts();
+
+ samples = SAFE_MALLOC(sizeof(long long) * MAX(MAX_SAMPLES, sample_cnt));
+ if (set_latency() < 0)
+ tst_res(TINFO, "Failed to set zero latency constraint: %m");
+}
+
+static void timer_cleanup(void)
+{
+ free(samples);
+
+ if (cleanup)
+ cleanup();
+}
+
+static struct tst_timer_tcase {
+ long long usec;
+ unsigned int samples;
+} tcases[] = {
+ {1000, 500},
+ {2000, 500},
+ {5000, 300},
+ {10000, 100},
+ {25000, 50},
+ {100000, 10},
+ {1000000, 2},
+};
+
+static void timer_test_fn(unsigned int n)
+{
+ do_timer_test(tcases[n].usec, tcases[n].samples);
+}
+
+static void single_timer_test(void)
+{
+ do_timer_test(sleep_time, sample_cnt);
+}
+
+static struct tst_option options[] = {
+ {"p", &print_frequency_plot, "-p Print frequency plot"},
+ {"s:", &str_sleep_time, "-s us Sleep time"},
+ {"n:", &str_sample_cnt, "-n uint Number of samples to take"},
+ {"f:", &file_name, "-f fname Write measured samples into a file"},
+ {NULL, NULL, NULL}
+};
+
+static void parse_timer_opts(void)
+{
+ if (str_sleep_time) {
+ if (tst_parse_int(str_sleep_time, &sleep_time, 0, INT_MAX)) {
+ tst_brk(TBROK,
+ "Invalid sleep time '%s'", str_sleep_time);
+ }
+ }
+
+ if (str_sample_cnt) {
+ if (tst_parse_int(str_sample_cnt, &sample_cnt, 1, INT_MAX)) {
+ tst_brk(TBROK,
+ "Invalid sample count '%s'", str_sample_cnt);
+ }
+ }
+
+ if (str_sleep_time || str_sample_cnt) {
+ if (sleep_time < 0)
+ sleep_time = 10000;
+
+ if (!sample_cnt)
+ sample_cnt = 500;
+
+ long long timeout = sleep_time * sample_cnt / 1000000;
+
+ tst_set_timeout(timeout + timeout/10);
+
+ test->test_all = single_timer_test;
+ test->test = NULL;
+ test->tcnt = 0;
+ }
+}
+
+struct tst_test *tst_timer_test_setup(struct tst_test *timer_test)
+{
+ setup = timer_test->setup;
+ cleanup = timer_test->cleanup;
+ scall = timer_test->scall;
+ sample = timer_test->sample;
+
+ timer_test->scall = NULL;
+ timer_test->setup = timer_setup;
+ timer_test->cleanup = timer_cleanup;
+ timer_test->test = timer_test_fn;
+ timer_test->tcnt = ARRAY_SIZE(tcases);
+ timer_test->sample = NULL;
+ timer_test->options = options;
+
+ test = timer_test;
+
+ return timer_test;
+}
diff --git a/src/kernel/tests/lib/tst_tmpdir.c b/src/kernel/tests/lib/tst_tmpdir.c
new file mode 100644
index 0000000..0c39eb8
--- /dev/null
+++ b/src/kernel/tests/lib/tst_tmpdir.c
@@ -0,0 +1,347 @@
+/**********************************************************
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ *********************************************************/
+
+/**********************************************************
+ *
+ * OS Testing - Silicon Graphics, Inc.
+ *
+ * FUNCTION NAME : tst_tmpdir, tst_rmdir
+ *
+ * FUNCTION TITLE : Create/remove a testing temp dir
+ *
+ * SYNOPSIS:
+ * void tst_tmpdir();
+ * void tst_rmdir();
+ *
+ * AUTHOR : Dave Fenner
+ *
+ * INITIAL RELEASE : UNICOS 8.0
+ *
+ * DESCRIPTION
+ * tst_tmpdir() is used to create a unique, temporary testing
+ * directory, and make it the current working directory.
+ * tst_rmdir() is used to remove the directory created by
+ * tst_tmpdir().
+ *
+ * RETURN VALUE
+ * Neither tst_tmpdir() or tst_rmdir() has a return value.
+ *
+ *********************************************************/
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "test.h"
+#include "safe_macros.h"
+#include "ltp_priv.h"
+#include "lapi/futex.h"
+
+/*
+ * Define some useful macros.
+ */
+#define DIR_MODE (S_IRWXU|S_IRWXG|S_IRWXO)
+
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX 1024
+#endif
+#endif
+
+/*
+ * Define global variables.
+ */
+extern char *TCID; /* defined/initialized in main() */
+static char *TESTDIR = NULL; /* the directory created */
+
+static char test_start_work_dir[PATH_MAX];
+
+/* lib/tst_checkpoint.c */
+extern futex_t *tst_futexes;
+
+static int rmobj(const char *obj, char **errmsg);
+
+int tst_tmpdir_created(void)
+{
+ return TESTDIR != NULL;
+}
+
+char *tst_get_tmpdir(void)
+{
+ if (TESTDIR == NULL) {
+ tst_brkm(TBROK, NULL, "you must call tst_tmpdir() first");
+ return NULL;
+ }
+
+ return strdup(TESTDIR);
+}
+
+const char *tst_get_startwd(void)
+{
+ return test_start_work_dir;
+}
+
+static int purge_dir(const char *path, char **errptr)
+{
+ int ret_val = 0;
+ DIR *dir;
+ struct dirent *dir_ent;
+ char dirobj[PATH_MAX];
+ static char err_msg[PATH_MAX + 1280];
+
+ /* Do NOT perform the request if the directory is "/" */
+ if (!strcmp(path, "/")) {
+ if (errptr) {
+ strcpy(err_msg, "Cannot purge system root directory");
+ *errptr = err_msg;
+ }
+
+ return -1;
+ }
+
+ errno = 0;
+
+ /* Open the directory to get access to what is in it */
+ if (!(dir = opendir(path))) {
+ if (errptr) {
+ sprintf(err_msg,
+ "Cannot open directory %s; errno=%d: %s",
+ path, errno, tst_strerrno(errno));
+ *errptr = err_msg;
+ }
+ return -1;
+ }
+
+ /* Loop through the entries in the directory, removing each one */
+ for (dir_ent = readdir(dir); dir_ent; dir_ent = readdir(dir)) {
+ /* Don't remove "." or ".." */
+ if (!strcmp(dir_ent->d_name, ".")
+ || !strcmp(dir_ent->d_name, ".."))
+ continue;
+
+ /* Recursively remove the current entry */
+ sprintf(dirobj, "%s/%s", path, dir_ent->d_name);
+ if (rmobj(dirobj, errptr) != 0)
+ ret_val = -1;
+ }
+
+ closedir(dir);
+ return ret_val;
+}
+
+static int rmobj(const char *obj, char **errmsg)
+{
+ int ret_val = 0;
+ struct stat statbuf;
+ static char err_msg[PATH_MAX + 1280];
+ int fd;
+
+ fd = open(obj, O_DIRECTORY | O_NOFOLLOW);
+ if (fd >= 0) {
+ close(fd);
+ ret_val = purge_dir(obj, errmsg);
+
+ /* If there were problems removing an entry, don't attempt to
+ remove the directory itself */
+ if (ret_val == -1)
+ return -1;
+
+ /* Get the link count, now that all the entries have been removed */
+ if (lstat(obj, &statbuf) < 0) {
+ if (errmsg != NULL) {
+ sprintf(err_msg,
+ "lstat(%s) failed; errno=%d: %s", obj,
+ errno, tst_strerrno(errno));
+ *errmsg = err_msg;
+ }
+ return -1;
+ }
+
+ /* Remove the directory itself */
+ if (statbuf.st_nlink >= 3) {
+ /* The directory is linked; unlink() must be used */
+ if (unlink(obj) < 0) {
+ if (errmsg != NULL) {
+ sprintf(err_msg,
+ "unlink(%s) failed; errno=%d: %s",
+ obj, errno, tst_strerrno(errno));
+ *errmsg = err_msg;
+ }
+ return -1;
+ }
+ } else {
+ /* The directory is not linked; remove() can be used */
+ if (remove(obj) < 0) {
+ if (errmsg != NULL) {
+ sprintf(err_msg,
+ "remove(%s) failed; errno=%d: %s",
+ obj, errno, tst_strerrno(errno));
+ *errmsg = err_msg;
+ }
+ return -1;
+ }
+ }
+ } else {
+ if (unlink(obj) < 0) {
+ if (errmsg != NULL) {
+ sprintf(err_msg,
+ "unlink(%s) failed; errno=%d: %s", obj,
+ errno, tst_strerrno(errno));
+ *errmsg = err_msg;
+ }
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void tst_tmpdir(void)
+{
+ char template[PATH_MAX];
+ char *env_tmpdir;
+ char *errmsg, *c;
+
+ /*
+ * Create a template for the temporary directory. Use the
+ * environment variable TMPDIR if it is available, otherwise
+ * use our default TEMPDIR.
+ */
+ env_tmpdir = getenv("TMPDIR");
+ if (env_tmpdir) {
+ c = strchr(env_tmpdir, '/');
+ /*
+ * Now we force environment variable TMPDIR to be an absolute
+ * pathname, which dose not make much sense, but it will
+ * greatly simplify code in tst_rmdir().
+ */
+ if (c != env_tmpdir) {
+ tst_brkm(TBROK, NULL, "You must specify an absolute "
+ "pathname for environment variable TMPDIR");
+ return;
+ }
+ snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", env_tmpdir, TCID);
+ } else {
+ snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", TEMPDIR, TCID);
+ }
+
+ /* Make the temporary directory in one shot using mkdtemp. */
+ if (mkdtemp(template) == NULL) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s: mkdtemp(%s) failed", __func__, template);
+ return;
+ }
+
+ if ((TESTDIR = strdup(template)) == NULL) {
+ tst_brkm(TBROK | TERRNO, NULL,
+ "%s: strdup(%s) failed", __func__, template);
+ return;
+ }
+
+ SAFE_CHOWN(NULL, TESTDIR, -1, getgid());
+
+ SAFE_CHMOD(NULL, TESTDIR, DIR_MODE);
+
+ if (getcwd(test_start_work_dir, sizeof(test_start_work_dir)) == NULL) {
+ tst_resm(TINFO, "Failed to record test working dir");
+ test_start_work_dir[0] = '\0';
+ }
+
+ /*
+ * Change to the temporary directory. If the chdir() fails, issue
+ * TBROK messages for all test cases, attempt to remove the
+ * directory (if it was created), and exit. If the removal also
+ * fails, also issue a TWARN message.
+ */
+ if (chdir(TESTDIR) == -1) {
+ tst_resm(TERRNO, "%s: chdir(%s) failed", __func__, TESTDIR);
+
+ /* Try to remove the directory */
+ if (rmobj(TESTDIR, &errmsg) == -1) {
+ tst_resm(TWARN, "%s: rmobj(%s) failed: %s",
+ __func__, TESTDIR, errmsg);
+ }
+
+ tst_exit();
+ }
+}
+
+void tst_rmdir(void)
+{
+ char *errmsg;
+
+ /*
+ * Check that TESTDIR is not NULL.
+ */
+ if (TESTDIR == NULL) {
+ tst_resm(TWARN,
+ "%s: TESTDIR was NULL; no removal attempted",
+ __func__);
+ return;
+ }
+
+ /*
+ * Unmap the backend file.
+ * This is needed to overcome the NFS "silly rename" feature.
+ */
+ if (tst_futexes) {
+ msync((void *)tst_futexes, getpagesize(), MS_SYNC);
+ munmap((void *)tst_futexes, getpagesize());
+ }
+
+ /*
+ * Attempt to remove the "TESTDIR" directory, using rmobj().
+ */
+ if (rmobj(TESTDIR, &errmsg) == -1) {
+ tst_resm(TWARN, "%s: rmobj(%s) failed: %s",
+ __func__, TESTDIR, errmsg);
+ }
+}
+
+void tst_purge_dir(const char *path)
+{
+ char *err;
+
+ if (purge_dir(path, &err))
+ tst_brkm(TBROK, NULL, "%s: %s", __func__, err);
+}
diff --git a/src/kernel/tests/lib/tst_virt.c b/src/kernel/tests/lib/tst_virt.c
new file mode 100644
index 0000000..53d33e6
--- /dev/null
+++ b/src/kernel/tests/lib/tst_virt.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2013 Linux Test Project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it
+ * is free of the rightful claim of any third person regarding
+ * infringement or the like. Any license provided herein, whether
+ * implied or otherwise, applies only to this software file. Patent
+ * licenses, if any, provided herein do not apply to combinations of
+ * this program with other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <unistd.h>
+#include "test.h"
+#include "safe_macros.h"
+
+static int is_kvm(void)
+{
+ FILE *cpuinfo;
+ char line[64];
+ int found;
+
+ /* this doesn't work with custom -cpu values, since there's
+ * no easy, reasonable or reliable way to work around those */
+ cpuinfo = SAFE_FOPEN(NULL, "/proc/cpuinfo", "r");
+ found = 0;
+ while (fgets(line, sizeof(line), cpuinfo) != NULL) {
+ if (strstr(line, "QEMU Virtual CPU")) {
+ found = 1;
+ break;
+ }
+ }
+
+ SAFE_FCLOSE(NULL, cpuinfo);
+ return found;
+}
+
+static int is_xen(void)
+{
+ char hypervisor_type[4];
+
+ if (access("/proc/xen", F_OK) == 0)
+ return 1;
+
+ if (access("/sys/hypervisor/type", F_OK) == 0) {
+ SAFE_FILE_SCANF(NULL, "/sys/hypervisor/type", "%3s",
+ hypervisor_type);
+ return strncmp("xen", hypervisor_type,
+ sizeof(hypervisor_type)) == 0;
+ }
+
+ return 0;
+}
+
+static int try_systemd_detect_virt(void)
+{
+ FILE *f;
+ char virt_type[64];
+ int ret;
+
+ /* See tst_cmd.c */
+ void *old_handler = signal(SIGCHLD, SIG_DFL);
+
+ f = popen("systemd-detect-virt", "r");
+ if (!f) {
+ signal(SIGCHLD, old_handler);
+ return 0;
+ }
+
+ if (!fgets(virt_type, sizeof(virt_type), f))
+ virt_type[0] = '\0';
+
+ ret = pclose(f);
+
+ signal(SIGCHLD, old_handler);
+
+ /*
+ * systemd-detect-virt not found by shell or no virtualization detected
+ * (systemd-detect-virt returns non-zero)
+ */
+ if (ret < 0 || (WIFEXITED(ret) && WEXITSTATUS(ret) == 127))
+ return -1;
+
+ if (ret)
+ return 0;
+
+ if (!strncmp("kvm", virt_type, 3))
+ return VIRT_KVM;
+
+ if (!strncmp("xen", virt_type, 3))
+ return VIRT_XEN;
+
+ return VIRT_OTHER;
+}
+
+int tst_is_virt(int virt_type)
+{
+ int ret = try_systemd_detect_virt();
+
+ if (ret >= 0) {
+ if (virt_type == VIRT_ANY)
+ return ret != 0;
+ else
+ return ret == virt_type;
+ }
+
+ switch (virt_type) {
+ case VIRT_ANY:
+ return is_xen() || is_kvm();
+ case VIRT_XEN:
+ return is_xen();
+ case VIRT_KVM:
+ return is_kvm();
+ case VIRT_OTHER:
+ return 0;
+ }
+
+ tst_brkm(TBROK, NULL, "invalid virt_type flag: %d", virt_type);
+ return -1;
+}
diff --git a/src/kernel/tests/lib/tst_wallclock.c b/src/kernel/tests/lib/tst_wallclock.c
new file mode 100644
index 0000000..282d6ad
--- /dev/null
+++ b/src/kernel/tests/lib/tst_wallclock.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 Linaro Limited. All rights reserved.
+ * Author: Rafael David Tinoco <rafael.tinoco@linaro.org>
+ */
+
+#include <errno.h>
+
+#define TST_NO_DEFAULT_MAIN
+
+#include "tst_test.h"
+#include "tst_timer.h"
+#include "tst_clocks.h"
+#include "tst_wallclock.h"
+#include "lapi/posix_clocks.h"
+
+static struct timespec real_begin, mono_begin;
+
+static int clock_saved;
+
+void tst_wallclock_save(void)
+{
+ /* save initial monotonic time to restore it when needed */
+
+ if (tst_clock_gettime(CLOCK_REALTIME, &real_begin))
+ tst_brk(TBROK | TERRNO, "tst_clock_gettime() realtime failed");
+
+ if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_begin)) {
+ if (errno == EINVAL) {
+ tst_brk(TCONF | TERRNO,
+ "tst_clock_gettime() didn't support CLOCK_MONOTONIC_RAW");
+ }
+
+ tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed");
+ }
+
+ clock_saved = 1;
+}
+
+void tst_wallclock_restore(void)
+{
+ static struct timespec mono_end, elapsed, adjust;
+
+ if (!clock_saved)
+ return;
+
+ clock_saved = 0;
+
+ if (tst_clock_gettime(CLOCK_MONOTONIC_RAW, &mono_end))
+ tst_brk(TBROK | TERRNO, "tst_clock_gettime() monotonic failed");
+
+ elapsed = tst_timespec_diff(mono_end, mono_begin);
+
+ adjust = tst_timespec_add(real_begin, elapsed);
+
+ /* restore realtime clock based on monotonic delta */
+
+ if (tst_clock_settime(CLOCK_REALTIME, &adjust))
+ tst_brk(TBROK | TERRNO, "tst_clock_settime() realtime failed");
+}